🐳

EC2の中で起動しているDockerコンテナの死活監視

2021/10/04に公開

概要

EC2の中でDockerコンテナを2つ起動している(Composeを利用)

  • Webコンテナ
  • DBコンテナ

通常であればDBコンテナは別ホストにするかRDSにすべきですが今回は考えない。
このうち、Webコンテナの死活監視をしたい。なるべくシンプルに。

作業の流れ

  • インスタンス起動時にコンテナが起動するように設定。
  • Dockerのヘルスチェック機能でステータスを出力しておく。
  • ホスト側でcronを使ってステータスを取得してCloudWatchメトリクスに送信。(1ならOK,0ならNGみたいな感じで。)
  • CloudWatchメトリクスにアラームを設定。メトリクスが異常であれば対象のEC2を再起動させ、コンテナを復旧させる。

インスタンス起動時にコンテナを起動

https://dev.classmethod.jp/articles/ec2-finished-yo-use-cloudinit/

このへんを参考に、インスタンス起動時にコンテナを起動するスクリプトを作成して配置しておきます。今回はdocker-compose upで起動します。
ポイントはこのスクリプトはrootユーザーで実行されるのでディレクトリのあたりとかちょっと注意です。フルパスでdocker-compose.ymlを指定して起動するようにします。

Composeのヘルスチェックについて

https://docs.docker.jp/engine/reference/builder.html#healthcheck

私はここで調べるまで知らなかったのですが結構前から実装されていたようです。これを使う事でヘルスチェックを実装する事ができます。実装するとdocker psコマンドでステータスを確認する事ができます。

version: "3.9"
services: 

  web:
    build:
      context: .
    image: test/web:latest
    healthcheck:
      test: ["CMD-SHELL", "curl http://localhost:80 || exit 1"]
      start_period: "30s" # 開始時の間隔。コンテナ初期化するのに必要な時間を指定します。
      retries: 3 # リトライ回数
      timeout: "5s" # タイムアウトの長さ
      interval: "60s" # ヘルスチェックの間隔
    ports: 
      - "80:80"

Dockerfileで設定する事もできますが、docker-composeで設定する事でいちいちDockerImageをビルドする必要がないので便利です。

docker psでステータスを確認してみます。

起動直後:Up 2 seconds (health: starting)
起動中:Up About a minute (healthy)

こんな感じです。

注意点として、ヘルスチェックが行ってくれるのはステータスをデーモンに送るまでで、もしこれがunhealthyになったとしても何もしてくれません。

  • コンテナを再起動させたい
  • どこかに通知を送りたい

といったヘルスチェックの結果に応じた処理は別に実装する必要があります。

ステータスチェックを行うスクリプトを作成

では実際にヘルスチェックが失敗した時の処理を実装していきます。

docker ps --filter name=sample --format "{{.Status}}" | awk '{print $NF}'

これでステータスのチェック結果をシンプルに出力する事ができるので、これをシェルスクリプトに仕込んで定期実行するようにします。
その中でCloudWatchメトリクスに送信するようにしておきます。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/cloudwatch-push-custom-metrics/

put-metric-dataコマンドを使えば送信できそうですね。これで行きましょう。
死活監視なのでプロセス数を送信するようにした方が他のWebサービス監視とプロット結果が統一できるので揃えておく事にします。なのでOKだったら1でNGだったら0をメトリクスに送信します。

#!/bin/sh

STATUS=$(docker ps --filter name=sample --format "{{.Status}}" | awk '{print $NF}')

if [ $STATUS = "(healthy)" ]; then
  VALUE=1
else
  VALUE=0
fi

/usr/local/bin/aws cloudwatch put-metric-data --metric-name ProcessMonitoring --namespace Processes --value ${VALUE} --dimensions "Monitoring=sample.web"

awsコマンドをフルパスにしないといけないのに気づくのが遅れました。最初は実行結果をログに出力しておいた方が良いです。

*/10 * * * * sh /home/ssm-user/cronjob.sh > /tmp/cronjob.log 2>&1

こうしておくとログが出るので、最初は確認できるようにしておいたほうが良さそうです。
次はAWSの設定です。

CloudWatchアラームの設定

以下のようにCloudWatchメトリクスにステータスが送信されているのでこれにアラームを設定し、異常になったらアクションするように設定します。

今回はシンプルに該当インスタンスを再起動するように設定します。
1がOKなので0になったらアラーム発動→SNSパブリッシュ→Lambda実行(インスタンス再起動)の構成でいきます。

EC2のメトリクスであればEC2アクションが使えるので楽ですが今回は使えないです。

動作確認

EC2に入りコンテナを落とした状態で待機。次の10分でステータスが更新された際に異常となりました。

そしてSNSにパブリッシュされLambdaが起動し、EC2再起動で無事サービスが復旧しました。やったね!

次にやりたい事

インスタンスごと再起動だとダウンタイムが激しいので、コンテナだけ復旧できるようにしたい。
→ヘルスチェックはDockerデーモンが実行しているので、メトリクスが止まった場合はコンテナが落ちたかDockerデーモンが落ちたかのどちらか。なのでまずはデーモンが動いているかチェックし、その後コンテナを起動。みたいな。

参考文献

https://www.tothenew.com/blog/monitoring-docker-container-status-using-aws-cloudwatch/
https://qiita.com/moomindani/items/aef16aa93db56b071ba3
https://dev.classmethod.jp/articles/aws-shellscript-summary/#process-monitoring

調査内容(結論に至るまでの道筋なので見なくても大丈夫)

だいたい調べてもECSが出てきて、EC2でDockerコンテナを動かすケースが少ない。(当然か)なのでやってる人が結構少ない。

Dockerコンテナの死活監視で調べたらだいたい別の監視サービスが出てくる(DataDog、Mackerelとか)
→シンプルに実装したい+お金かかるのでナシ。

CloudWatch Agentとかその辺で解決できないだろうか?
→メトリクスは取れるけど今回は死活監視なのでちょっと合わない。

Composeのヘルスチェックは?
→ヘルスチェック自体はしてくれるけど、特定の条件下になったらコンテナを立て直すみたいな機能がない。その部分は別で実装する必要がある。これは使えそう。後はどうやってコンテナを作り直すか。
→数時間以内に復旧すればOK+インスタンス起動時に自動でコンテナ起動するようになっているので一旦はEC2再起動で乗り越える。

Discussion