Fargate利用時のヘルスチェックを理解する

記事タイトルとURLをコピーする

こんにちは 技術課の山本です

近所を車で走っていたらシカが信号待ちをしていました
この街の夜は人よりシカを見かけることが多い気がします

独特な信号待ちをするニホンジカ

ニホンジカ

さて本題です

Fargate利用時のヘルスチェックを理解する

本記事の前提として以下の構成とします

Fargateクラスターの構成例

Fargate クラスターの中に以下のサービスがあります

  • Application Load Balancer (ALB) を経由してユーザーリクエストを受ける サービスA (ALBのターゲットは nginx コンテナ)
  • 単体で動く サービスB

ヘルスチェックは以下 2 種類のいずれかを行うことになります

  1. Application Load Balancer (ALB) によるヘルスチェック
  2. ECSサービスによるヘルスチェック(タスク定義に設定)

まずはそれぞれの仕様と注意点を見ていきましょう

1. Application Load Balancer (ALB) によるヘルスチェックの仕様

仕様

3行で要約します
1. Application Load Balancer (ALB)は、登録されたターゲットに定期的にリクエストを送信して、それらのステータスをテストします。これらのテストをヘルスチェックと呼びます
2. Application Load Balancer (ALB)は、正常なターゲットにのみリクエストをルーティングします ※正常なターゲットと見なされるには、ヘルスチェックに合格する必要があります
3. ターゲットグループに異常な登録済みターゲットのみが含まれている場合、ロードバランサーは、ヘルスステータスに関係なく、それらすべてのターゲットにリクエストをルーティングします

ヘルスチェックの設定値は以下になります
ドキュメントから簡単に抜粋します

設定 説明
プロトコル ターゲットにヘルスチェックを実行するときにロードバランサーが使用するプロトコル。使用可能なプロトコルは HTTP および HTTPS です
ポート ターゲットでヘルスチェックを実行するときにロードバランサーが使用するポート。デフォルトでは、各ターゲットがロードバランサーからトラフィックを受信するポートを使用します
パス ターゲットでのヘルスチェックの送信先。プロトコルバージョンが HTTP/1.1 または HTTP/2 の場合は、有効な URI (/ パス ?クエリ ) を指定します。デフォルトは / です
タイムアウト ヘルスチェックを失敗と見なす、ターゲットからレスポンスがない時間 (秒単位)。範囲は 2~120 秒です。ターゲットタイプが instance または ip の場合のデフォルトは 5 秒です
間隔 個々のターゲットのヘルスチェックの概算間隔 (秒単位)。範囲は 5 ~ 300 秒です。ターゲットタイプが instance または ip の場合のデフォルトは 30 秒です。「タイムアウト」よりも長い時間を設定します
非正常のしきい値 非正常なインスタンスが非正常であると見なすまでに必要なヘルスチェックの連続失敗回数。範囲は 2 ~ 10 です。デフォルトは 2 です
正常のしきい値 非正常なインスタンスが正常であると見なすまでに必要なヘルスチェックの連続成功回数。範囲は 2 ~ 10 です。デフォルトは 5 です
成功コード ターゲットからの正常なレスポンスを確認するために使用するコード。プロトコルバージョンが HTTP/1.1 または HTTP/2 の場合、指定できる値は 200~499 です。複数の値 (例: "200,202") または値の範囲 (例: "200-299") を指定できます。デフォルト値は 200 です。

設定画面(ターゲットグループから設定)

ヘルスチェックのステータスは以下になります
ドキュメントから抜粋します

ステータス 説明
initial ロードバランサーは、ターゲットを登録中か、ターゲットで最初のヘルスチェックを実行中です
healthy ターゲットは正常です
unhealthy ターゲットはヘルスチェックに応答しなかったか、ヘルスチェックに合格しませんでした
unused ターゲットがターゲットグループに登録されていないか、ターゲットグループがロードバランサーのリスナールールで使用されていないか、ロードバランサーに対して有効ではないアベイラビリティーゾーンにターゲットがあるか、ターゲットが停止または終了状態にあります
draining ターゲットは登録解除中です
unavailable ターゲットグループのヘルスチェックは無効になっています

ステータス確認画面 (ターゲットグループから確認)
ステータス確認画面2 (ターゲットグループから確認)

注意点

  • HTTP/HTTPS によるヘルスチェックとなる
  • 可能な限り「パス」には作成したヘルスチェックモジュールのパスを指定する
    • ヘルスチェックの成功条件を定義してヘルスチェックモジュールを作成しターゲットに配置する
      • 例:ヘルスチェックモジュールは /healthcheck.jsp に配置する & ヘルスチェックモジュールは DynamoDBのあるテーブルを参照可能なことを確認し ステータス 200 を返却する
  • 「タイムアウト」(秒)の値が小さいとヘルスチェックに失敗(unhealthy)しやすくなる
    • リクエストの集中する時間帯等にはヘルスチェックが応答するまでの時間が長くなりタイムアウトしやすくなるため
    • アプリケーションのタイムアウト値やALB に設定しているタイムアウト値も参考にして決定する
  • 「間隔」(秒)の値が小さいとヘルスチェックに失敗(unhealthy)しやすくなる
    • ヘルスチェックのリクエスト数が多くなりターゲットの負担が大きくなるため
  • 正常/非正常のしきい値はヘルスチェックモジュールの成功/失敗確率に応じて設定
  • ターゲットのコンテナのみに対してヘルスチェックするため nginx コンテナにのみヘルスチェックをする
    • flask コンテナにはECSサービスによるヘルスチェック(タスク定義に設定)が必要

(補足)ALBをECSのサービスと紐付けている場合のECSの動作仕様

ALBのヘルスチェックで異常と判定したターゲットに紐づくECSタスクをECSが停止

ALBのヘルスチェックで異常と判定したターゲットに紐づくECSタスクはECSが停止します
そしてサービスに設定したタスクの数に達するまで新しいタスクを作成します

ヘルスチェックで異常になるとサービスのイベントに unhealthy in target-group ...とログが出ます

ECS のサービス画面からALBのヘルスチェックで異常と判定したイベントを確認

そしてタスクを停止し新しいタスクを起動したログがその後に出ます

参考:
Service load balancing - Amazon Elastic Container Service

If a service's task fails the load balancer health check criteria, the task is stopped and restarted. This process continues until your service reaches the number of desired running tasks.

タスクが起動してヘルスチェックに合格するようになるまでに時間がかかる場合には ALBのヘルスチェック開始を遅らせる事が可能(ヘルスチェックの猶予期間)

タスクが起動してヘルスチェックに合格するようになるまでに時間がかかる場合には ALBのヘルスチェック開始を遅らせることもECS側の設定で可能です
ECSでサービスを作成/更新する際に「ヘルスチェックの猶予期間」を指定可能です
デフォルトは 0 秒です (待たない)

ヘルスチェックの猶予期間

参考: Service definition parameters - Amazon Elastic Container Service

The period of time, in seconds, that the Amazon ECS service scheduler should ignore unhealthy Elastic Load Balancing target health checks, container health checks, and Route 53 health checks after a task enters a RUNNING state. This is only valid if your service is configured to use a load balancer. If your service has a load balancer defined and you do not specify a health check grace period value, the default value of 0 is used. If your service's tasks take a while to start and respond to health checks, you can specify a health check grace period of up to 2,147,483,647 seconds during which the ECS service scheduler ignores the health check status. This grace period can prevent the ECS service scheduler from marking tasks as unhealthy and stopping them before they have time to come up.

2. ECSサービスによるヘルスチェック(タスク定義に設定)の仕様と注意点

仕様

タスク定義に各コンテナに行うヘルスチェックのコマンドを定義することが可能です
ヘルスチェックのコマンドはコンテナ毎に定義可能です

設定箇所 (タスク定義)

  • タスク内にヘルスステータスが異常のコンテナがある場合にタスクのヘルスステータスは 異常になります
    • サービスの一部であるタスクの場合、異常なタスクは停止され、サービススケジューラが新しいタスクに置き換えます
    • タスクがサービスの一部としてではなく手動で実行される場合、タスクはヘルスステータスに関係なく継続実行します
  • ヘルスチェックを定義していないコンテナのヘルスステータスは、タスクのヘルスステータスに影響を与えません(無影響)
  • ECSはタスク定義に指定したヘルスチェックのみを実行します
    • コンテナーイメージに埋め込んだDockerヘルスチェックを実行しません

ヘルスチェックの設定値は以下になります
ドキュメントから簡単に抜粋します

設定値 説明
コマンド コンテナーが正常であるかどうかを判別するためにコンテナーが実行するコマンド。コマンド引数を直接実行するためにCMDで開始するか、コンテナーのデフォルトシェルでコマンドを実行するためにCMD-SHELLで開始できます。どちらも指定されていない場合、デフォルトでCMDが使用されます ※CMD-SHELLはShell、 CMDはExecと同じ
マネジメントコンソールから記載する場合:
CMD-SHELL, curl -f http://localhost/ || exit 1
AWS CLI から記載する場合:
[ "CMD-SHELL", "curl -f http://localhost/ || exit 1" ]
間隔(秒) ヘルスチェックの実行間隔。 5〜300秒を指定できます。デフォルト値は30秒​​です
タイムアウト(秒) ヘルスチェックが失敗と見なされる前に成功するのを待つ期間。 2〜60秒を指定できます。デフォルト値は5秒です
再試行 失敗したヘルスチェックを再試行する回数。 1〜10回の再試行を指定できます。デフォルト値は3回の再試行です
開始期間(秒) コンテナが起動してヘルスチェックを受けられるようになるまでの待機期間(ヘルスチェックの猶予期間)。 0〜300秒を指定できます。 startPeriodはデフォルトで無効になっています

ヘルスチェックのステータスは以下になります
ドキュメントから抜粋します

状態 説明
HEALTHY コンテナのヘルスチェックに合格しました。
UNHEALTHY コンテナのヘルスチェックが失敗しました。
UNKNOWN コンテナヘルスチェックが評価されているか、コンテナヘルスチェックが定義されていません。

タスクのヘルスステータスと各コンテナのヘルスステータスは実行中のタスク画面から確認できます

タスクのヘルスステータスと各コンテナのヘルスステータス

ヘルスチェックに失敗するとイベントに failed container health checks. と出ます

ECS のサービス画面からコンテナのヘルスチェックが異常になったイベントを確認

注意点

  • コマンドによるヘルスチェックとなる
    • HTTP/HTTPSリクエストを送る際には curl コマンド等を利用する
    • タスク内のコンテナに通信する際には 127.0.0.1:<ポート番号> (localhost:<ポート番号> と書いても良い) 宛に通信する
      • Fargateは awsvpcモードで動作しているため 1つのタスクに ENI を 1つ割り当てる仕組み そのため 127.0.0.1:<ポート番号> への通信は同一タスク内の通信となる (localhost と書いても良い)
      • 例:curl -f http://localhost/ || exit 1
  • 「タイムアウト」(秒)と「間隔」(秒) の注意点については Application Load Balancer (ALB) と同じ
  • 再試行の値はヘルスチェックコマンドの成功/失敗確率に応じて設定

(補足)コンテナ間に依存関係がある場合

あるコンテナが起動してヘルスチェックが正常になってから他のコンテナを起動する

同じタスク内にあるコンテナのヘルスステータスが「正常」になってから
他のコンテナをタスク内に起動することが可能です

タスク定義で依存関係を設定

Task definition parameters - Amazon ECS
dependsOn を参照

ALBのヘルスチェックを使ってバックエンドのコンテナもヘルスチェックをする

サービスAの構成では
ALBのターゲットである nginx コンテナに対するヘルスチェックをします
その際にバックエンドにある flask コンテナのヘルスチェックをも実施可能です

前提:
flask コンテナが 5000 ポートで待ち受けている
flaskコンテナのパス: /healthcheck にヘルスチェック関数がある

nginx.conf を以下のように記載します

events {
}
http {
    server {
            location /healthcheck {
                proxy_pass http://127.0.0.1:5000;
            }
            #access_log  /var/log/nginx/access.log;
            #error_log  /var/log/nginx/error.log warn;
        }
}

ALB のヘルスチェック のパスに /healthcheck を指定すると以下の動きになります

  1. nginx コンテナの /healthcheck に向けたリクエストが nginx によって flask コンテナ(5000ポート待受) に転送される(リバースプロキシ)
  2. flask コンテナが /healthcheck にあるヘルスチェック関数を実行して ステータスコードを返す

まとめ

Fargate のヘルスチェックをする際に ALB のヘルスチェックを使ったり
ECSタスク定義のヘルスチェックを使ったりする方法を見てきました
ヘルスチェックは提供するサービスの正常な動作を担保するための肝となる部分です
仕様を把握して適切な設定が出来るようこの記事を書きました
紹介した2つのいずれかのヘルスチェックを上手に活用していきましょう
お役に立てると嬉しいです

余談

見出し:「ALBのヘルスチェックで異常と判定したターゲットに紐づくECSタスクをECSが停止」
に記載した仕様により一つ悲しいことがありました
自分の環境で検証をしていた際に 誤ったヘルスチェックパスを指定してしまい 以下の 1〜4 が交互に延々と繰り返されてしまいました

  1. ALBのヘルスチェックで異常(UNHEALTHY)となる
  2. ALBが登録解除
  3. ECSがタスクを停止
  4. ECSが新しいタスクを起動

誤った ヘルスチェック設定をしたため 異常(UNHEALTHY)になった ターゲット

ECSサービスのイベントログ

あろうことか
そのまま帰宅してしまい 次の日に Cost Anomaly Detection を見てみると 毎時間 15GB のアウトバウンド通信を行った料金が発生していました
ECSが新しいタスクを起動する際に ECR から NAT Gateway 経由でコンテナイメージを pull していたのですね ^^;

毎時間15GB近いアウトバウンド通信(Cost Anomaly Detection画面)

ヘルスチェックの設定を誤るとこういった通信料も発生してきます
気を付けましょう(自戒)
ECR用 の VPCエンドポイントもあるので運用時には利用しましょう
Cost Anomaly Detection は便利ですね

blog.serverworks.co.jp

山本 哲也 (記事一覧)

カスタマーサクセス部のエンジニア(一応)

好きなサービス:ECS、ALB

趣味:トレラン、登山(たまに)