ECSのTaskを同期的に実行するコマンドecs_task_executorを作った
ECSのTaskの実行について
ECSにはServiceとTaskという二つのコンテナの実行方法がある。
Serviceは主にwebサーバーなどに代表されるような常に一定数のコンテナを保つように管理されるもので、Taskの方は一回実行したらそれっきりで終了するバッチ的な利用を想定されている。
困ること
API経由でのTaskの実行は非同期で行われる
どういうことかというと、awsコマンドが実行成功した時点で正常終了してしまうということが起きる。
つまり実際にコンテナ上で実行したタスクがしばらくした後に異常終了した場合でもAPIを叩いた側は気づく事が出来ない。
以下は、ecs-cliでの実行例だが、sleep 120を処理しているコマンドでも実行自体は13秒で終わっている事がわかる。
$ time ecs-cli compose --project-name kenjiszk-test --file docker-compose.yml run web "sleep 120" INFO[0000] Using ECS task definition TaskDefinition="kenjiszk-test:3" INFO[0000] Starting container... container=e6fece2d-6f6f-44ec-95b9-44f37c7c1205/web INFO[0000] Describe ECS container status container=e6fece2d-6f6f-44ec-95b9-44f37c7c1205/web desiredStatus=RUNNING lastStatus=PENDING taskDefinition="kenjiszk-test:3" INFO[0013] Started container... container=e6fece2d-6f6f-44ec-95b9-44f37c7c1205/web desiredStatus=RUNNING lastStatus=RUNNING taskDefinition="kenjiszk-test:3" real 0m13.105s user 0m0.129s sys 0m0.098s
で、何が困るか?
連続するタスクがあって、それぞれ直前のタスクが成功した場合のみだけ処理を進めたいような場合、デフォルトの挙動だと失敗しようがしなかろうが成功してしまうのでどんどん先に進んでしまう。
それどころか、タスクの終了も待たないので、タスク自体が重なって実行されてしまうことになる。
具体的な例をあげると、Railsのデプロイ処理の場合
- db:migrateなどのdbの処理
- コード(コンテナ)の入れ替え
という流れを経るので、db:migrateが失敗したとしても新しいコードをデプロイしてしまうというやばいことも起きかねない。
そういった処理をする場合には、同期的に実行する事が不可欠になる。
同期的に実行できるようなコマンドを作った
という事で、同期的にコンテナ状のタスクを実行できて、失敗した場合にはしっかり失敗するようなコマンドを作成した。
github.com
READMEを見てもらえるとだいたいどういった処理になっているかわかるが、内部でやっていることは
といった単純な処理をしている。
run_and_fail.shというしばらくすると失敗するscriptを用意して実行しみた例。
以下のように終了まで待っているのと、異常終了コードをハンドリング出来ている。
$ ecs_task_executor --cluster Sample -t kenjiszk-test:1 -n web -c 'run_and_fail.sh' Set timeout as 600 sec. LastStatus=PENDING TimeElapsed=5.024269701s LastStatus=PENDING TimeElapsed=10.045879005s LastStatus=PENDING TimeElapsed=15.070078421s LastStatus=RUNNING TimeElapsed=20.092714283s LastStatus=RUNNING TimeElapsed=25.109575722s LastStatus=RUNNING TimeElapsed=30.131358467s LastStatus=RUNNING TimeElapsed=35.150669821s LastStatus=RUNNING TimeElapsed=40.16816051s LastStatus=RUNNING TimeElapsed=45.185257392s LastStatus=RUNNING TimeElapsed=50.209708875s { ContainerArn: "arn:aws:ecs:ap-northeast-1:000000000:container/df87b9cd-962f-4580-ad8d-f6b97446b9a2", ExitCode: 255, HealthStatus: "UNKNOWN", LastStatus: "STOPPED", Name: "web", NetworkBindings: [], NetworkInterfaces: [], TaskArn: "arn:aws:ecs:ap-northeast-1:00000000:task/de79a37c-4c51-4307-826c-f6a3c7d733cb" } $ echo $? 255
今後
自前でコマンドを作って見たが、実は同期的にTaskを実行できるオプションがあるんじゃないかとちょっとヒヤヒヤしている。
ecs-cliがタスクを非同期でしか実行してくれないからキックした後のエラーを拾ってくれなくて、同期的に実行できるラッパー書いたのだけど、ecs-cliに実はそういうオプションがありそうでならない。誰かもっと簡単な方法知っている人いないだろうか。https://t.co/C9xM6dswtY#ecs #aws
— Kenji Suzuki (@kenjiszk) 2018年6月23日