ALB経由のアプリが502 Bad Gatewayを返すとき、ALB本体が壊れているケースはほとんどありません。502は「ALBはターゲットへの接続には成功したが、有効なHTTPレスポンスを受け取れなかった」という意味です。つまり調査対象はバックエンド側になります。
502の原因は4パターンに分かれる
現場で遭遇するALBの502は、だいたい次の4つに収束します。
- バックエンドのKeep-AliveタイムアウトがALBのアイドルタイムアウトより短く、再利用しようとした接続が切られている
- バックエンドがHTTPとして不正なレスポンスを返している(ヘッダが大きすぎる、Content-Lengthが実体と不一致、ヘッダに不正な文字が混入)
- アプリケーションプロセスがクラッシュまたは再起動中で、接続直後に切断されている
- HTTPSターゲットでTLSハンドシェイクが成立していない
いずれも、ALBが接続した直後からレスポンス受信の途中までのどこかで失敗しています。
503と504との違いを押さえる
| ステータス | ALBが判断している内容 | 主な原因 |
|---|---|---|
| 502 | 接続はできたが、レスポンスが不正、または途中で切れた | Keep-Alive不一致、不正ヘッダ、クラッシュ、TLS失敗 |
| 503 | ルーティングできるhealthyなターゲットが1つもない | 全台unhealthy、ターゲット未登録 |
| 504 | アイドルタイムアウト内に応答を返しきらなかった | 遅いクエリ、外部API待ち |
ここを混同すると、504向けの対処であるアイドルタイムアウト延長を502に適用してしまいます。502では逆効果になるので注意してください。
アクセスログでALB生成かターゲット由来かを判別する
まず見るのはtarget_status_codeです。-ならALBが生成した502、502ならバックエンドが返した502をALBが中継しただけで、調べる先はALBより奥になります。CloudWatchでもHTTPCode_ELB_502_CountとHTTPCode_Target_5XX_Countを分けて見れば同じ判別ができます。
アクセスログはAthenaでクエリするのが実用的です。CREATE TABLE文はログ形式のバージョンでカラムが増えるため、公式ドキュメントの最新DDLを使ってください。
SELECT time,
client_ip,
target_ip,
elb_status_code,
target_status_code,
request_processing_time,
target_processing_time,
response_processing_time,
error_reason,
classification,
classification_reason,
request
FROM alb_logs
WHERE elb_status_code = '502'
AND time >= '2024-01-01T00:00:00.000000Z'
ORDER BY time DESC
LIMIT 100;
target_status_codeが-でtarget_processing_timeが-1なら、接続切断や不正レスポンス系の典型です。request_processing_timeが-1なら、ターゲットが受信前に接続を閉じています。classification_reasonにBadHeaderやMultipleContentLength、BadContentLengthが出ていれば、そこが直接の原因です。なおerror_reasonに値が入るのは主にLambdaターゲットで、EC2やIPターゲットでは-のままが多い。
低頻度の502はKeep-Alive不一致を疑う
全リクエストの0.01〜1%程度で断続的に502が出るなら、タイムアウトの序列を確認します。ALBはターゲットへの接続をKeep-Aliveで再利用します。バックエンドのKeep-AliveタイムアウトがALBのアイドルタイムアウト(デフォルト60秒)より短いと、バックエンドがFINを送った直後にALBが同じ接続へリクエストを送り、502になります。
対処はバックエンド側をALBより長くして、数秒の余裕を持たせることです。
# nginx(ALB のアイドルタイムアウトが 60 秒の場合)
keepalive_timeout 75s;
Node.jsならkeepAliveTimeoutとheadersTimeout、Goならhttp.ServerのIdleTimeoutが該当します。
ターゲットへ直接curlして発生源を切り分ける
ALBを挟まずターゲットへ直接リクエストすれば、不正レスポンスの発生源がアプリ自身かどうか判断できます。同一VPC内の踏み台や同じサブネットのEC2から実行します。
# ヘッダを含めて表示(ALB 経由時と比較する)
curl -sv http://10.0.1.23:8080/api/users -o /dev/null
直接curlは200なのにALB経由だけ502なら、ヘッダサイズやKeep-Alive、TLSといったALBとターゲットの間の問題です。直接curlでも異常が出るならアプリやWebサーバの設定を見ます。再現が断続的なら、Keep-Alive不一致かデプロイ由来を優先して疑ってください。
なおレスポンスヘッダサイズの上限やLambdaレスポンスの上限値は変わり得ます。設計に組み込む前にElastic Load Balancingの公式ドキュメントとクォータで現在値を確認してください。
詳しくは
元記事では、不正ヘッダやContent-Length不一致の具体的な発生例、HTTPSターゲットの証明書とプロトコル確認、Lambdaターゲットが要求する戻り値の形、ECSとEKSのデプロイ時に502が集中する場合のDeregistration delayやpreStopの設定まで扱っています。あわせてCloudWatchメトリクスの読み合わせ方、502のアラームをALB生成分とターゲット由来で分ける設定例、再発防止としてのタイムアウト序列の固定も載せています。