猫でもわかるWebプログラミングと副業

本業エンジニアリングマネージャー。副業Webエンジニア。Web開発のヒントや、副業、日常生活のことを書きます。

AWS ECS + Java アプリケーションの立ち上がりが遅い場合のヘルスチェック設定【AWS CDK】

AWS ECS + Java の場合の注意点と解決方法

AWS ECS で Java アプリケーションを動かそうとすると、最初の立ち上がりが遅くて、ヘルスチェックが通らないことがあります。

Docker が立ち上がってから Java のアプリケーションが立ち上がるまでに時間がかかるのですが、ECS は(デフォルトだと) Docker が立ち上がったらすぐヘルスチェックを開始するので、まだアプリケーションが立ち上がっておらず、ヘルスチェックが失敗します。

Java 以外の言語だと立ち上がりが早いのでハマることはあまりないんですが、Java の場合はよくハマってしまいます。

ECS へのデプロイ時に、最初のヘルスチェックが通らないと、コンテナが立ち上がってはストップを繰り返して、ECS のデプロイが終わらなくなります。たまに運良くヘルスチェックが通って成功したりします。  

このような場合、LB (Load Balancer) のヘルスチェックの設定を見直すのがよいです。AWS ECS において、ヘルスチェックの設定は大きく2箇所あります

  • LB のターゲットグループのヘルスチェックの設定
    • ヘルスチェックの間隔
    • ヘルスチェックが失敗になるタイムアウトの時間
    • など...
  • ECS Service で設定する「ヘルスチェックの猶予期間」の設定

Java の立ち上がりが遅い問題については、「ヘルスチェックの猶予時間」を調整することで解決するのが良いです。

ヘルスチェックの猶予期間(healthCheckGracePeriodSeconds)について

[Amazon ECS Service Definition Parameters)https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service_definition_parameters.htmlhealthCheckGracePeriodSeconds の説明があります。

以下、公式ドキュメントのざっくり日本語訳


Amazon ECS において、タスクのステータスが RUNNING になってから、この時間の間は、ELB ターゲットグループのヘルスチェックなどが失敗していても、無視します。単位は「秒」で指定します。ロードバランサを利用している ECS Service でのみ利用できます。デフォルトは0秒です。

タスクが start してから、ヘルスチェックに応答できるようになるまで時間を要する場合に利用できます。最大値は 2,147,483,647 秒です。この設定を使えば、タスクが Unhealthy と判定されて止まってしまうのを防ぐことができます。

※ドキュメントに出てくる「ECS service scheduler」とは、ECS のタスクの start と stop を管理している仕組みのことです。


このパラメータは 2017年12月に追加されたようです

この設定は ECS の「サービス」の方で設定するもので、 Load Balancer の方の設定ではないので注意してください。

AWS CDK で healthCheckGracePeriodSeconds を設定する

例えば、 ApplicationLoadBalancedFargateService を使っている場合は以下のように設定します

import { Duration } from "aws-cdk-lib";
...


    const ecsService = new ApplicationLoadBalancedFargateService(this, "...", {
      // (中略)
      // Java が立ち上がるまでに時間がかかるため、ヘルスチェックを遅らせる
      healthCheckGracePeriod: Duration.seconds(120),
    });

ECS のログでヘルスチェックの様子を確認する

上記解決方法で解決したらOKなのですが、一応、ECS でヘルスチェックの様子を確認する方法を書いておきます。

デプロイ時のヘルスチェックの様子は、AWS のコンソールで、対象となるサービスの画面を開き、「デプロイ」または「イベント」のタブを確認すると見られます。

Amazon Elastic Container Service > クラスター > {クラスタ名} > サービス > {サービス名}

と開いてくと、各サービスの画面が見られると思います。

ただし、残念ながら非常に見づらいです...が、僕が知っている限り、他にいい感じのログとかはないのでここで確認するしかないです。

通常は以下のようにログが出ていると思います

  • service XXX has started 1 tasks: task {task_id_1}
    • 新しくタスク(Dockerコンテナ)が実行されます
  • service XXX registered 1 targets in target-group {target_group_name}
    • 新しく実行された Docker コンテナが Load balancer の target-group に追加されます
  • service XXX has stopped 1 running tasks: task {task_id_0}
    • 古いタスク(Docker コンテナ)が終了されます
  • service XXX deregistered 1 targets in target-group {target_group_name}
    • 古いタスク(Dockerコンテナ)が LB から外れます
  • (service XXX, taskSet ecs-svc/YYY) has begun draining connections on 1 tasks.
    • LB から外れるのにともない、古いタスクにリクエストが来なくなります

これがコンテナの数だけ繰り返されます

このログの task_id を見比べて、start したタスクと別のタスクが stop していればとくに問題がありません。 start した task_id がすぐに stop して、それが延々繰り返される場合は、最初のヘルスチェックに失敗している可能性が高いです。

さらに残念なことに、まだ Java のアプリケーションが立ち上がってないので、アプリケーションログを出力することも非常に難しいです *1

AWS CDK でヘルスチェック周りの設定

当然ですが、 AWS CDK でヘルスチェック自体の設定もできます。例えば以下のように設定します。他にも設定項目はあるので、必要に応じて調べてください。

const ecsService = new ApplicationLoadBalancedFargateService(...);

ecsService.targetGroup.configureHealthCheck({
  path: "/health",
  // ヘルスチェックの間隔。デフォルト10秒
  interval: Duration.seconds(30),

  // ヘルスチェックのリクエストのタイムアウト。デフォルト5秒
  timeout: Duration.seconds(10),

  // unhealthy な状態から healthy な状態になるために3回ヘルスチェックが成功すれば良い
  // デフォルトは5回
  // 間隔を30秒にしていると5回のヘルスチェックに2分半かかるので3回に減らしている
  healthyThresholdCount: 3,
});

参考ページ

ここから先、特に追加のコンテンツはありませんが、役に立ったら投げ銭してもらえると嬉しいです。

*1:tomcat の設定とかで何らかのログを出すことはできるかも

この続きはcodocで購入