5xxエラーは「インフラからのSOS」だ。そのパケットの悲鳴を読み解く術を伝授しよう
現場でエンジニアをやっていると、深夜2時にPagerDutyが鳴り響く。Slackには「502が急増しています」という殺伐としたアラートが流れる。君たちが遭遇するその5xx系のステータスコードは、単なるWebサーバーの不機嫌さではない。それは、複雑に絡み合ったネットワークとアプリケーションの連携が、どこかで断絶したという「インフラからの切実なSOS」なんだ。
今日は、HTTP/1.1の標準仕様をベースに、現場で叩き上げられた「5xx系トラブルシューティングの解剖学」を共有しよう。
—
1. 500 Internal Server Error:深淵なるブラックボックス
500エラーは最も厄介だ。クライアント(ロードバランサーやブラウザ)から見れば、「サーバーが何かでコケた」としか分からない。
発生要因:
- アプリケーションの未捕捉例外(NullPointerExceptionなど)
- データベース接続プールの枯渇
- 権限設定ミスによるファイル読み込み失敗
実務的対策:
まずはログを見ろ。だが、ログが出力されていないなら、それは「プロセスが起動すらしていない」か「スタックトレースが標準出力から漏れている」可能性が高い。
Python/Flaskでのデバッグ用ハンドリング例
@app.errorhandler(500)
def handle_500(e):
# ログには詳細なトレースバックを吐き出し、ユーザーには抽象的なメッセージを返すのが鉄則
app.logger.error(f”Server Error: {e}”)
return {“error”: “Internal Server Error”}, 500
—
2. 502 Bad Gateway:仲介役の絶望
ロードバランサー(NginxやAWS ALB)がバックエンドサーバー(Gunicorn, Node.js等)に接続を試みたが、不正なレスポンスが返ってきた、あるいは接続が拒否された状態だ。
発生要因:
- バックエンドプロセスがダウンしている(死活監視の盲点)
- 上流からのリクエストを処理しきれず、TCPバックログが溢れている
- ソケット通信のタイムアウト(Upstreamが応答を返さずTCP RSTが返る)
Nginxの設定チェックポイント:
upstream backend_app {
server 127.0.0.1:8000;
keepalive 32; # これを適切に設定しないとTCPコネクションの乱立で502を誘発する
}
location / {
proxy_pass http://backend_app;
# バックエンドが死んだ時、速やかに切り替えるためのタイムアウト設定
proxy_connect_timeout 5s;
proxy_read_timeout 10s;
}
—
3. 503 Service Unavailable:過負荷という名の自衛
「今は忙しいから無理」という、サーバーからの丁重な(あるいは必死な)お断りだ。
発生要因:
- メンテナンスモード
- リクエスト過多によるスレッドプール枯渇
- バックエンドのロードアベレージ高騰による意図的な拒否
デバッグのヒント:
もし君が `curl` で確認するなら、以下のフラグを付けてヘッダーを見てほしい。
`curl -Iv https://your-api.com/`
ここで `Retry-After` ヘッダーが返ってきていないか確認すること。これがあるなら、サーバーは「あと〇〇秒待ってくれ」と言っているんだ。
—
4. 504 Gateway Timeout:終わらない待ち時間
ロードバランサーはバックエンドへリクエストを投げた。しかし、バックエンドが処理を終える前に、ロードバランサー側のタイムアウト制限が先に発動した。
なぜこれが危険か:
504が発生しても、バックエンド側の処理は裏で動いている可能性が高い。二重決済やデータの二重登録を引き起こすトリガーになるからだ。
設計上の解決策:
長時間かかる処理は、HTTPリクエスト内で完結させてはいけない。非同期キュー(RabbitMQやRedis+Celery)を用いたタスク駆動型に変更するのが、シニアエンジニアの流儀だ。
—
トラブルシューティングの鉄則:パケットの視点を持て
最後に、現場で必ずやってほしい「確認フロー」を伝授する。
1. レイヤーを確認せよ: ロードバランサーのログと、バックエンドサーバー(コンテナ)のログを突き合わせろ。「ロードバランサー側で502だが、バックエンドにはアクセスが来ていない」なら、それはネットワーク(Security GroupやFirewall)の遮断だ。
2. コネクションのライフサイクルを見よ:
# 現在の接続状態を確認する
netstat -an | grep 8000 | grep ESTABLISHED | wc -l
もしバックエンドのコネクション数が振り切れているなら、それはアプリの処理速度がボトルネックだ。
3. 再現コードで切り分けろ:
Fetch APIを使って、特定のヘッダーを付与したリクエストを投げ、どのタイミングでエラーになるかを確認する。
// Fetch APIでの簡易検証コード
async function checkStatus() {
const res = await fetch(‘https://api.example.com/data’, {
method: ‘GET’,
headers: { ‘X-Debug-Mode’: ‘true’ } // インフラ側でログを拾いやすくするためのカスタムヘッダー
});
console.log(`Status: ${res.status}`);
}
5xxエラーは、システムが限界に達したという「進化のサイン」でもある。怖がることはない。ログという名のパケットの足跡を追い、ボトルネックを特定し、構造を最適化する。それこそが、我々インフラエンジニアの醍醐味なんだ。
さあ、モニターの向こう側の「真実」を見つけに行こう。何か行き詰まったら、いつでもまた聞きに来るといい。
コメント