こんにちは。SCSKの末本です。
本記事では、AWSで構築しているシステムの保守運用をする中で発生した事象と、その解決策についてご紹介します。
発生した事象
今回のシステムは、Amazon Elastic Container Service(以下 ECS)を利用しており、1タスクで複数のコンテナを構成する環境です。
発生した事象は、AWS_ECS_TASK_PATCHING_RETIREMENT によって ECS のタスクが生まれ変わる際、クライアントからのアクセスに対して一時的に HTTP 503 エラーが返されたというものです。
なぜ 503エラー が発生したのか?
今回のシステム構成と、問題についてご説明します。
システム構成(1タスク内)
- tomcat コンテナ:
アプリケーションを実行するバックエンドとして動作するコンテナ - web(Apache)コンテナ:
フロントエンドとして動作し、ロードバランサーからのリクエストを受け取り、tomcat コンテナへ転送するコンテナ - log コンテナ:
ログ収集および転送を行うサイドカーコンテナ
AWSによる ECSパッチ適用時の流れ
AWS が ECSのパッチ適用をするため、ECSタスクの入替(新しいタスクを起動し、古いタスクを停止)が行われます。
この流れの中で、タスクがリクエストを受け付ける準備が整う前に、クライアントからアクセスされてしまうという問題が発生しました。
- 新しいタスクが起動を開始する。
- web(Apache)コンテナ、log コンテナが比較的早く起動する。
(この時点では、バックエンドの tomcat はまだアプリケーションレベルでの準備ができていない。) - ヘルスチェックを有効化したコンテナ(web・log)の状態をもって、タスク全体が早期に起動完了(RUNNING)と見なされる。
今回、tomcat コンテナにヘルスチェックが設定されていませんでした。
そのため tomcat のアプリケーション初期化時間にかかわらず、ヘルスチェックを有効化したコンテナ(web・Log)のヘルスステータスがすべて Healthy であることで、タスク全体が起動完了したと見なされました。ご参考:Amazon ECS タスクライフサイクル – Amazon Elastic Container Service
- タスクがターゲットグループに登録され、クライアントからアクセスが可能な状態になる。
- クライアントからのリクエストが web(Apache)に到達するが、バックエンドの tomcat コンテナはまだ起動途中。
結果として Apache はバックエンドに接続できず、クライアントに HTTP 503 Service Unavailable を返す。
今回の問題
今回の問題は、ロードバランサーのヘルスチェック(Apacheのポートをチェック)がOKでも、タスク内の コンテナ間の依存関係 が考慮されていなかったため、サービスが利用可能な状態になる前にアクセスされてしまったことです。
【回避策】ECSタスク定義の依存関係(dependsOn)の活用
この問題を解決するために、ECSタスク定義内の dependsOn プロパティとコンテナヘルスチェックを組み合わせて設定しました。
これにより、コンテナ間の起動順序と、次のコンテナが起動を開始するための 条件 を定義できます。
dependsOn パラメータの意味については以下の通りです。
| 設定値(条件) | 意味 |
|---|---|
| START | 依存するコンテナの起動開始後、実行 |
| COMPLETE | 依存コンテナの実行が完了(終了)後、実行 |
| SUCCESS | 依存コンテナの実行が正常終了後、実行(exit code:0) |
| HEALTHY | 依存コンテナに定義した healthcheck に合格後、実行 |
修正後のコンテナ依存関係の設計
- フロントエンド(web)が起動する前にバックエンド(tomcat)が完全に起動することで、リクエスト処理可能な状態とする。
- log コンテナがログファイルを正しく転送できるよう、各コンテナ起動後に log コンテナを起動させ、転送対象のファイルが存在しないというエラーの発生を防ぐ。
| コンテナ | 依存設定 | 設定値(条件) | 内容 |
|---|---|---|---|
| tomcat | (なし) | – | 依存関係なしのため、まず最初に起動する。 |
| web(Apache) | tomcat | HEALTHY | tomcat がアプリケーションレベルで完全に起動し、 サービス可能になるまで待機する。 |
| log | web(Apache) | HEALTHY | web コンテナのプロセス起動が確認されてから、 ログ転送を開始する。 |
設定内容
単に START(コンテナプロセスが起動)を待つだけでは、アプリケーションの初期化時間などを考慮できません。
この問題を解決するために、tomcat コンテナのタスク定義内に コンテナヘルスチェック を設定し、web(Apache)コンテナおよび log コンテナの dependsOn 設定で、HEALTHY 条件を指定しました。
// ECSタスク定義(コンテナ定義の抜粋イメージ)
"containerDefinitions": [
{
"name": "tomcat",
// ... 他の設定
// Tomcatがサービス可能であることを確認するコンテナヘルスチェック
"healthCheck": {
"command": [
"CMD-SHELL",
"curl -f http://localhost:8080/status || exit 1"
],
"interval": 30, // 30秒ごとにチェック
"retries": 3, // 3回失敗したらUNHEALTHY
"startPeriod": 10, // 起動直後の10秒間はヘルスチェックの失敗を無視(猶予期間)
"timeout": 6 // タイムアウトは6秒
}
},
{
"name": "web",
// ... 他の設定
// webコンテナはtomcatがHEALTHY(サービス可能)になるまで起動を待機
"dependsOn": [
{
"containerName": "tomcat",
"condition": "HEALTHY" // ここが重要!
}
]
},
{
"name": "log",
// ... 他の設定
// logコンテナはwebがHEALTHY(サービス可能)になるまで起動を待機
"dependsOn": [
{
"containerName": "web",
"condition": "HEALTHY" // ここが重要!
}
]
}
]
上記の設定により、今回の事象は解決し、ECSタスクのパッチ適用時に503エラーが出なくなりました。
今回の対応から学んだ3つの設計ポイント
1. ALBヘルスチェック ≠ サービス利用可能性
ロードバランサーのヘルスチェックは、外部から特定のポートが応答しているかを確認するもので、タスクへのトラフィックを流すかどうかを判断します。
しかしマルチコンテナタスクでは、「ポートが開いている」ことと「アプリケーションがリクエストを処理する準備ができている」ことは同義ではありません。
- ALBヘルスチェック:タスクへのトラフィック受付可否を判断
- コンテナヘルスチェック:タスク内のコンポーネントのサービス提供可否を判断
2. マルチコンテナ設計における依存関係の明示
ECSタスクの設計において、各コンテナの起動順序を意識して設計しなければ、今回のようなコンポーネント間の疎通エラーを招きます。
以下のように、役割に応じて依存関係を定義することが、システムの信頼性を高めると学びました。
- フロントエンドは、バックエンドの HEALTHY 状態を待つ。
- サイドカーコンテナは、メインコンテナの START または HEALTHY 状態に依存する。
3. startPeriod(起動猶予期間)の活用
Tomcat などの Javaアプリケーションは、プロセス起動からリクエスト処理可能になるまでに数十秒かかることがあります。
dependsOn パラメータで HEALTHY を使う場合、tomcat コンテナのヘルスチェック定義に startPeriod(起動猶予期間)を設定することで、コンテナが起動直後の不安定な状態で即座に失敗と見なされるのを防ぐことができます。
今回の対応で可用性を高めることができましたが、デメリットとして タスク全体の起動時間の増加 があげられます。
本システムでは、開発環境で検証した上で、起動時間の増加を許容する方針にしました。
まとめ
AWS ECSのタスク入れ替え時の503エラーは、一見するとシンプルなネットワークエラーに見えますが、その裏には「複数コンテナの非同期起動」という本質的な課題が隠れていました。
最後までお読みいただきありがとうございました!

