k6とはOSSの負荷テストツールで、2016年頃から開発されている、そこそこ歴史のある負荷ツールである。
現在はGrafana Labsの下で継続して開発が行われている。GitHubはこちら。
これにはOperator版も用意されているので、これを使ってPodに負荷をかけた時のメモ。
Running distributed k6 tests on Kubernetesを参考にしている。
デプロイ
Operatorのデプロイはコードをcloneしてmakeする形で行う。コンテキストを切り替えた状態で以下を実行する。
git clone https://github.com/grafana/k6-operator && cd k6-operator
make deploy
デプロイに成功すると、k6-operator-system
というNamespaceにPodが作成される。
$ kubectl get pod -n k6-operator-system
NAME READY STATUS RESTARTS AGE
k6-operator-controller-manager-54488d95cd-gxd7b 2/2 Running 0 27s
なおMac環境でkustomizeコマンドが入っていないと以下のようなエラーになる。
cd config/manager && /Users/hogehoge/go/bin/kustomize edit set image controller=ghcr.io/grafana/k6-operator:latest
/bin/sh: line 1: 10199 Killed: 9 /Users/hogehoge/go/bin/kustomize edit set image controller=ghcr.io/grafana/k6-operator:latest
make: *** [deploy] Error 137
この場合はbrew install kustomize
を実行してkustomizeを入れればOKなはず。
検証
動作確認をする。
テストコードを作成する。
cat << EOF > test.js
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ target: 200, duration: '30s' },
{ target: 0, duration: '30s' },
],
};
export default function () {
const result = http.get('https://test-api.k6.io/public/crocodiles/');
check(result, {
'http response status code is 200': result.status === 200,
});
}
EOF
ConfigMapとして作成する。
kubectl create ns k6-test
kubectl create configmap crocodile-stress-test --from-file ./test.js -n k6-test
k6のカスタムリソースを作成する。
※古いドキュメントだとkind: K6
となっているが、kind: TestRun
に変更された。
cat << EOF > ./k6-test.yaml
apiVersion: k6.io/v1alpha1
kind: TestRun
metadata:
name: k6-sample
spec:
parallelism: 4
script:
configMap:
name: crocodile-stress-test
file: test.js
EOF
applyするとテストが実行される。
kubectl apply -f ./k6-test.yaml -n k6-test
testrunリソースを見ると、STAGE
がstarted
になっている。
$ kubectl get testrun -n k6-test
NAME STAGE AGE TESTRUNID
k6-sample started 28s
Podは以下のようになっていて、parallelism
で4並列で動かすよう指定していたため、テスト本体は4つ同時に動いている。
$ kubectl get pod -n k6-test
NAME READY STATUS RESTARTS AGE
k6-sample-1-gf698 1/1 Running 0 41s
k6-sample-2-n2rbl 1/1 Running 0 41s
k6-sample-3-tbk6f 1/1 Running 0 41s
k6-sample-4-v657j 1/1 Running 0 41s
k6-sample-initializer-7cxff 0/1 Completed 0 50s
k6-sample-starter-ftdzh 0/1 Completed 0 34s
ちなみに各Podのcommand
は以下のような感じ。
- mkdir -p $(dirname /tmp/test.js.archived.tar) && k6 archive /test/test.js -O
/tmp/test.js.archived.tar 2> /tmp/k6logs && k6 inspect --execution-requirements
/tmp/test.js.archived.tar 2> /tmp/k6logs ; ! cat /tmp/k6logs | grep 'level=error'
- 'curl --retry 3 -X PATCH -H ''Content-Type: application/json'' http://10.98.39.51:6565/v1/status
-d ''{"data":{"attributes":{"paused":false,"stopped":false},"id":"default","type":"status"}}'''
- k6
- run
- --quiet
- /test/test.js
- --address=0.0.0.0:6565
- --paused
- --tag
- instance_id=1
- --tag
- job_name=k6-sample-1
initializerでテストコードのアーカイブ化などを行い、starterで起動したテスト用Podがpaused
なのを解除して一斉にテストを実行している感じだと思われる。
テスト終了後はSTAGE
がfinished
になる。
$ kubectl get testrun -n k6-test
NAME STAGE AGE TESTRUNID
k6-sample finished 89s
ログを見るとテストの結果を確認することが出来る。
$ kubectl logs -n k6-test k6-sample-1-gf698
✓ http response status code is 200
checks.........................: 100.00% ✓ 3557 ✗ 0
data_received..................: 3.3 MB 56 kB/s
data_sent......................: 450 kB 7.5 kB/s
http_req_blocked...............: avg=4.01ms min=1.8µs med=5.56µs max=270.01ms p(90)=6.9µs p(95)=8.8µs
http_req_connecting............: avg=1.92ms min=0s med=0s max=120.45ms p(90)=0s p(95)=0s
http_req_duration..............: avg=432.43ms min=103.84ms med=124.67ms max=2.93s p(90)=1.59s p(95)=2.09s
{ expected_response:true }...: avg=432.43ms min=103.84ms med=124.67ms max=2.93s p(90)=1.59s p(95)=2.09s
http_req_failed................: 0.00% ✓ 0 ✗ 3557
http_req_receiving.............: avg=125.08µs min=23.3µs med=120.21µs max=3.12ms p(90)=157.28µs p(95)=182.03µs
http_req_sending...............: avg=35.59µs min=7.79µs med=28.69µs max=735.26µs p(90)=51.94µs p(95)=58.07µs
http_req_tls_handshaking.......: avg=2.05ms min=0s med=0s max=126.54ms p(90)=0s p(95)=0s
http_req_waiting...............: avg=432.27ms min=103.71ms med=124.54ms max=2.93s p(90)=1.59s p(95)=2.08s
http_reqs......................: 3557 59.281034/s
iteration_duration.............: avg=436.66ms min=104.08ms med=125.48ms max=2.93s p(90)=1.59s p(95)=2.09s
iterations.....................: 3557 59.281034/s
vus............................: 2 min=0 max=50
vus_max........................: 50 min=50 max=50
非常に簡単にテストを動かすことが出来た。
注意点
Istioを使って自動的にサイドカーを挿入している場合、以下のような感じで初期化用Podが終わらない。
$ kubectl get pod -n k6-test
NAME READY STATUS RESTARTS AGE
k6-sample-initializer-9q5qd 1/2 NotReady 0 5m56s
これはk9sなんかで中を見ると理由が分かる。
┌─────────────────────────────────────────────────────────────────── Containers(k6-test/k6-sample-initializer-9q5qd)[3] ────────────────────────────────────────────────────────────────────┐
│ NAME↑ PF IMAGE READY STATE INIT RESTARTS PROBES(L:R) CPU MEM CPU/R:L MEM/R:L %CPU/R %CPU/L R │
│ istio-proxy ● public.ecr.aws/v6x6b8s5/vmwareallspark/proxyv2:1.17.3-release-v1-distroless true Running false 0 off:on 7↑ 68 100:2000 128:1024 7↑ 0 3 │
│ istio-validation ● public.ecr.aws/v6x6b8s5/vmwareallspark/proxyv2:1.17.3-release-v1-distroless true Completed true 0 off:off 0 0 100:2000 128:1024 0 0 0 │
│ k6 ● ghcr.io/grafana/k6-operator:latest-runner false Completed false 0 off:off 0 0 0:0 0:0 n/a n/a a
Jobが終わっているにも関わらず、istio-proxyが動き続けるため、Jobが完了出来ずにいる状態となっている。
これはKubernetes v1.28以降のSidecarContainers
を使えば対応できると思われる。(参考記事:Kubernetes v1.28: Introducing native sidecar containers)
ただ、とりあえず手っ取り早く動かしたい人は、Istio環境ではOperatorの利用を諦めて、以下のようなPodを作成するのが早いと思う。
apiVersion: v1
kind: Pod
metadata:
labels:
app: k6
name: k6-sample
spec:
containers:
- command:
- k6
- run
- /test/test.js
image: ghcr.io/grafana/k6
name: k6
ports:
volumeMounts:
- mountPath: /test
name: k6-test-volume
volumes:
- configMap:
name: k6-test
name: k6-test-volume