DSOCインフラチームの藤田です。コロナの影響で大好きなバスケットボールができずに悶々とした日々を送っていましたが、最近Netflixで配信されたマイケル・ジョーダンのドキュメンタリーを視聴して、更に悶々としています。マイケル・ジョーダンについて名前は知ってるけど詳しく知らないという人にもおすすめな内容になっています。なぜなら僕もその一人なのです。
ここ数ヶ月はあるシステムのコンテナ化をメイン業務として進めていました。移行先のマネージドプラットフォームとしてECS on EC2を採用し、主にECSクラスターや監視・ログ収集の設計・構築に取り組んでいます。その中でぶつかった問題、その解決のために作成したツールについて紹介したいと思います。
DAEMONサービスを使うときの問題
ECSインスタンスに対してドレイニングという処理を行うことができます。これは対象インスタンス上で起動しているサービスタスクをクラスター内の他のインスタンスで起動、対象インスタンスのタスクを停止させ、以降新しいタスクが起動しないようにします。この機能はインスタンス内のタスクを全て安全に停止してからインスタンスを終了させるのに役に立ちます。具体的にはECSインスタンスのスケールイン、AMIの更新などで便利です。
ここで1つ問題があります。タスクの停止命令はインスタンスで起動している全タスクに対して同時に送られます。そのため、REPLICAサービスよりもDAEMONサービスが先に停止してしまう可能性があります。例えばDAEMONサービスとしてFluentdが稼働していた場合、REPLICAサービスよりも先に停止してしまいログを取りこぼしてしまうことが予想されます。この問題はGitHubでもissueとして上がっています。
解決策
概要
DAEMONサービスにサイドカーを追加し、コンテナの停止順序に依存関係をもたせることで直ぐに停止しないようにしました。
ECSのタスク定義の中に dependsOn
という設定があります。これはタスクの起動・停止順序の依存関係を定義することができます。これを利用してDAEMONサービスのコンテナのサイドカーとしてecs-daemon-protectorという今回作ったコンテナを起動させます。起動順序を DAEMONサービスのコンテナ
→ ecs-daemon-protector
とすることで、停止順序は逆の ecs-daemon-protector
→ DAEMONサービスのコンテナ
となります。タスク定義は以下のようになります。この例だとecs-daemon-protectorが停止するまでfluentdは停止されません。
[ { "name": "fluentd", "image": "fluent/fluentd:latest", "essential": true }, { "name": "ecs-daemon-protector", "image": "iamwaneal/ecs_daemon_protector:latest", "dependsOn": [ { "containerName": "fluentd", "condition": "START" } ] } ]
ecs-daemon-protectorはインスタンスがドレイニングされた時、インスタンス上の全てのREPLICAタスクが終了するまで待ち続けるコンテナです。これらの設定によりREPLICAサービスのコンテナが全て停止するまでDAEMONサービスの停止を防ぐことができます。
プログラムの補足
普段はsleepし続けます。停止命令(SIGTERM)を受け取ったら、インスタンスで起動している全てのREPLICAサービスのタスクが停止するまで待ってから終了するというプログラムです。
REPLICAサービスのコンテナが全て停止しているかどうかの確認は少し工夫しました。例えばバッチ処理を行うようなコンテナは停止命令を受けると中断処理が始まり、それが完了してから停止するというケースがあります。そのため、コンテナによってはDesired StatusはSTOPPEDなのにLastStatusがRUNNINGで実は動いているという状況が生まれます。そこでDesiredStatusだけでなく、LastStatusが全てSTOPPEDになっていることまで確認するようにしました。
最後に
FargateやEKSを利用されている方には全く刺さらない内容ではありましたが、同じ問題で困っている方がおられましたらお力になれると幸いです。
buildersbox.corp-sansan.com
buildersbox.corp-sansan.com
buildersbox.corp-sansan.com