ようへいの日々精進XP

よかろうもん

ECS + ecs-cli + Locust + consul でカヂュアルにスケールする負荷試験を構築する考察

ども、かっぱです。

tl;dr

考察シリーズ第三弾。

hakobera.hatenablog.com

上記の記事で紹介されていた「AWS ECS + Docker + Locust による負荷テスト」について、どんな風に実現するんだろうなあとずーっと気になっていたので、自分なりに ECS + Locust な負荷試験環境を考えてみたのでメモ。


メモ

こんな感じになった(ゴール)

f:id:inokara:20160319042029p:plain

ソースコードとか

github.com

何をする必要があったのか

  • Locust Master と Locust Slave をそれぞれ異なるタスクに分割する
  • 同一の ECS クラスタに Master と Slave を個別にデプロイする
  • コンテナインスタンスを跨って Locust クラサバ構成を構築する必要があったので etcd や Consul にお出まし頂いた(結局は Consul を採用した)

困った点

  • Docker Compose では docker-compose.yml 内に定義されている Service 毎に scale コマンドが発行することが出来たが、ecs-cli compose 経由だと Task 毎にしか scale することが出来ない
  • Consul も Docker コンテナで立ち上げているが、Consul コンテナは必ず各コンテナインスタンス上で起動する必要があるので手動でコンテナを起動する必要があった
  • Locust の Master ノードコンテナが起動しているコンテナインスタンス IP を保存するだけのみ Consul を動かしておくのはちょっと無駄が大きい(かも)
  • Consul クラスタの構成をどうするか(Atlas で Token を発行して --atlas-join を利用した)

Demo

  • 事前に Locust Master と Locust Slave コンテナをビルドして ECR あたりに push しておく
$ aws --region us-east-1 ecr get-login
$ docker login -u AWS -p xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -e none https://xxxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com
$ git clone https://github.com/inokappa/sample-ecs-locust.git
$ cd sample-ecs-locust
$ cd docker-locust-master
$ docker build 
$ docker build -f Dockerfile.master -t docker-locust-master .
$ docker tag docker-locust-master xxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/docker-locust-master
$ docker push xxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/docker-locust-master
$ cd ../docker-locust-slave
$ docker build -f Dockerfile.slave -t docker-locust-slave .
$ docker tag docker-locust-slave xxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/docker-locust-slave
$ docker push docker-locust-slave xxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/docker-locust-slave
$ ecs-cli up --keypair ${SSH_KEY_NAME} \
  --capability-iam \
  --size 2 \
  --vpc vpc-xxxxxxxx \
  --instance-type t2.small \
  --subnets subnet-xxxxxxxx,subnet-xxxxxxxx \
  --azs ap-northeast-1a,ap-northeast-1c

Consul も利用するので、Consul Server を動かすホスト(コンテナインスタンス)の数は奇数が望ましいけど、今回は検証ということでコンテナインスタンスは 2 台で起動する。

また、ecs-cli でコンテナインスタンスを起動した場合には、セキュリティグループは 0.0.0.0/0 で HTTP ポート(80 番)が開放されたルールのみがアタッチされるが、各コンテナインスタンス間で Consul の通信を許可するルールも追加する必要があるので注意する。

  • Consul コンテナの起動(事前に Atlas で token を発行しておくこと)
#
# 一台目のコンテナインスタンスで consul server を起動
# - -bootstrap オプションは古いオプションなので基本的には利用しない
#
$ docker run --net=host -d \
-p 8300:8300 \
-p 8301:8301/tcp \
-p 8302:8302/tcp \
-p 8301:8301/udp \
-p 8302:8302/udp \
-p 8303:8303 \
-p 8400:8400 \
-p 8500:8500 \
-p 8600:8600 \
--name consul-node01 \
gliderlabs/consul-server \
-server \
-bootstrap \
-advertise $(curl -s http://169.254.169.254/latest/meta-data/local-ipv4) \
-atlas=inokappa/demo01 --atlas-join --atlas-token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

#
# 二台目のコンテナインスタンスで consul server を起動
#
$ docker run --net=host -d \
-p 8300:8300 \
-p 8301:8301/tcp \
-p 8302:8302/tcp \
-p 8301:8301/udp \
-p 8302:8302/udp \
-p 8303:8303 \
-p 8400:8400 \
-p 8500:8500 \
-p 8600:8600 \
--name consul-node02 \
gliderlabs/consul-server \
-server \
-advertise $(curl -s http://169.254.169.254/latest/meta-data/local-ipv4) \
-atlas=inokappa/demo01 --atlas-join --atlas-token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

#
# consul クラスタの確認(consul クラスタ自体は 3 ノード以上で奇数ノードでの構成が望ましいので注意する)
#
$ docker exec -t -i consul-node02 consul members
Node           Address          Status  Type    Build  Protocol  DC
ip-10-0-0-164  10.0.0.164:8301  alive   server  0.6.3  2         dc1
ip-10-0-1-242  10.0.1.242:8301  alive   server  0.6.3  2         dc1
  • Locust Master コンテナを起動する
#
# environment で TARGET_HOST を指定する
#
$ vi ecs-compose-master.yml
master:
  image: xxxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/docker-locust-master
  cpu_shares: 100
  mem_limit: 268435456
  environment:
    - TARGET_HOST=http://kome.inokara.com


#
# Task として Locust Master コンテナを起動する
#
$ ecs-cli compose -f ecs-compose-master.yml up

以下のようにブラウザからアクセスすることが出来る。

f:id:inokara:20160319023249p:plain

また、consul には Locust の Master ノードコンテナが起動しているインスタンスのプライベート IP アドレスが登録されている。

$ curl 127.0.0.1:8500/v1/kv/locust/master/ip?raw
10.0.1.242
  • Locust Slave コンテナを起動する
#
# environment で TARGET_HOST を指定する
#
$ vi ecs-compose-slave.yml
slave:
  image: xxxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/docker-locust-slave
  cpu_shares: 100
  mem_limit: 268435456
  environment:
    - TARGET_HOST=http://example.inokara.com

#
# Task として Locust Slave コンテナを起動する
#
$ ecs-cli compose -f ecs-compose-slave.yml up

以下のように Slave が追加されたことを確認する。

f:id:inokara:20160319025038p:plain

  • 負荷試験を開始する

f:id:inokara:20160319025219p:plain

  • Locust Slave をスケールアウトする
#
# 最初の Slave 1 台から 3 台に増やす
#
$ ecs-cli compose -f ecs-compose-slave.yml scale 3

以下のように出力される。

INFO[0000] Starting container...                         container=79377890-bf38-483c-a516-e865c8951372/slave
INFO[0000] Starting container...                         container=9137b17d-fb67-4d7f-9e16-92281beab362/slave
INFO[0000] Describe ECS container status                 container=79377890-bf38-483c-a516-e865c8951372/slave desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-docker-locust-slave:3
INFO[0000] Describe ECS container status                 container=9137b17d-fb67-4d7f-9e16-92281beab362/slave desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-docker-locust-slave:3
INFO[0006] Started container...                          container=79377890-bf38-483c-a516-e865c8951372/slave desiredStatus=RUNNING lastStatus=RUNNING taskDefinition=ecscompose-docker-locust-slave:3
INFO[0006] Started container...                          container=9137b17d-fb67-4d7f-9e16-92281beab362/slave desiredStatus=RUNNING lastStatus=RUNNING taskDefinition=ecscompose-docker-locust-slave:3

3 台にスケールさせるということで、2 台のコンテナが追加される。どのコンテナインスタンスに追加されるかは ECS 次第となる。

f:id:inokara:20160319025524p:plain

  • Locust Slave をスケールインする
#
# 最初の Slave 1 台から 1 台に減らす
#
$ ecs-cli compose -f ecs-compose-slave.yml scale 1

以下のように出力される。

INFO[0000] Stopping container...                         container=79377890-bf38-483c-a516-e865c8951372/slave
INFO[0000] Stopping container...                         container=9137b17d-fb67-4d7f-9e16-92281beab362/slave
INFO[0000] Describe ECS container status                 container=79377890-bf38-483c-a516-e865c8951372/slave desiredStatus=STOPPED lastStatus=RUNNING taskDefinition=ecscompose-docker-locust-slave:3
INFO[0000] Describe ECS container status                 container=9137b17d-fb67-4d7f-9e16-92281beab362/slave desiredStatus=STOPPED lastStatus=RUNNING taskDefinition=ecscompose-docker-locust-slave:3
INFO[0006] Stopped container...                          container=79377890-bf38-483c-a516-e865c8951372/slave desiredStatus=STOPPED lastStatus=STOPPED taskDefinition=ecscompose-docker-locust-slave:3
INFO[0006] Stopped container...                          container=9137b17d-fb67-4d7f-9e16-92281beab362/slave desiredStatus=STOPPED lastStatus=STOPPED taskDefinition=ecscompose-docker-locust-slave:3

1 台にスケールダウンさせるということで、2 台のコンテナが削除される。

f:id:inokara:20160319030951p:plain


最後に

課題

  • コンテナインスタンスの Auto Scaling をどうする?
  • たまに Master / Slave コンテナが正常に終了しない

ということで...

  • Quipper さんでは ECS + Locust をどのように実装し、活用しているのかめっちゃ気になる次第。機会があれば、是非お話しを伺いたい次第。