Nginx 504 Gateway Timeout とクライアントタイムアウト問題の解決策

恐れられている504 Gateway Timeoutを含むNginxのタイムアウトを克服するため、重要なプロキシディレクティブの調整方法を学びましょう。このガイドでは、堅牢な接続処理を実現するために、`proxy_read_timeout` の増加、バッファリングの最適化、そしてNginxとアップストリームサーバー間の通信障害を診断するためのエラーログの活用方法を詳細に解説します。

61 ビュー

Nginx 504 Gateway Timeoutおよびクライアントタイムアウト問題の解決

Nginxはその高いパフォーマンスと安定性で知られていますが、通信の断線を示すHTTPステータスコード、特に504 Gateway Timeoutや様々なクライアントサイドのタイムアウトといった、しばしばイライラするエラーが発生することがあります。これらの問題は、Nginxがバックエンドサービス(アプリケーションサーバーや他のプロキシなど)からの応答を待つ時間と、クライアント(ブラウザやアップストリームサービス)がNginx自体を待つ意思のある時間との間の不一致からほぼ常に発生します。

この包括的なガイドでは、これらのタイムアウトの根本原因を診断し、504エラーを解決して全体的な接続安定性を向上させるための具体的な設定調整を提供します。これらのメカニズムを理解することは、特にマイクロサービスアーキテクチャや応答の遅いアップストリームアプリケーションを扱う際に、高可用性を維持するために不可欠です。


504 Gateway Timeoutエラーの理解

504 Gateway Timeoutエラーは、Nginxがリバースプロキシまたはゲートウェイとして機能する際に、リクエストを転送しているアップストリームサーバーからタイムリーな応答を受け取れない場合に発生します。簡単に言うと、Nginxはバックエンドに応答を求め、設定された時間待機しましたが、応答がなかったため断念したということです。

これは、502 Bad Gateway(アップストリームからの無効な応答を意味する)や503 Service Unavailable(アップストリームが過負荷または利用不可であることを意味する)とは異なります。

アップタイムアウトを制御する主要なディレクティブ

リクエストをプロキシする場合、Nginxは主にhttpserver、またはlocationブロック内、あるいは具体的にはupstreamブロック内で、いくつかの重要なディレクティブを使用します。これらの値を調整することが、504エラーを解決する主要な方法です。

1. proxy_connect_timeout

これは、アップストリームサーバーとの接続を確立するためのタイムアウトを設定します。Nginxがこの期間内に接続できない場合、タイムアウトエラーを返します。

デフォルト: 60秒

proxy_connect_timeout 60s;

2. proxy_send_timeout

これは、アップストリームサーバーへの2つの連続する書き込み操作間の時間を設定するタイムアウトです。これは、大きなリクエストボディを送信する場合に関連します。

デフォルト: 60秒

proxy_send_timeout 60s;

3. proxy_read_timeout(504の最も一般的な修正策)

これは、リクエストヘッダーが送信された後、アップストリームサーバーからの応答を待つためのタイムアウトを設定します。バックエンドアプリケーションがリクエストを処理して応答ボディを生成するのに時間がかかりすぎる場合、このディレクティブを増やす必要があります。

デフォルト: 60秒

# 例:遅いAPIのために読み取りタイムアウトを120秒に増やす
proxy_read_timeout 120s;

ベストプラクティス: アプリケーションがデフォルトの60秒を頻繁に超える場合は、この値を慎重に増やしてください。非常に高い値は、根本的なバックエンドパフォーマンスの問題を隠してしまう可能性があります。


クライアントサイドのタイムアウトへの対処

504はNginxとバックエンド間の通信に関連しますが、クライアントサイドのタイムアウトは、クライアント(例:ブラウザ、モバイルアプリ、またはNginxにリクエストを行っている別のサービス)がNginxがバックエンドとの通信を完了する前に待機をあきらめる場合に発生します。

Nginxが504をログに記録する前にクライアントタイムアウトが発生している場合は、クライアントとNginx間の接続を確認する必要があります。

1. クライアントサイドのキープアライブ

クライアントが接続を早期に切断した場合、Nginxはエラーを受け取るか、クライアントはデータを待つ間に単にタイムアウトする可能性があります。

クライアント側の接続設定(設定可能であれば)が攻撃的すぎないことを確認してください。クライアントが別のプロキシまたはロードバランサーである場合は、Nginxのsend_timeoutに対してそのタイムアウト設定を確認してください。

2. Nginx send_timeout

このディレクティブは、Nginxがクライアントからのデータ応答を待つ時間(クライアントへの2つの連続する書き込み操作間の時間)を制御します。

デフォルト: 60秒

# Nginxが応答を送信中にクライアントがタイムアウトする場合に設定します
send_timeout 120s;

大容量応答のバッファリングの最適化

タイムアウトは、処理に時間がかかりすぎたためではなく、Nginxがアップストリーム応答のバッファリングを開始し、接続がタイムアウトする前にバッファ書き込みを完了できなかったために発生することがあります。これは、特に大きな応答を扱う場合に重要です。

Nginxはバッファを使用して、アップストリームから受信したデータをクライアントに送信する前に一時的に保持します。応答が非常に大きい場合、これらのバッファは超過し、複雑な処理や知覚される遅延につながる可能性があります。

主要なバッファリングディレクティブ

これらは通常、locationブロックまたはserverブロック内に設定されます。

ディレクティブ 目的
proxy_buffers アップストリームからの応答ヘッダーを読み取るために使用されるバッファの数とサイズを設定します。形式:number size;
proxy_buffer_size 応答ヘッダーを読み取るために使用される最初のバッファのサイズを設定します。
proxy_max_temp_file_size 応答が利用可能なバッファを超えた場合、Nginxは一時ファイルに書き込みます。これは、これらの一時ファイルの最大サイズを設定します。

高トラフィック/大容量応答の例設定:

location /api/heavy_report {
    proxy_pass http://backend_app;

    # 読み取りタイムアウトを増やす
    proxy_read_timeout 180s;

    # 潜在的に大きな応答ボディのバッファリングを調整する
    # 8つのバッファを使用、各最大1MB (1024k)
    proxy_buffers 8 1024k;
    proxy_buffer_size 256k;

    # バッファがオーバーフローした場合、最大500MBの一時ファイルを許可する
    proxy_max_temp_file_size 500m;
}

バッファリングに関するヒント: バックエンド応答が実際に非常に大きい(例:数GB)場合、静的コンテンツを提供するか、ストリーミングを直接実装することを検討してください。非常に大きな応答のバッファリングは、Nginxサーバーでかなりのメモリを消費する可能性があります。


トラブルシューティング手順とログ分析

タイムアウトを解決するには、停止が発生した場所を特定する必要があります:クライアント -> Nginx、またはNginx -> バックエンド。

ステップ1:Nginxエラーログを確認する

Nginxエラーログは、Nginxがバックエンドを待ってタイムアウトしたかどうかを判断するための決定的な情報源です。

次のようなフレーズを含むエントリを探してください。

  • upstream timed out (110: Connection timed out)
  • upstream prematurely closed connection while reading response header from upstream

これらが見つかった場合、問題はproxy_read_timeoutまたはバックエンドの処理時間に関連しています。

ステップ2:バックエンドアプリケーションログを確認する

Nginxがタイムアウトした場合(ログに504と表示される)、アップストリームサービスのログ(例:PHP-FPMログ、Gunicornログ、Javaアプリケーションサーバーログ)をすぐに確認してください。リクエストがバックエンドに到達したかどうか、および完了するのにかかった時間を確認する必要があります。

  • バックエンドログでリクエストが設定したproxy_read_timeoutよりも長くかかったと表示されている場合、Nginxのタイムアウトを増やしてください。
  • バックエンドログでリクエストが迅速に完了したと表示されている場合、問題はNginxとバックエンド間のネットワーク遅延、またはNginxに直面している misconfigured client timeout である可能性があります。

ステップ3:X-Upstream-Response-Timeヘッダーを使用する(オプション)

詳細な診断のために、アクセスログフォーマットで$upstream_response_time変数を使用して、アップストリームが応答した正確な時間をログに記録できます。これにより、バックエンドの実際のパフォーマンスを確認できます。

nginx.confで:

log_format proxy_detailed '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" $request_time $upstream_response_time';

access_log /var/log/nginx/access.log proxy_detailed;

$upstream_response_timeを分析することで、Nginx自体のタイムアウト設定とは無関係に、Nginxが待機した正確な期間を確認できます。


まとめと変更の適用

Nginxのタイムアウト問題を解決するには、一般的にクライアントの期待とバックエンドの処理能力の間のバランスを取ることが含まれます。関係を覚えておいてください。

  • 504 Timeout: バックエンドが遅すぎるか、Nginxが待機中にネットワークリンクが失敗しました(proxy_read_timeout)。
  • Client Timeout: クライアントがNginxを待つのをあきらめました(send_timeoutまたはクライアント設定)。

設定変更(例:タイムアウトの増加やバッファサイズの調整)を行った後は、常に設定構文をテストし、Nginxをリロードしてください。

sudo nginx -t
sudo systemctl reload nginx

修正を適用した後は、ログを慎重に監視してください。タイムアウトを盲目的に増加させると、設定による回避策ではなく最適化を必要とする根本的なシステムパフォーマンスのボトルネックを隠してしまう可能性があります。