docker compose ECS integrationでlocustクラスタをさくっと起動する
はじめに
locustはOSSのパフォーマンステストツールです。ワーカープロセスをスケールアウトさせることで分散実行が行えます。locustをクラウド上で実行させた例や手順は既にいくつか紹介されています。
- MQTTの負荷テストもバッチリ!!Locustを活用した分散負荷テスト環境の構築
- Load-testing an IoT application using Google Cloud and Locust
- Locust + AWS ECS(Fargate)で負荷試験環境を作った話
今回はあまり頑張らないで使い慣れたdocker composeのECS integrationを使ってECS上でクラスタを起動してみました。
ローカルでクラスタを起動
開発時にはローカルでクラスタを起動します。—scale
オプションでワーカープロセスの数を指定できます。テストスクリプトはローカルのファイルシステムをマウントすることでコンテナ側に共有しています。
version: '3' services: master: image: locustio/locust ports: - "8089:8089" volumes: - ./:/mnt/locust command: -f /mnt/locust/locustfile.py --master worker: image: locustio/locust volumes: - ./:/mnt/locust command: -f /mnt/locust/locustfile.py --worker --master-host master
# デフォルトコンテキストに切り替え(optional) > docker context use default # ワーカー数2で起動 > docker-compose -f docker-comose-local.yml up --scale worker=2
ECS上でクラスタを起動
ECS上で起動する時もそれほど多くの手順は必要ないです。ローカル起動との違いは以下の通りです。
- テストスクリプトを含むコンテナイメージを使用する
- スクリプトをEFSに置いてマウントしたり、S3に置いて動的に取得する方法も検討しましたが、これが一番手間が少ないと思っています
- aws用のdocker contextを使用する
- docker-compose.ymlにALB用の設定を追記する
上記を含む詳細な手順を以下に示します。
イメージのビルドとPUSH
今回使用するDockerfileは下記の通りです。テストスクリプトおよびテストに必要なファイルをコンテナイメージに含めます。
FROM locustio/locust WORKDIR /mnt/locust COPY *.py /mnt/locust/
ECRリポジトリの作成、イメージのビルド、プッシュを行います。今回の例ではリポジトリURIを環境変数に設定してdocker-compose.ymlから参照しています。
#!/bin/bash set -ue -o pipefail # リポジトリ名 repository="changeme" # リポジトリの存否を確認してなければ作る aws ecr describe-repositories --repository-names $repository || aws ecr create-repository --repository-name $repository # リポジトリURIを取得 repositoryUri=`aws ecr describe-repositories --repository-names $repository | jq -r '.repositories[0].repositoryUri'` # イメージビルド docker build . -t $repository docker tag $repository:latest $repositoryUri:latest # push aws ecr get-login-password | docker login --username AWS --password-stdin $repositoryUri docker push ${repositoryUri}:latest echo "" echo "" echo "export LOCUST_IMAGE=${repositoryUri}"
ECS用docker contextの作成
docker-compose.yml
docker-compose.ymlは以下のようになります。LOCUST_IMAGE
でコンテナイメージのリポジトリを指定します。またx-aws-protocol
プロパティでALBで使用するプロトコルを指定しています。このオプションを含むECS向けのオプションはECS integration Compose features を参照。
version: '3' services: master: image: ${LOCUST_IMAGE} ports: - target: 8089 x-aws-protocol: http command: -f /mnt/locust/locustfile.py --master worker: image: ${LOCUST_IMAGE} command: -f /mnt/locust/locustfile.py --worker --master-host master ulimits: nofile: #同時接続数が多くなる場合に備えて上限を設定しておく soft: 65535 hard: 65535
クラスタ起動
ECS上でのクラスタ起動もdocker composeで行えます。up
サブコマンドを実行するとCloudFormationによって一連のリソースが生成されます。
# コンテキスト切り替え docker context use your_ecs_context # クラスタ起動 LOCUST_IMAGE=xxxx docker compose up --scale worker=3
スタック作成完了後にhttp://ALBのDNS名:8089
にアクセスするとlocustのUIが表示されます。
デフォルトではアクセス元が無制限になっているので必要に応じて制限をしたり、HTTPSリスナーを設定してください。
以下のワンライナーでDNS名を表示できます。
aws elbv2 describe-load-balancers \ --load-balancer-arns (aws cloudformation describe-stack-resource \ --stack-name YOUR_STACK_NAME \ --logical-resource-id LoadBalancer | jq .StackResourceDetail.PhysicalResourceId) | \ jq .LoadBalancers[].DNSName
カスタマイズ
今回紹介した例は必要最小限な手順なので実際に使用する際には用途に応じた追加の設定が必要だと思います。以下に私のユースケースにフィットしなかった点を挙げておきます。
docker compose stopでタスクを停止できない
利用料金を考えると使っていないときはタスクを停止したいところですが停止するにはdocker compose以外のインターフェースから操作する必要があります。またdownサブコマンドではスタックが削除されるので意図しない削除が行われないよう注意が必要です。
テストスクリプトの変更、切り替えが容易にできない
今回の例ではdocker-composeファイルに実行するテストスクリプトファイル名をハードコードしているのでテストスクリプトの変更が容易に行えません。以下のような対応案を検討し、2つ目の方法を採用しました。
- コンテナには外部ストレージ(パラメータストアなど)から実行するスクリプトファイル名を取得するブートストラップスクリプトを含め、テストスクリプトはS3から取得する
- 任意のテストスクリプトを実行するタスク定義を作成、サービスの更新を行うことで更新する (スクリプト化)
テストスクリプト自体の更新はローカルで十分にテスト実行ができる前提があり頻繁に更新することはないだろうということで対応はしていません。
まとめ
docker-compose ECS integrationを使ってなるべく少ない設定でローカルとAWS環境上にlocustクラスタを構築してみました。最後に書いたように実際はここで紹介した例よりもカスタマイズや追加のコーディングをしましたが最初のクラスタ作成までの時間がとても短かったので見通しがよく効率よく作業できました。