【実務・中級編】 ALBにおけるHTTPステータスコード504 (Gateway Timeout) の発生原因とタイムアウト設定 – クラウド&コンテナネットワーク実践ガイド

ALBで「504 Gateway Timeout」に泣かないために:現場で使えるタイムアウト戦略とデバッグの真髄

「またか……」

真夜中のアラート通知で叩き起こされ、ダッシュボードに飛び込む。そこにあるのは無機質な 504 Gateway Timeout のログの山。AWSのALB(Application Load Balancer)を使っているエンジニアなら、一度は経験する「登竜門」です。

教科書には「バックエンドが60秒以内にレスポンスを返さなかった」と書いてありますが、現場で起きているのはもっと泥臭い、アプリケーションの処理遅延やネットワークの断絶かもしれません。今日は、この憎き504を確実に仕留めるための、現場の知見を詰め込んだ「504攻略ガイド」を執筆します。

—

1. なぜ「504」が起きるのか? パケットの視点で考える

ALBは「プロキシ」です。クライアントとバックエンドの間に入り、TCPコネクションを張り直す役割を担っています。

ALBの仕様において、504 は「ALBがバックエンドからのレスポンスを待ったが、タイムアウト時間(アイドルタイムアウト)以内に何も届かなかった」ときに発生します。

通信シーケンスのリアル

1. クライアント -> (SYN) -> ALB
2. ALB -> (SYN) -> バックエンド(EC2/ECS/Lambda)
3. ALB はバックエンドからの HTTP 200 OK などのレスポンスを待機。
4. ここでカウントダウン開始!(デフォルトは60秒)
5. 60秒経過してもバックエンドが FIN も DATA も返さない。
6. ALB は無慈悲にもクライアントに対し、504 Gateway Timeout を返す。

ここで重要なのは、「ALBが504を返した後も、バックエンドは一生懸命処理を続けているかもしれない」という点です。これを「ゾンビプロセス」と呼ぶこともあります。

—

2. アイドルタイムアウトの調整:闇雲に伸ばすのは悪手

よくある間違いが「504が出るから、タイムアウト設定をデフォルトの60秒から300秒に延ばせば解決だ!」という判断です。これは多くの場合、傷口を広げるだけです。

なぜ安易な延長がいけないのか

  • リソースの枯渇: バックエンドのコネクションが長時間占有され、結果として後続の新しいリクエストを捌けなくなり、雪崩式に障害が拡大します。
  • UXの低下: ユーザーはブラウザの画面が真っ白なまま数分間待たされることになります。

設定の確認と変更(Terraform例)

もし正当な理由で調整が必要なら、AWSコンソールだけでなく、Infrastructure as Codeで管理しましょう。

# AWS ALBのリスナー設定
resource "aws_lb" "main" {
  name               = "my-app-alb"
  internal           = false
  load_balancer_type = "application"
  
  # アイドルタイムアウトを120秒に変更する場合
  idle_timeout = 120 
}

—

3. 現場で使える「504」デバッグ手順

504が発生した際、まず確認すべきは「どこで詰まっているのか」です。

① ALBアクセスログの確認

ALBのアクセスログにある target_processing_time を確認してください。これがタイムアウト値に近い値であれば、バックエンドの処理が重いことが確定します。

② curlによる疎通確認

特定のパスで再現するなら、curl で詳細なタイミングを追跡します。

# -w オプションで各フェーズの時間を計測
curl -so /dev/null -w "Time to connect: %{time_connect}s\nTime to first byte: %{time_starttransfer}s\nTime total: %{time_total}s\n" https://api.example.com/heavy-task

③ バックエンド側のスローログ

PythonやPHP、Goなどのアプリケーション側で「どのSQLで詰まっているのか」「どの外部APIコールで待たされているのか」をログ出力させます。

Python (FastAPI/Flask等) でのタイムアウト実装例:

import requests

def call_external_api():
    try:
        # バックエンドから外部へ出す際も、必ずタイムアウトを明示する
        # これを忘れると、ALBのタイムアウトまでバックエンドがハングする
        response = requests.get("https://third-party-api.com", timeout=(3.05, 10))
        return response.json()
    except requests.exceptions.Timeout:
        # ここで適切に例外をキャッチして500や503を返す
        print("外部APIへのリクエストがタイムアウトしました")

—

4. プロからのアドバイス:根本解決の切り札

もし、どうしても処理に時間がかかる API がある場合は、設定値の調整ではなく「設計の見直し」が必要です。

1. 非同期処理への移行: リクエストを受け取ったら 202 Accepted を即座に返し、実際の処理は SQS や Redis 経由でバックグラウンドワーカー(Celery, Sidekiq等)に投げましょう。
2. コネクションの再利用: Keep-Alive が無効になっていないか確認してください。TCPのハンドシェイクコストが積もり積もって、レスポンスを遅延させているケースは非常に多いです。
3. ロードバランサーのメトリクス: TargetResponseTime の P95/P99 を CloudWatch で監視し、504が発生する前に予兆を検知するアラートを仕込みましょう。

最後に

ネットワークのトラブルシューティングは、パケットの旅路を想像する力が必要です。「ALBは悪くない、バックエンドが詰まっているのか? それともネットワークの経路で何か起きているのか?」と、一つずつ疑いを消していく作業こそが、エンジニアとしての確かな地力になります。

もし今日、ログに 504 が出ても焦らないでください。それは、あなたのシステムが「限界」を教えてくれているサインです。感謝しつつ、冷静に原因を切り分けていきましょう。

現場からは以上です。健闘を祈ります。

コメント

タイトルとURLをコピーしました