ECSとスポットインスタンスは相性がよく比較的多くの場所で推奨されていますが、そのままでは強制Terminate時にきれいにECSクラスタを抜けられないことはあまり良く知られていません。(構築・運用したことがある人はすぐに気づきますが)
今回は簡単な説明と具体的な対応を書いています。
なぜ対応が必要なのか
スポットインスタンスが強制Terminateされる時に適切な処理が行われていないと下記のような事象が発生します。なので、プロダクションで利用する際は対応はほぼ必須かと思います。
- 該当インスタンス上のコンテナがELBから接続されている場合に、Drainingされずに停止されていしまう。
- Drainingされなかったリクエストが503 Errorとなる。
- 状況によっては十分な数のコンテナが存在しない時間帯が発生する。
どんな対応が必要なのか
公式ドキュメントにも記載がありますが、基本的には コンテナインスタンスの登録解除 をすることにより、下記作業が行われます。
- 該当インスタンス上で可動しているコンテナのELBからのDraining
- 該当インスタンス上で可動しているコンテナの、他インスタンスへのオフロード。
また、2分あれば十分かと思いますがELBのDrainingの許容時間なども合わせて見直したほうがいいかと思います。
ただ、自分で設定するのはめんどくさい、、ので今回はCloudFormationのテンプレートを用意しました。
CloudFormation テンプレート
https://github.com/mats16/ecs-spot-deregister
基本的にはこちらのテンプレートを実行してすればOKです。(リージョン毎に設定されるので、ECSをお使いのリージョンでCFnを実行してください)
基本的には構成図のとおりですが下記のようなことを行っています。
- CloudWatch Eventsで強制Terminationの2分前通知を検知
- Lambda Functionを実行
- Lambda内からAPIを叩き、該当インスタンスをECSクラスタからDeregisterする
中のコードを見ていただければわかりますが、instance_id
からcluster_name
とcontainer_instance_id
を直接参照できるAPIが無いので、やや微妙な書き方になっています。
あとがき
本投稿は、個人の意見であり、所属する企業や団体は関係ありません。お約束です。
久しぶりにpython書いたのでご指摘や機能要望があれば、PullRequestやIssue頂ければと思います。
(SNS挟んだほうがいいとか、Slackに通知したいなど)