「504 Gateway Timeout」が急に増え、レスポンスやログに「upstream request timeout」。この瞬間に厄介なのは、原因がアプリの遅延だけとは限らず、ロードバランサー、Ingress、Nginx、Envoy、サービスメッシュなど“途中の中継”のどこでも起こり得る点です。闇雲にタイムアウトを延長すると、待ちリクエストが積み上がって障害を拡大させることもあります。
本記事では、まず最初の15分で「どの層が504を返しているか」を確定し、connect/read/routeといった待ち時間の種類を分解して、原因を最短で絞り込みます。さらに、Nginx・Envoy・GKE internal Ingressなど環境別に、確認すべきログと設定の対応関係を整理し、暫定復旧の判断基準から再発防止の観測テンプレまで、次回も迷わない形でまとめます。
※本コンテンツは「記事制作ポリシー」に基づき、正確かつ信頼性の高い情報提供を心がけております。万が一、内容に誤りや誤解を招く表現がございましたら、お手数ですが「お問い合わせ」よりご一報ください。速やかに確認・修正いたします。
- 1 upstream request timeoutで何が起きているのか
- 2 upstream request timeoutの切り分けを最短で終える手順
- 3 upstream request timeoutをNginxで疑うときの確認点と設定対応
- 4 upstream request timeoutをEnvoyやIstioで疑うときの確認点と設定対応
- 5 upstream request timeoutがGKE IngressやKubernetesで出るときの見方
- 6 upstream request timeoutの対処法:暫定復旧と恒久対策を分ける
- 7 upstream request timeoutのよくある質問
- 8 参考にした情報源
upstream request timeoutで何が起きているのか
まず押さえるべき結論は「中継が待ち切れなかった」
upstream request timeoutは、多くの場合「クライアントが遅い」ではなく、クライアントとアプリの間にいる“中継(ロードバランサー、プロキシ、Ingress、サービスメッシュなど)”が、上流(upstream)の応答を期限内に受け取れず、処理を打ち切ったことを示すサインです。
この結果として、HTTPの世界では504 Gateway Timeoutとして表に出ることがよくあります。504は「ゲートウェイ/プロキシが上流サーバーから期限内に有効な応答を受け取れなかった」状態を指します。
ここで重要なのは、upstream request timeoutという文言が出た瞬間に「アプリが遅い」と決めつけないことです。中継が複数段ある現代の構成では、どの中継がタイムアウトしたのかで原因も対策も変わります。
15分でやるべきことは「どの層が504を返したか」の確定
障害対応中に最も価値があるのは、詳細な理屈よりも「次に何を見ればよいか」が一瞬で分かることです。まずは以下の問いに答えるところから始めます。
-
504(またはupstream request timeout)を返しているのは、どの装置/プロセスか
例:Cloud Load Balancerなのか、Nginxなのか、Envoyなのか、Ingressなのか -
それは「上流に接続できない」のか「接続はできるが返ってこない」のか
-
特定のエンドポイントだけか、全体か。デプロイ直後だけか、常時か
この確定ができれば、調査は迷走しません。
upstream request timeoutの切り分けを最短で終える手順
最初の15分チェックリスト(緊急対応テンプレ)
以下は、現場で“最初に見るべき順番”を固定するためのテンプレです。障害対応のたびに順番が揺れると、判断が遅れます。
-
ユーザー影響の把握
-
影響する機能/APIはどれか(例:/api/orders だけ、全APIなど)
-
発生開始時刻はいつか(デプロイ/設定変更と相関があるか)
-
-
504を返している主体の特定
-
ブラウザのレスポンスヘッダ、LBのアクセスログ、プロキシのアクセスログを確認
-
可能ならレスポンス本文(例:upstream request timeout)も採取
-
-
ログの“同一リクエスト”突合
-
request-id / trace-id / x-request-id などがあるなら、それで追跡
-
ない場合は「時刻+URL+クライアントIP」で近似
-
-
上流到達性の確認(接続問題の除外)
-
DNS、ルーティング、セキュリティ(FW/SG/NetworkPolicy)
-
上流Pod/VMが生存しているか(readyか、リスタートしていないか)
-
-
遅延の正体を“分解”
-
connectが遅いのか、応答開始が遅いのか、途中が止まるのか
-
-
暫定復旧の判断
-
タイムアウト延長で止血できるか(悪化兆候がないか)
-
それとも流量制御/リトライ抑制/ロールバックが先か
-
「どこで遅いか」を分解する:connect / send / read / route
タイムアウトは一括りにされがちですが、実際には「何を待っているか」が異なります。ここを分解できると、原因が急速に絞れます。
-
connect:上流へTCP接続を張るまで
典型原因:DNS不調、ルーティング、セキュリティ、上流枯渇、SYN再送 -
send:上流へリクエストを送る間
典型原因:大きいボディ、帯域不足、バックプレッシャー -
read:上流からレスポンスを受信する間(無音時間含む)
典型原因:アプリ処理遅延、DB待ち、外部API待ち、ストリーミング中断 -
route(Envoy等):下流リクエストを受け取り終えてから、上流の完全な応答を得るまで
典型原因:上流処理が既定値より長い、あるいは上流が返せない
特にEnvoyのroute timeoutは、「下流リクエストを最後まで受け取ってから」タイマーが始まる点が重要です。アップロード中に切れる問題と、アップロード後の処理遅延は切り分け方が違います。
症状→疑う層→次アクション
以下の表は、障害時に“読む順番”が揺れないようにするためのものです。
| 層 | よくある見え方 | まず疑うこと | 次アクション(最短) |
|---|---|---|---|
| クライアント | 画面が固まる/通信切断 | クライアント側タイムアウト | クライアント設定値と実測レイテンシ確認 |
| 外側LB/CDN | 504がLBから返る | 上流到達性、LB側タイムアウト | LBログで上流エラー種別/レイテンシ確認 |
| Ingress/Proxy | upstream request timeout/504 | connectかreadか、設定既定値 | 直下のプロキシログ(upstream_response_time等)確認 |
| Service Mesh | EnvoyでUT/504 | route timeout、上流準備不足 | Envoyアクセスログの応答フラグ確認、ルート設定確認 |
| アプリ | 遅い/スレッド枯渇 | ロック/外部API/DB | APM、スロークエリ、スレッドプール、キュー長 |
| DB/外部API | スロークエリ/接続遅延 | インデックス、負荷、レート制限 | スローログ、待機イベント、依存先SLA確認 |
upstream request timeoutをNginxで疑うときの確認点と設定対応
Nginxで最初に見るログ項目(例)
Nginxをリバースプロキシとして使っている場合、設定を触る前に「ログが何を語っているか」を見ます。代表的には以下のような情報が役に立ちます(カスタムログフォーマットに依存します)。
-
ステータス(504/499/502など)
-
request_time(全体にかかった時間)
-
upstream_connect_time(接続にかかった時間)
-
upstream_header_time(ヘッダ受信まで)
-
upstream_response_time(応答全体)
-
upstream_addr(どの上流に当たったか)
これらが取れていない場合は、恒久対策として「次回の障害対応を早める」意味でも、ログを整備する価値があります。
proxy_connect_timeout / proxy_send_timeout / proxy_read_timeoutの役割
Nginxの代表的タイムアウトは、待っている対象が異なります。特にproxy_read_timeoutは誤解されやすいため、connectと分けて理解することが重要です。
| ディレクティブ | 何を待つか | ありがちな誤解 | 症状の典型 |
|---|---|---|---|
| proxy_connect_timeout | 上流へ接続が確立するまで | 「アプリが遅い」と混同 | 上流へ到達できず短時間で失敗 |
| proxy_send_timeout | 上流へリクエスト送信中 | readと混同 | 大きいボディ送信で詰まる |
| proxy_read_timeout | 上流からの受信が一定時間進まない | connectと混同 | 上流が返さない/途中で止まる |
| send_timeout(参考) | クライアントへ送る送信 | upstreamとは別 | 下流送信が遅い |
ここでのポイントは、connectが遅いならネットワーク/到達性、readが遅いなら上流処理/DB/外部APIと、疑う方向が変わる点です。
「延長で止血」する前に確認すべき危険サイン
タイムアウトを伸ばすと、一見“治った”ように見えます。しかし、上流が詰まっている状態で延長すると、待ちリクエストが増えてワーカー/コネクションを占有し、障害が拡大することがあります。延長前に、次の危険サインを確認してください。
-
直近で同時接続数やワーカー使用率が張り付いている
-
キュー長(アプリ側のリクエスト待ち、ジョブ待ち)が増え続けている
-
依存先(外部API/DB)の遅延が同時刻に悪化している
-
リトライが多く、同じリクエストが重複していそう
-
504が出る前からp95/p99レイテンシが急上昇している
この場合、延長より先に「流量制御」「リトライ抑制」「ロールバック」「依存先の隔離(サーキットブレーカー)」の方が安全なことがあります。
upstream request timeoutをEnvoyやIstioで疑うときの確認点と設定対応
Envoyのroute timeoutは「上流の完全応答」を待つ(開始条件が重要)
Envoy公式の説明として、route timeoutは「Envoyが上流から完全なレスポンスを受け取るまで待つ時間」であり、さらに「下流のリクエストストリームを全て受信してから」タイムアウトが開始する、とされています。
この仕様は、次のような誤解を防ぎます。
-
誤解1:アップロード中に切れる → route timeoutが原因
実際には、アップロード中は別のタイムアウトや下流側制限が疑わしい -
誤解2:処理が遅いだけ → とりあえず60秒に伸ばす
実際には、上流の詰まりが悪化するリスクがある
Envoyアクセスログの「応答フラグ(UTなど)」を読み解く
Envoy系では、ログに短いフラグが出ることがあります。例えば、UTは「Upstream request timeout(504とセット)」として説明されることがあります(環境やログ設定で表示は異なります)。
ログにフラグが出る場合は、次の整理が有効です。
-
UT:上流応答を待ち切れなかった(route/stream系のタイムアウトが絡む可能性)
-
上流接続エラー系:到達性、コネクションプール、上流枯渇
-
リセット系:上流が途中で切った、あるいはメッシュが切った
Istio環境では「どこでタイムアウトを設定しているか」を固定する
IstioはEnvoyをデータプレーンとして使いますが、タイムアウトは複数箇所で設定され得ます(アプリ、IngressGateway、VirtualService、DestinationRule、クライアント側など)。
そのため、まず次を固定します。
-
タイムアウトを管理する“正”はどこか(例:VirtualServiceのtimeoutを基準)
-
IngressGatewayとサイドカーの双方に設定があるか
-
クライアント側(SDK/ブラウザ)のタイムアウトが先に切っていないか
公式タスクでも、ルートにtimeoutを設定して挙動を確認する流れが示されています。
リトライとタイムアウトを同時に増やすと「詰まり」が増幅する
障害時にやりがちな組み合わせが、タイムアウト延長+リトライ増加です。
リトライは「一時的な失敗」には効きますが、上流が遅延している状況では、同じ処理が重複して負荷を押し上げ、さらに遅延が増える悪循環になり得ます。
-
上流が遅い(処理能力不足)のにリトライ → さらに上流が詰まる
-
冪等でないAPIにリトライ → 二重処理/二重課金の事故
したがって、恒久対策としては「タイムアウトをどこまで許容するか」だけでなく、「リトライ対象を何に限定するか」「冪等性をどう担保するか」までセットで設計するのが安全です。
upstream request timeoutがGKE IngressやKubernetesで出るときの見方
GKE internal Ingressでは“Envoyが中継する”ことがある
GKEの内部Ingress(内部Application Load Balancer)で、レスポンスとして「504 Gateway Timeout: upstream request timeout」が現れるケースがあり、公式ドキュメントでは、その理由としてinternal Application Load Balancer宛のトラフィックがproxy-only subnet範囲のEnvoyプロキシで中継されることが説明されています。
つまり、ここでのupstream request timeoutは、アプリのログではなく中継(Envoy)側のタイムアウトである可能性が高く、観測点を誤ると迷走します。
この場合の最短手順は次の通りです。
-
internal Ingress経路かどうか確認(どのLB/Ingressか)
-
その経路がEnvoy中継になる条件(proxy-only subnet等)を確認
-
Envoy相当のログ/メトリクス(あるいはLB側ログ)でtimeoutを確認
-
上流サービス(Pod/NEG等)の準備不足・到達性・遅延を切り分け
Kubernetes特有の「準備不足」パターン(デプロイ直後だけ増える)
Kubernetesでは、Podが生存していても“すぐ応答できる”とは限りません。特に以下は頻出です。
-
readinessが通っていないのに流入が始まっている(設定/運用不整合)
-
startupに重い初期化(マイグレーション、キャッシュ構築、外部APIへの同期)をしている
-
ローリング更新で同時に落ちる割合が高く、瞬間的にキャパが不足する
症状が「デプロイ直後だけ」「新しいPodだけ」に偏るなら、タイムアウト延長より先に**起動シーケンスの整備(startup/readinessの設計)**を疑う方が恒久的です。
依存先がボトルネックの場合、Ingress設定だけでは解決しない
upstream request timeoutは中継で起きますが、根本は上流(アプリ/DB/外部API)にあります。以下のような状況は、Ingressの設定をいじっても解消しません。
-
DBのスロークエリ(インデックス不足、ロック競合、接続枯渇)
-
外部APIのレート制限や遅延(再試行が逆効果)
-
アプリのスレッドプール枯渇(待ち行列が増える)
この場合は、まず“遅延の正体”をAPMやスローログで掴み、処理を短くする方向へ進むべきです。
upstream request timeoutの対処法:暫定復旧と恒久対策を分ける
暫定復旧で選べる手段(危険度順)
暫定復旧は「ユーザー影響を止める」ことが目的です。ただし、手段には危険度があります。ここでは一般的な傾向として、危険度が上がるほど副作用が増える前提で整理します。
-
ロールバック(直前の変更が原因の可能性が高い場合は最速)
-
流量制御(レート制限、機能フラグで重い処理を止める)
-
依存先の隔離(外部API呼び出しを止め、代替応答を返す)
-
リトライ抑制(クライアント/プロキシ/メッシュのリトライを限定)
-
タイムアウト延長(症状が遅延のみで、待ちが増えても破綻しない場合)
タイムアウト延長は最後のカードになりがちです。延長するなら、対象を絞り、上限を決め、監視を同時に強化することが重要です。
暫定対応か恒久対策か:判断チェック表
次の表で「延長して良い状況」と「延長すると悪化しやすい状況」を整理します。
| 観測 | 状況 | 推奨 |
|---|---|---|
| p95/p99が少し伸びた程度 | 上流は捌けている | ルート限定で小幅延長は検討可 |
| 依存先遅延が一時的 | 外部APIの瞬間遅延 | リトライ条件最適化+短期延長は可 |
| ワーカー/接続が張り付き | 待ちが溜まっている | 延長は悪化しやすい(流量制御優先) |
| キュー長が増加し続ける | 処理能力不足 | 延長より性能改善/スケール/設計変更 |
| 冪等でない処理が多い | 二重実行リスク | リトライ抑制+冪等性設計が先 |
恒久対策の基本は「速く返す」か「待たせない設計」
恒久対策は、タイムアウトを“伸ばす”ではなく、タイムアウトが問題になりにくい構造に寄せるのが本質です。
-
速く返す(性能改善)
-
DB:インデックス最適化、クエリ改善、ロック競合の解消、接続プールの見直し
-
アプリ:CPU/IOのボトルネック解消、スレッドプール調整、N+1の解消
-
外部API:キャッシュ、並列化、サーキットブレーカー
-
-
待たせない(設計変更)
-
長時間処理は非同期化(ジョブ投入→受付応答→結果取得)
-
処理を分割(ページング、段階的計算、バッチ化)
-
失敗しても影響を小さく(フォールバック、縮退運転)
-
観測の型(再発防止のためのテンプレ)
再発防止で効くのは「次回の調査を速くする」ことです。以下は最低限揃える観測の型です。
ログ(相関のため)
-
入口(LB/Ingress/Proxy)にrequest-idを付与し、下流へ伝播
-
アプリログにもrequest-idを出す
-
可能ならDBクエリログにも関連ID(難しければ時刻相関で代替)
メトリクス(兆候のため)
-
p50/p95/p99レイテンシ(入口とアプリ双方)
-
5xx率(504/502/503の内訳)
-
同時接続数、ワーカー使用率、キュー長
-
依存先のレイテンシとエラー率(DB/外部API)
トレース(根本原因のため)
-
スパンで「どこで待ったか」を可視化(DB、外部API、ロック待ちなど)
この“型”があると、次回は「ログがないから分からない」という状態を避けられます。
upstream request timeoutのよくある質問
どの値をどれだけ伸ばせばよいですか
まず、対象ルートの実測(p95/p99)を見てください。例えばp99が12秒で、既定タイムアウトが10秒なら、切れて当然です。
ただし、延長は「待ちが増えても破綻しない」ことが前提です。ワーカーや接続が張り付いている状態では、延長は障害を拡大させます。
Envoyではroute timeoutが「下流リクエストを受け取り終えてから」始まる点も踏まえ、アップロードと処理遅延を混同しないようにしてください。
特定の経路だけ起きるのはなぜですか
特定経路だけなら、原因はかなり絞れます。
-
その経路だけDBが重い(スロークエリ、ロック)
-
その経路だけ外部API依存がある
-
その経路だけレスポンスが大きい/計算が重い
-
その経路だけ別クラスタ/別リージョンへルーティングしている
-
その経路だけメッシュ設定(timeout/retry)が異なる
まず「他の経路と何が違うか」を表にし、差分から当たりを付けるのが最短です。
504が出てもバックエンドは処理を続けますか
状況によります。中継がタイムアウトしても、バックエンド側でキャンセルが伝播しない場合、処理が継続することがあります。
このとき、クライアントが再試行すると二重実行になり得ます。特に冪等でない処理(課金、注文確定、在庫引当など)は要注意です。恒久対策として、冪等性キー、重複排除、ジョブ化などを検討してください。
GKE internal Ingressで「upstream request timeout」が出たらアプリが原因ですか
アプリが遅いケースもありますが、まずは「その経路がEnvoyで中継される」ことを前提に観測点を合わせる方が近道です。GKEの公式ドキュメントでは、internal Application Load Balancer宛トラフィックがEnvoyでプロキシされるためこのエラーが出る場合がある、と説明されています。
したがって、アプリログだけを見て「何も出ていない」と困るより、LB/Ingress/中継側ログを先に確認してください。
Nginxのproxy_read_timeoutを伸ばしたら直りました。これで終わりですか
短期的には止血になりますが、根本が解決したとは限りません。上流処理が遅いままだと、待ちが増え、別の障害(ワーカー枯渇、接続枯渇、キュー増大)が出る可能性があります。
「なぜ遅いのか」をAPM/スローログで掴み、処理を短くするか、待たせない設計へ寄せるのが安全です。Nginxのタイムアウトは公式ドキュメントの通り“何を待つか”が分かれているため、connectとreadを混同しないで観測してください。
参考にした情報源
-
Google Cloud:Troubleshoot load balancing in GKE
https://docs.cloud.google.com/kubernetes-engine/docs/troubleshooting/load-balancing -
Google Cloud:Ingress configuration(504 Gateway Timeout: upstream request timeoutの記載)
https://docs.cloud.google.com/kubernetes-engine/docs/how-to/ingress-configuration -
Envoy:How do I configure timeouts?(route timeoutの定義と開始条件)
https://www.envoyproxy.io/docs/envoy/latest/faq/configuration/timeouts -
NGINX:Module ngx_http_proxy_module(proxy系ディレクティブ)
https://nginx.org/en/docs/http/ngx_http_proxy_module.html -
Istio:Request Timeouts(Istioでのタイムアウト設定タスク)
https://istio.io/latest/docs/tasks/traffic-management/request-timeouts/ -
Carpe Diem:IstioやEnvoyで発生するネットワーク系エラー(UT等の整理)
https://christina04.hatenablog.com/entry/istio-and-envoy-errors -
Stack Overflow:Ingressgateway returns a status code different from Pod(UTの説明例)
https://stackoverflow.com/questions/54323340/ingressgateway-returns-a-status-code-different-from-pod