LoginSignup
18
13

More than 3 years have passed since last update.

初学者がlocustをAWS ECS(Fargate)で実行する

Last updated at Posted at 2020-06-17

https://qiita.com/sekikatsu/items/992e82671aa505c5a652 の続きです。

ローカル環境でlocustを実行できるようになったので、クラウドの豊富なマシンパワーを使って負荷掛けができるようにします。
Terraformを使って本格的にやる場合は https://qiita.com/neilli-sable/items/b17dfba5eabfcafccaf8 など既存記事があるので、ご参考ください。
この記事は、ECSを初めて使うくらいの人が、とりあえずAWS Webコンソールを使ってlocustを動かしてみる、という内容です。

序章

「ローカルで動いてるんだからAWSに移行するのも簡単やろ」と思ってたら、全然簡単じゃなかった。ECSのハードル高い。

とりあえず、スタート時点での知識レベルは以下くらい

流れ

以下のフローで実行できるようにしようとした

  1. master 1台 + slave N台を起動する
  2. コンテナが起動すると、指定したURLからlocustfile.pyをgit clone
  3. cloneしたlocustfile.pyを使ってlocustをmaster/slaveで起動
  4. WebUIから負荷掛け
  5. WebUIから結果をダウンロード。locustの場合、結果の回収方法を考えなくていいので楽(JmeterだとS3に転送したりしないといけない)

方針

  • locustio/locustイメージはgitが入っていないので、自作する
  • Dockerイメージ内にmaster/slaveそれぞれ用のスクリプトを用意して、それを叩くことでmaster/slaveの切り替えと複数コマンド実行を実現
    • masterとslaveでDockerイメージを分けるのは面倒だったので、横着して一つにまとめる
    • ECSのCMDで複数コマンドを実行する方法がわからなかったので、スクリプトを置いてコマンドを一つにする
      • 今回はここに一番時間を取られた
      • CMDに/bin/bash,-c,'git clone ** && locust **'を設定すると、No such file or directoryと心ないことを言われる
      • CMDに/bin/bash,-c,git,clone,(略)を設定しても、動かず、かつログも出力されず、心が折れた
    • 今思えば、イメージ内に入れるのではなく、これもGitHubに入れてgit cloneで取得すれば良かった
  • 本当はMasterへの接続先を自動で取ってきてSlaveを起動したかったんだけど、別のところでハマりすぎて心が折れたので、目で確認+手で入力、という最強の手段を用いる
  • 変数は全て環境変数経由で渡す

Dockerイメージを用意する

使う環境変数

  • LOCUST_FILE_GIT_URL: locustfile.pyを取得するための git clone URL
  • LOCUST_TARGET_URL: 負荷掛け対象のURL。あとでWebから上書きできるので、適当でもいい
  • LOCUST_MASTER: slave用。Masterのprivate IPアドレスを入れる。
ディレクトリ構造
- カレントディレクトリ
  - Dockerfile
  - master
  - slave

master用のエントリポイント。
Slaveの起動時に必要なMasterのIPアドレスを一応出力しておく(実際にはAWSコンソール上で見る方が早いから使わないけど)。

master
#!/bin/bash
echo "=== IP Address ===================="
ip address show
echo "=== Git clone ====================="
git clone ${LOCUST_FILE_GIT_URL} /locust_src
echo "=== Start LOCUST as master mode ==="
locust -f /locust_src/locustfile.py --master -H ${LOCUST_TARGET_URL}

slave用のエントリポイント

slave
#!/bin/bash
echo "=== Confirm to connect master ===="
ping -c 3 ${LOCUST_MASTER}
echo "=== Git clone ===================="
git clone ${LOCUST_FILE_GIT_URL} /locust_src
echo "=== Start LOCUST as slave mode ==="
locust -f /locust_src/locustfile.py --worker --master-host ${LOCUST_MASTER}

Dockerfile

Dockerfile
FROM python:3.6

RUN pip install locust

RUN mkdir /locust
ADD ./master /locust/master
ADD ./slave /locust/slave
RUN chmod +x /locust/master
RUN chmod +x /locust/slave

CMD ["/locust/master"]

この状態でdocker build + push。

docker build -t sekikatsu36/locust:0.0.1 .
docker push sekikatsu36/locust:0.0.1

できたのがこれ
https://hub.docker.com/repository/docker/sekikatsu36/locust/general

locustfile.pyの用意

githubリポジトリに作ってpushするだけ。
今回はPOSTを使った負荷掛けをしたかったので、Bodyに入れるパラメータも環境変数から取るようにした。

使う環境変数

  • LOCUST_TARGET_PATH: 負荷掛けの対象パス。上のLOCUST_TARGET_URLと組み合わせることでフルパスになる。
  • LOCUST_PARAM: パラメータをkey1=value;key2=value2の形式で入れる
locustfile.py
from locust import HttpUser, TaskSet, task, between, constant
import os

class UserBehavior(TaskSet):
    @task(1)
    def login(self):

        l = os.environ['LOCUST_PARAM'].split(';')
        tmp = map(lambda s:s.split('='), l)
        param = dict(tmp)

        self.client.post(os.environ['LOCUST_TARGET_PATH'], param)

class WebsiteUser(HttpUser):
    tasks = {UserBehavior:1}
    wait_time = constant(1)

できたのがこれ
https://github.com/sekikatsu36/locust-py

AWSでECSクラスタを作る

やっとAWSの出番。

  • クラスターテンプレートは「ネットワーキングのみ AWS Fargateを使用」を選択。
    • image
  • クラスタ名は適当につける
  • VPCはお好みで。
  • 負荷掛けにしか使わないので、Container Insightsは要らない
    • image

タスク定義を作る

どんなタスク(コンテナ)を実行するのか、定義する。
あくまでテンプレート的なもので、タスクの実行時に定義は上書きできるので、横着してタスク定義もmaster/slaveで使い回す。

  • 「起動タイプの互換性の選択」はFARGATE
    • image
  • タスクサイズは、実行する処理によるけど、試しに動かすならメモリ=1GB、vCPU=0.5で十分

    • image
  • 「コンテナの追加」ボタンから、作ったDockerイメージを登録する

    • コンテナ名は適当に
    • イメージはさっき作ったやつ。今回はsekikatsu36/locust:0.0.1
    • ポートマッピングは 8089(WebUI用)と5557/5558(Slave=>Master通信)を開ける
      • image
    • 環境はコマンドだけ/locust/masterを入れておく。なくてもデフォルトこれなので動くが、あとでSlaveを変更するときに入ってると個人的に変更しやすかった
    • 環境変数は必要な値を適宜設定。LOCUST_MASTERはあとで毎回上書きする必要があるので、適当に入れる。
      • image
    • ログ設定は特にいじらなければ、CloudWatch Logsの/ecs/{コンテナ名}に出力されるようになる。今回はそのまま。
  • 記載のないものはデフォルトで

セキュリティグループを作る

master/slaveに適用されるセキュリティグループを作る。
この操作自体はタスクの実行時にもできそうなUIをしているが、そちらはUIがダメダメすぎて使えないので、事前に作っておく。(このUIで無駄にてこずってしまった)

  • まず、ルールを何も買えない状態でセキュリティグループを作る。そうするとインバウンドルールが空になっているはず。
  • そのあとで、以下のインバウンドルールを追加する
    • WebUIにつなぐために、手元から8089ポートへの通信を開ける
    • Slave=>Masterの通信を許可するために、「ソース」にこのセキュリティグループ自体、「ポート範囲」に5557-5558を指定。
  • image

masterを実行

  • 作ったクラスターの「タスク」タブから、「新しいタスクの実行」を選ぶ
  • 「キャパシティープロバイダーの戦略」 でエラーが出る。が、今は使わないので「起動タイプへ切り替える」を選択して、FARGATEを選ぶ
  • VPCとサブネットを適当に選択
  • セキュリティグループはさっき作ったやつ
  • 記載のないものはデフォルトで

実行できたら、タスクのプライベートIPを確認する。
http://(プライベートIP):8089 にアクセスすると、locustの画面が出てくるはず

slaveを実行

masterと同じ手順でタスクを実行。違いは以下。

  • コンテナを以下のように上書き
    • コマンドを /locust/slave
    • 環境変数のLOCUST_MASTERを、MasterのプライベートIPアドレスに変更
  • セキュリティグループは同じのも使いまわせるが、8089も5557/5558も開ける必要はないので、気になるなら閉じたものを作成&設定
  • タスクの数を用途に合わせて適宜変更
  • サブネットは複数指定できるので、Slaveのサブネットを分散させたい場合、配置予定のサブネットを全て登録

slaveが立ち上がったあと、locustのWebUIでWorkerの数が増えていれば成功。

image.png

あとはローカルと同じように負荷掛けを実行するだけ

おまけ

masterとslaveの実行が若干面倒くさいので、CLIで叩きたいという欲が出る。

masterのタスク実行をCLIで行う

masterはタスク定義をそのまま実行するだけなので、簡単。

aws ecs run-task \
 --cluster locust-test \
 --count 1 \
 --launch-type FARGATE \
 --task-definition locust-base \
 --network-configuration "awsvpcConfiguration={subnets=['サブネット1','サブネット2',(略)],securityGroups=['セキュリティグループID'],assignPublicIp=ENABLED}"
  • サブネットはsubnet-***というサブネットIDをひたすら列挙するだけ
  • セキュリティグループIDもsg-***を設定する

蛇足だけど、AWS WebコンソールでこのID群を確認するのが面倒臭いのは私だけだろうか。UIの改善を求めたい。

slaveのタスク実行をCLIで行う。

slaveはコンテナの設定を上書きする都合で、ちょっと面倒くさい。
コンテナの設定値は差分をJsonで表現して与える必要がある。

$ #差分のJsonファイルの雛形を作る
$ echo "{\"containerOverrides\":[{\"name\":\"locust\",\"command\":[\"/locust/slave\"],\"environment\":[{\"name\":\"LOCUST_MASTER\",\"value\":\"MASTER_IP\"}]}]}" > override-for-slave-base.json 
$ cat override-for-slave-base.json | jq
{
  "containerOverrides": [
    {
      "name": "locust",
      "command": [
        "/locust/slave"
      ],
      "environment": [
        {
          "name": "LOCUST_MASTER",
          "value": "MASTER_IP"
        }
      ]
    }
  ]
}
$ #masterのIPアドレスを置換
$ sed "s/MASTER_IP/確認したマスターのIPアドレス/g" override-for-slave-base.json > override-for-slave.json
$ #slaveを立ち上げる
$ aws ecs run-task \
 --cluster locust-test \
 --count 10 \ #slaveの必要個数を入力
 --launch-type FARGATE \
 --task-definition locust-base \
 --network-configuration "awsvpcConfiguration={subnets=['サブネット1','サブネット2',(略)],securityGroups=['セキュリティグループID'],assignPublicIp=DISABLED}" \
 --overrides file://./override-for-slave.json

terraformで一発、とまではいかないけれど、まぁまぁ楽に起動できるようにはなった。

18
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
13