EC2를 사용하여 k6 부하 테스트 향상

32396 단어 clojurek6testingaws
k6은 사용 가능한 최고의 부하 테스트 도구이지만 가격이 비쌉니다. 나는 많은 돈을 지불하지 않고 무제한 VU(가상 사용자)를 시작하고 싶었고 수명이 짧은 많은 EC2 인스턴스를 생성하여 이를 수행할 수 있는 깔끔한 방법을 찾았습니다!

너무 많은 템플릿



heredocs와 함께 간단한 user-data 스크립트를 사용했습니다. Docker가 설치된 AMI가 있는지 확인하십시오. S3 액세스도 필요합니다.

AWS 자격 증명을 설정하여 시작하십시오.

# load_test.user-data
echo "setting up aws-cli"
mkdir -p /home/ec2-user/.aws
cat <<EOF > /home/ec2-user/.aws/config
[default]
output=json
region=us-west-1
EOF

cat <<EOF > /home/ec2-user/.aws/credentials
[default]
aws_access_key_id={{ aws-creds.access-key-id }}
aws_secret_access_key={{ aws-creds.secret-access-key }}
EOF


다음으로 부하 테스트를 작성하고 실행 가능하게 만들 수 있습니다.

# load_test.user-data
echo "writing load test: {{load-test}}"
mkdir -p /home/ec2-user/load_test
chmod a+rw /home/ec2-user/load_test
cat <<EOF > /home/ec2-user/load_test/{{load-test}}
{{load-test-content|safe}}
EOF


k6 도커 이미지를 가져와 단일 명령으로 실행합니다.

# load_test.user-data
echo "running load test"
sudo -u ec2-user docker run --rm \
  --ulimit nofile=65536:65536 \
  --network host \
  -v /home/ec2-user/load_test/:/mnt/load_test:rw \
  -i loadimpact/k6 \
  run --summary-export=/mnt/load_test/out.json \
  /mnt/load_test/{{load-test}} > /dev/null


마지막으로 테스트 결과를 s3에 업로드합니다.

# load_test.user-data
echo "uploading results"
sudo -u ec2-user aws s3api put-object \
  --bucket load-test-results \
  --key {{uuid}}/{{instance-name}} \
  --body /home/ec2-user/load_test/out.json > /dev/null


중요: EC2 인스턴스를 종료하려면 trap를 호출하도록 systemctl halt 함수를 설정해야 합니다. EC2 인스턴스를 실행 상태로 두면 비용 절감이라는 목표에 역효과를 낼 수 있습니다.
user-data에서 템플릿을 사용했음을 눈치채셨을 것입니다. Selmer를 사용하여 중요한 모든 것을 공급할 수 있습니다.

(defn build-user-data
  [aws-creds uuid instance-name load-test load-test-template aws-creds]
  (selmer/render-file
   "load_test.user-data"
   {:aws-creds aws-creds
    :uuid uuid
    :instance-name instance-name
    :load-test (.getName (io/file (io/resource load-test)))
    :load-test-content (selmer/render-file load-test load-test-template)}))

aws-creds:access-key-id:secret-access-key 키가 있는 맵이어야 합니다. S3 자격 증명입니다.
load-test-content 자체가 템플릿이라는 것을 알 수 있습니다. 즉, 부하 테스트를 임의의 주소로 가리키고 어디에서나 웹 앱을 호스팅할 수 있습니다. 다음은 로드 테스트의 예입니다.

import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
    stages: [
        { duration: '30s', target: 5000 }, // ramp up
        { duration: '30s', target: 5000 }, // sustained load
        { duration: '30s', target: 0 }, // cool down
    ],
};

export default function () {
    let res = http.get('{{url-prefix}}/ping')

    check(res, {'status is 200': (r) => r && r.status === 200});

    sleep(1);
}

load-test-template는 단일 키:url-prefix가 있는 맵이어야 합니다. 부하 테스트에서 템플릿을 사용하는 더 흥미로운 용도를 찾을 수 있습니다. 😉

관현악법



우수한 Cognitect AWS 라이브러리를 사용하여 테스트를 시작할 수 있습니다.

(defn launch-load-test-instance
  [ec2 aws-creds uuid instance-name load-test load-test-template]
  (aws/invoke ec2
              {:op :RunInstances
               :request {:ImageId "my-ami"
                         :InstanceType "my-size"
                         :MinCount 1 :MaxCount 1
                         :InstanceInitiatedShutdownBehavior "terminate"
                         :UserData (-> (build-user-data aws-creds uuid instance-name load-test load-test-template)
                                       byte-streams/to-byte-array
                                       base64/encode
                                       byte-streams/to-string)
                         :TagSpecifications [{:ResourceType "instance"
                                              :Tags [{:Key "Name" :Value instance-name}
                                                     {:Key "load-test-id" :Value uuid}]}]
                         :SecurityGroupIds [...]
                         :KeyName "my-company"}}))

(defn launch-load-test-instances
  [ec2 aws-creds uuid instance-prefix load-test load-test-template num-instances]
  (doall
   (map
    #(launch-load-test-instance ec2 aws-creds uuid
                      (str instance-prefix %)
                      load-test load-test-template)
    (range num-instances))))


위의 코드에는 채워야 할 몇 가지 공백이 있습니다.

그 코드를 작성하고 나면 마치 미친 과학자가 된 기분입니다. 파괴하라, 내 부하들이여!



테스트 결과 수집



이전 코드에서 각 ec2 인스턴스에 uuid를 제공하고 user-data 스크립트에서 모든 k6 결과에 대한 공유 버킷으로 사용했음을 알 수 있습니다. 나는 당신이 그것을 추적하기를 바랍니다. 😈

(def s3-bucket "load-test-results")

(defn get-k6-summary [s3 key]
  (->> {:op :GetObject
        :request {:Bucket s3-bucket
                  :Key key}}
       (aws/invoke s3)
       :Body
       io/reader
       json/decode-stream))

(defn get-k6-summaries [s3 uuid]
  (map
   (comp (partial get-k6-summary s3) :Key)
   (->> {:op :ListObjects
         :request {:Bucket s3-bucket
                   :Prefix uuid}}
        (aws/invoke s3)
        :Contents)))

get-k6-summaries를 호출하면 생성된 모든 결과의 시퀀스가 ​​반환됩니다. 이러한 개별 결과는 일부 분석에 도움이 될 수 있지만 결합할 수 있는 만큼 많은 메트릭을 집계해야 합니다.

(defn metric-type [m]
  (condp = (set (keys m))
    #{"count" "rate"} :counter
    #{"min" "max" "p(90)" "p(95)" "med" "avg"} :trend
    #{"fails" "value" "passes"} :rate
    #{"min" "max" "value"} :gauge
    :unknown))

(defn merge-summaries
  "summaries is a vector of k6 output summaries converted from JSON to EDN.
   get-k6-summaries returns this structure precisely. This function works with the various types
   of k6 metrics (https://k6.io/docs/using-k6/metrics) and does the following:
    - min values are the minimum in all the maps
    - max values are the maximum in all the maps
    - avg values are the average in all the maps
    - count is the sum in all the maps
    - rate is the average in all the maps
    - fails is the sum in all the maps
    - passes is the sum in all the maps
   p, med, & 'value' values are not included in the merged summary, but they can still be viewed
   for individual machines by inspecting the output from get-k6-summary"
  [summaries]
  (letfn [(merge-metrics [v1 v2]
            (condp = (metric-type v2)
              :counter {"count" (+ (get v1 "count") (get v2 "count"))
                        "rate" (double (/ (+ (get v1 "rate") (get v2 "rate")) 2))}
              :trend {"min" (min (get v1 "min") (get v2 "min"))
                      "max" (max (get v1 "max") (get v2 "max"))
                      "avg" (double (/ (+ (get v1 "avg") (get v2 "avg")) 2))}
              :rate {"fails" (+ (get v1 "fails") (get v2 "fails"))
                     "passes" (+ (get v1 "passes") (get v2 "passes"))}
              :gauge {"min" (min (get v1 "min") (get v2 "min"))
                      "max" (max (get v1 "max") (get v2 "max"))}
              {}))
          (merge-checks [v1 v2]
            {"passes" (+ (get v1 "passes") (get v2 "passes"))
             "fails" (+ (get v1 "fails") (get v2 "fails"))})
          (max-vus [m]
            (get-in m ["metrics" "vus_max" "max"]))]
    {:metrics (apply
               merge-with
               merge-metrics
               (map #(get % "metrics")
                    summaries))
     :checks (apply
              merge-with
              merge-checks
              (map
               #(get-in % ["root_group" "checks"])
               summaries))
     :max-vus (->> summaries
                   (map max-vus)
                   (apply +))}))


집계 결과를 얻으려면 merge-summaries로 전화하십시오.

장단점



유료 k6 계획은 친숙한 인터페이스와 더 나은 결과 세트를 제공합니다. 그러나 $25,000/년이 그 금액의 가치가 있습니까? 모르겠어요. 그것은 각 사용자에게 달려 있습니다.

일부 ec2 인스턴스를 직접 관리하고 총체적으로 적은 정보로 작업하려는 경우 이 접근 방식이 더 나을 수 있습니다.

자세히 설명하지 않은 한 가지 문제는 실패한 EC2 인스턴스를 관리하는 것입니다. 특정 오류 상태에는 외부TerminateInstances 작업이 필요합니다. 이제 '매달린' ec2 인스턴스 관리가 문제입니다.

k6 클라우드는 또한 지역 간에 트래픽을 분산합니다. 대역폭이 느린 클라이언트도 시뮬레이트할 것이라고 생각합니다. 필요한 경우 이 모든 것을 제공할 수 있습니까?

k6의 유료 기능 세트에 근접할 수 있지만 많은 노력을 기울여야 합니다. 바라건대, 나는 당신에게 출발점을 제공했습니다.

재미를 잊지 마세요!

좋은 웹페이지 즐겨찾기