502にタイムアウトを延ばす人にならない
一言でいうと
502は「無効なレスポンスを受け取った」で、504は「時間内に応答がなかった」であり、この2つを混同すると、タイムアウトを延ばすだけで何時間も費やします。
なぜこれが問題なのか
障害対応で最も時間を食うのは、原因を見つけられないことではなく、間違った原因を確信することです。502を「バックエンドが死んだ」と決めつけると、バックエンドを再起動し、直らなければタイムアウトを延ばし、それでも直らなければサーバーを増やします。その間、本当の原因であるレスポンスヘッダーのサイズやコネクションの早期終了には、手を付けません。
数字があれば、この確信が根拠に変わります。アクセスログからURLごとの応答時間の上位と5xxの分布を取り出し、nginxのエラーログでconnection refusedとupstream timed outを別々に数えれば、502と504が自然に分かれます。この2つの数字を数えるのにかかる時間は、1分です。
502と504は別の話である
| コード | 意味 | nginxがこれを出すとき | よくある原因 |
|---|---|---|---|
| 502 Bad Gateway | アップストリームから無効なレスポンスを受け取った場合、または接続が拒否された場合 | 接続拒否、レスポンスのパース失敗、コネクションの早期終了 | バックエンドのダウン、バックログの飽和、レスポンスヘッダーがバッファーより大きい |
| 504 Gateway Timeout | アップストリームが決められた時間内に応答しなかった場合 | proxy_read_timeoutの超過 |
遅いクエリ、外部連携のレイテンシ、ロック待ち |
| 503 Service Unavailable | 利用可能なアップストリームがない場合 | すべてのサーバーが失敗として除外された場合 | 全体障害、ヘルスの判定エラー |
ここで最もよく間違えるのは、これです。502を「アップストリームが死んだ」と決めつけること。
仕様が言う502は、「ゲートウェイが上流から無効なレスポンスを受け取った」であって、「上流が応答しなかった」ではありません。応答は来たのに、プロキシが処理できなかった場合も、502です。代表的なのが、レスポンスヘッダーがプロキシバッファーより大きい場合です。
この状況が厄介な理由: アップストリームのヘルスチェックは、ずっと正常と出ます。ヘルスチェックの応答はヘッダーが小さいからです。そのため、「サーバーは問題なく動いているのに、特定のユーザーだけ502」になります。その特定のユーザーは、たいていSSOでログインしてCookieが大きいユーザーです。
proxy_buffer_size 16k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
そして必ず覚えておくこと: 502が出ているのにタイムアウトを延ばしても、何の効果もありません。タイムアウトは504の話です。この2つを混同すると、何時間も無駄にします。
診断のはしご: curlの5段階
症状があいまいなときは、この順序で絞り込みます。特に5段階目が、探索空間を半分にします。
# 1. 이름이 풀리는가
getent hosts api.example.com
# 2. 포트가 열려 있는가 (ping 은 이 환경에서 안 되니 쓰지 않는다)
curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 3 http://api.example.com/
# 3. 상태코드와 헤더
curl -sSI https://api.example.com/health
# 4. 시간 분해
curl -s -o /dev/null -w 'dns=%{time_namelookup} conn=%{time_connect} \
tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' https://api.example.com/
# 5. ★ 프록시를 건너뛰고 업스트림에 직접, 원래 Host 헤더를 유지한 채
curl -sSI -H 'Host: api.example.com' http://127.0.0.1:8080/health
このコードブロックの韓国語コメントは、順に、名前が解決できるか、ポートが開いているか(pingはこの環境では使えないので使わない)、ステータスコードとヘッダー、時間の分解、プロキシを飛ばしてアップストリームに直接(元のHostヘッダーを維持したまま)、という意味です。
5段階目の結果の解釈が核心です。
- アップストリームの直接呼び出しが正常なら、問題はプロキシとアップストリームの間(設定、ヘッダー、バッファー、タイムアウト)にあります
- アップストリームの直接呼び出しも失敗するなら、プロキシは無実です。アプリケーションやDBを見る必要があります
-H 'Host: ...'を外すと、この比較は無意味です。バーチャルホストのルーティングがかかっている場合、Hostが違うとまったく別のアプリに行くからです。
4段階目の時間の分解も有用です。
time_appconnect自体が長い → TLSハンドシェイク。証明書チェーンの検証やOCSP照会を疑いますtime_appconnectとtime_starttransferの間が開く → アプリケーション/DB。ネットワークではありません
GCログを読む最低限
[2026-08-19T02:14:33.221+0900][12.334s] GC(41) Pause Full (System.gc()) 486M->402M(512M) 812.443ms
この1行から読み取ること。
Pause Full: Full GCです。頻繁なら問題です486M->402M(512M): 回収前 → 回収後(ヒープ全体)。回収後が全体に近いなら、メモリが実際に不足しています。回収はうまくいっているのに頻繁に動いているのとは、別の問題です812.443ms: この時間の間、アプリケーションが止まります。応答時間が跳ねる原因が、これであることが多いです
判断の基準は、絶対値ではなく傾向とパターンです。
- Full GCのあとも使用量が右肩上がりのまま → メモリリークの疑い → ヒープダンプ
- Full GCはまれなのに、Young GCが非常に頻繁 → 短命なオブジェクトの過剰生成 → コードの問題
- 特定の時刻(バッチの時間)にだけ集中 → そのバッチが犯人
OOMが起きたとき
java.lang.OutOfMemoryErrorは、種類によって対応が完全に異なります。
| メッセージ | 意味 | 最初の対応 |
|---|---|---|
Java heap space |
ヒープ不足またはリーク | ヒープダンプの分析。やみくもに-Xmxを上げても、リークはそのままです |
Metaspace |
クラスメタデータの不足 | 繰り返しのデプロイによるクラスローダーリークを疑います。再起動後に観察します |
GC overhead limit exceeded |
GCに時間の大半を使うのに、ほとんど回収できない状態 | 事実上リークです。ヒープダンプ |
unable to create native thread |
スレッド生成の失敗 | ヒープではなく、OSの制限/スレッドリーク。スレッドダンプ |
最後の行が特に紛らわしいです。OOMなのにヒープの問題ではない場合です。-Xmxを上げると、かえって悪化することがあります(ヒープが大きくなると、スレッド用のメモリが減ります)。
そしてダンプは、発生した瞬間にしか取れません。-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<경로>(プレースホルダーはパスです)を事前に入れておかないと、「再現したらそのときに見ましょう」になり、再現はたいてい起きません。
再起動の前に、証拠を残す
障害対応の原則は、復旧が優先です。ただし、再起動ボタンを押す前の1–2分で、この3つは確保できます。
PID=$(pgrep -f tomcat | head -1)
jcmd $PID Thread.print > /tmp/threads_$(date +%H%M%S).txt
jcmd $PID GC.heap_info > /tmp/heap_$(date +%H%M%S).txt
cp /opt/tomcat/logs/catalina.out /tmp/catalina_$(date +%H%M%S).out
この1–2分を惜しむと、原因を永遠に見つけられません。「とりあえず再起動したら直りました」が3回繰り返されると、4回目には再起動でも直らず、そのときには証拠が何もありません。
ログから実際に抜き出すべきもの
アクセスログから、すぐに抜き出すべき4つです。
- ステータスコードの分布: 5xxがいつから増えたか
- 応答時間の上位URL: どこが遅いか
- アップストリームごとのエラー分布: 特定のサーバーだけが問題か
- 1分あたりのリクエスト数: トラフィックの急増が原因か、結果か
4つ目が重要です。障害のときにリクエストが増えたのが原因の場合もありますが、ユーザーが動かないのでリロードを繰り返して増えた結果であることも多いです。開始時刻を正確に見れば、区別できます。
現場での姿
最も厄介な形は、「サーバーは問題なく動いているのに、特定のユーザーだけ502」という形です。ヘルスチェックの応答はヘッダーが小さいため常に通り、セッションCookieが大きい一部のユーザーだけが、プロキシバッファーを超えて502を受け取ります。このときモニタリングのダッシュボードはすべて緑なので、ユーザーからの問い合わせが唯一のシグナルです。
そして、再起動の前に証拠を残す習慣が必要です。ヒープダンプとスレッドダンプは、再起動した瞬間に消え、同じ障害がまた起きるまで、原因がわからなくなります。