0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS ECS Fargate で 504 が出た時の調査と解決

0
Posted at

はじめに

AWS ECS Fargate にデプロイしたアプリへアクセスすると 504 Gateway Timeout が出た。
最初は ECR への接続エラーが原因に見えたが、調べると別の原因が潜んでいた。
その切り分け手順と最終的な解決方法をまとめます。

前提条件

カテゴリ 項目
基盤 AWS サービス Amazon ECS(Fargate 起動タイプ) / Application Load Balancer / Amazon ECR
基盤 リージョン us-west-2(オレゴン)
ECS クラスタ名 my-app-cluster
サービス名 my-sm-service
desired count 3
ALB ALB 名 my-sm-alb(インターネット向け)
ターゲットグループ名 my-sm-tg
プロトコル / ポート HTTP / 8080
コンテナ コンテナ名 sessionmanagement
コンテナポート 8080
ヘルスチェック パス /api/users/status
期待 HTTP ステータス 200
ネットワーク サブネット配置 パブリックサブネット2つ
パブリック IP 割り当て assignPublicIp: ENABLED
アプリ 種別 REST API
フレームワーク / 言語 Spring Boot / Java
作業ツール 操作環境 AWS CLI v2(コンソール GUI と等価の操作を CLI で実施)

発生していた状況

  • ECS Fargate(us-west-2)にデプロイ
  • ブラウザでアクセスすると 504
  • CloudWatch のイベントログに ECR への接続エラー
service my-sm-service was unable to place a task.
Reason: ResourceInitializationError: unable to pull secrets or registry auth:
... dial tcp 34.223.26.179:443: i/o timeout.

切り分けの流れ

1. ECR への到達性

エラー文面そのままに、タスクが ECR からイメージを取得できていない可能性 を疑った。
Fargate タスクが ECR に届かない典型例は次の3つ。

  • パブリックサブネットに置いているが assignPublicIp が無効
  • プライベートサブネットで NATVPCエンドポイント も無い
  • セキュリティグループのアウトバウンドで 443 が閉じている

2. ネットワーク設定を順に確認

サービスのネットワーク設定を見る。

aws ecs describe-services \
  --cluster my-app-cluster \
  --services my-sm-service \
  --region us-west-2 \
  --query "services[0].networkConfiguration.awsvpcConfiguration"

結果は assignPublicIp: ENABLED、サブネット2つ、SG1つ。パブリック IP は付くようになっている。

次にルートテーブル。

aws ec2 describe-route-tables \
  --region us-west-2 \
  --filters "Name=association.subnet-id,Values=subnet-...,subnet-..." \
  --query "RouteTables[].{RouteTableId:RouteTableId,Routes:Routes}"

0.0.0.0/0 → igw-...(active)があり、両サブネットとも本物のパブリックサブネットだった。

SG のアウトバウンドも全許可、NACL もデフォルトで全許可。
ネットワーク設定は全て正しい

3. 現状を再確認

設定上は問題ないので、サービスの今の状態を確認した。

aws ecs describe-services \
  --cluster my-app-cluster --services my-sm-service \
  --region us-west-2 \
  --query "services[0].{Desired:desiredCount,Running:runningCount,Deployments:deployments[].{Rollout:rolloutState}}"
{ "Desired": 3, "Running": 3, "Deployments": [{ "Rollout": "FAILED" }] }

Running: 3タスクはすでに起動していた。最初の ECR エラーは過去のもので、その後解消していた。本当の 504 の原因はここから先にある。

4. ALB のターゲットヘルスを確認

Rollout: FAILED が引っかかる。これはヘルスチェックの失敗で起こる典型的なパターン。

ALB の 504 は「ターゲットに転送はできたが応答が無い」時に発生する(ターゲット皆無の時は 503)。
つまり ALB → タスク間に問題がある。

aws elbv2 describe-target-health \
  --target-group-arn arn:aws:elasticloadbalancing:us-west-2:...:targetgroup/my-sm-tg/... \
  --region us-west-2

3つすべてのターゲットが unhealthy / Target.Timeout / Request timed out

ヘルスチェック設定は /api/users/status(HTTP 8080、期待コード 200)。
Target.Timeout はパスのミスマッチではなく、パケットが無言で破棄されている 状態を意味する。
SG が ALB → タスクの 8080 を通していない可能性が濃厚。

5. 真の原因 — SG インバウンドの送信元不一致

タスク SG のインバウンドを確認すると、8080 を許可するルール自体は存在していた。
ただし 送信元が、現役のALBのSGではない別の SGを指していた。

その送信元 SG を調べると、現在どの ENI にも紐づいていない「残骸 SG」だった。
おそらく ALB を作り直した際に SG が変わり、タスク SG 側は古い SG を参照したままだった、というパターン。

タスク SG が許可していた送信元: sg-cccc3333(旧/残骸 SG)
実際の ALB が持っている SG: sg-bbbb2222(ALB SG)
→ ALB の通信は許可対象に該当せず、無言で破棄 → ヘルスチェック失敗 → 504

検証した項目と結果

切り分けで確認した各設定を実際の値本来あるべき値で並べて比較する。
不一致は 1箇所だけ だったことが一目で分かる。

確認項目 実際の設定 あるべき値 判定
サブネットの assignPublicIp ENABLED ENABLED
サブネットのルートテーブル 0.0.0.0/0 → igw-...(active) IGW への経路あり
タスク SG のアウトバウンド 全許可(-1, 0.0.0.0/0) 443 が外部に出られる
ネットワーク ACL デフォルト(全許可) 全許可で問題なし
タスク SG インバウンド プロトコル TCP TCP
タスク SG インバウンド ポート 8080 8080(コンテナポートと一致)
タスク SG インバウンド 送信元 sg-cccc3333(旧/残骸 SG) sg-bbbb2222(ALB の SG)
ALB ターゲットグループ ポート 8080 8080
ヘルスチェックパス /api/users/status アプリが 200 を返すパス
ヘルスチェック期待コード 200 200

解決

タスク SG に「ALB の SG からの TCP 8080」を1本追加するだけ。

aws ec2 authorize-security-group-ingress \
  --group-id sg-aaaa1111(タスク SG) \
  --protocol tcp \
  --port 8080 \
  --source-group sg-bbbb2222(ALB SG) \
  --region us-west-2

画面から行う場合は、

EC2 コンソール → セキュリティグループ → タスク SG を選択 → インバウンドルールを編集 → カスタム TCP / 8080 / ソースに ALB の SG を指定 → 保存。

1〜2分待つと ALB が再判定して全ターゲットが healthy になり、ブラウザからのアクセスでも 504 が消えた。

ハマりどころと学び

  • ALB の 504 と 503 は意味が違う
    503 は「ターゲットなし」、504 は「ターゲットには到達したが応答なし」。
    504 ならアプリ側か SG インバウンドを疑う
  • Target.Timeout(Request timed out)は SG が無言ドロップしているサイン
    Connection refused や 404 とは区別する
  • エラーログは過去のものかもしれない
    診断時はまず「今どうなっているか」を確認する
  • SG ルールはポート番号だけでなく送信元 SG の妥当性も見る
    古い SG を参照していると一見正しく見えて実は無効。リソースを作り直した後は要注意

まとめ

  • 表面のエラー(ECR タイムアウト)は古いログで、現在の真因ではなかった
  • 真因は タスク SG のインバウンドが、現役の ALB の SG ではない古い SG を許可していた こと
  • ALB の SG をタスク SG のインバウンドに追加するだけで解決
  • 504 を見たら、ECR → ネットワーク → ALB → SG → アプリの順で切り分けると見落としが少ない

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?