誰もそれを見ない場合、5xxは間違いですか? [1]
ソフトウェアを起動する前にどれだけ長く慎重に確認しても、問題の一部は作業環境でのみ発生します。 たとえば、多数のクライアントに同時にサービスを提供することによる競合状態や、ユーザーからの予期しないデータを検証する際の例外などです。 その結果、これらの問題は5xxエラーにつながる可能性があります。
HTTP 5xx応答は多くの場合ユーザーに返され、短期間であっても会社の評判を損なう可能性があります。 同時に、実稼働サーバーでの問題のデバッグは非常に難しいことがよくあります。 ログから問題のセッションを簡単に抽出するだけでも、干し草の山の針を検索することになります。 また、すべての稼働中のサーバーから必要なログをすべて収集しても、問題の原因を理解するには不十分な場合があります。
検索とデバッグのプロセスを容易にするために、NGINXを使用してアプリケーションをプロキシまたはバランスするときに、いくつかの便利なトリックを使用できます。 この記事では、
error_page
経由のプロキシを使用した一般的なアプリケーションインフラストラクチャに適用される
error_page
ディレクティブの特別な使用について説明します。
デバッグサーバー
デバッグサーバー(デバッグサーバー、デバッグサーバー)-要求がリダイレクトされる特別なサーバーで、運用サーバーでエラーが発生します。 これは、NGINXがアップストリームから返された5xxエラーを検出し、異なるアップストリームグループからのエラーを引き起こすリクエストをデバッグサーバーにリダイレクトできるという事実を利用することで実現されます。 また、デバッグサーバーはエラーにつながるリクエストのみを処理するため、ログにはエラーのみに関連する情報が含まれます。 したがって、干し草の山で針を見つける問題は、ほんの一握りの針に帰着します。
デバッグサーバーは動作中のクライアント要求を処理しないため、パフォーマンスのためにデバッグサーバーをシャープにする必要はありません。 代わりに、最大ログを有効にして、あらゆる好みに診断ツールを追加できます。 例:
- デバッグモードでのアプリケーションの実行
- サーバーで詳細ログを有効にする
- アプリケーションプロファイリングの追加
- サーバーが使用するリソースのカウント
デバッグツールは通常、アプリケーションの速度を低下させるため、運用サーバーでは通常無効になっています。 ただし、デバッグサーバーに対して安全に有効にすることができます。 以下は、デバッグサーバーを備えたアプリケーションインフラストラクチャの例です。
理想的な世界では、デバッグサーバーのリソースの構成と割り当てのプロセスは、通常の運用サーバーのセットアップのプロセスと異なるべきではありません。 ただし、デバッグサーバーを仮想マシンの形式で作成する場合、これには利点があります(たとえば、オフライン分析用の複製とコピー)。 ただし、この場合、5xxエラーの突然のバーストを引き起こす重大な問題が発生した場合、サーバーが過負荷になるリスクがあります。 NGINX Plusでは、 max_connsパラメーターを使用して並列接続の数を制限することでこれを回避できます(構成例を以下に示します)。
デバッグサーバーは本番サーバーのようにロードされないため、すべての5xxエラーを再現できるわけではありません。 このような状況では、アプリケーションのスケーリング制限に達し、リソースが不足しており、アプリケーション自体にエラーはないと想定できます。 主な理由に関係なく、デバッグサーバーを使用すると、ユーザーとの対話を改善し、5xxエラーを防ぐことができます。
構成
以下は、メインサーバーの1つで5xxエラーを引き起こすリクエストを受信するための簡単なデバッグサーバー構成の例です。
upstream app_server { server 172.16.0.1; server 172.16.0.2; server 172.16.0.3; } upstream debug_server { server 172.16.0.9 max_conns=20; } server { listen *:80; location / { proxy_pass http://app_server; proxy_intercept_errors on; error_page 500 503 504 @debug; } location @debug { proxy_pass http://debug_server; access_log /var/log/nginx/access_debug_server.log detailed; error_log /var/log/nginx/error_debug_server.log; } }
アップストリームの
app_server
ブロックには、
app_server
サーバーのアドレスが含まれています。 以下は、
upstream debug_server
単一のデバッグサーバーアドレス
upstream debug_server
。
最初のロケーションブロックは、 proxy_passディレクティブを使用して単純なプロキシをセットアップし、
upstream
app_server
アプリケーションサーバーのバランスを取ります(例ではバランスアルゴリズムを指定していないため、標準のラウンドロビンアルゴリズムが使用されます)。 含まれているproxy_intercept_errorsディレクティブは、300以上のHTTPステータスを持つ応答がerror_pageディレクティブを使用して処理されることを意味します。 この例では、
@debug
、および504のエラーのみが
@debug
され、
location
@debug
ブロックに渡されて処理されます。 404などの他のすべての応答は、変更されずにユーザーに送信されます。
@debug
ブロックでは2つのアクションが発生します
@debug
つ目は、
upstream
debug_server
グループへのプロキシです。これには、もちろんデバッグサーバーが含まれます。 次に、access_logとerror_logを別々のファイルに書き込みます。 実稼働サーバーへの誤った要求によって生成されたメッセージを分離することにより、デバッグサーバー自体で生成されたエラーとそれらを簡単に関連付けることができます。
access_logディレクティブは、 詳細な個別のロギング形式を参照することに注意してください。 この形式は、
http
レベルでlog_formatディレクティブに次の値を指定することで定義できます。
log_format detailed '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent "$http_referer" ' '"$http_user_agent" $request_length $request_time ' '$upstream_response_length $upstream_response_time ' '$upstream_status';
詳細形式は、デバッグサーバーへの要求とその応答に関する追加情報を提供する5つの変数を追加することにより、デフォルトの結合形式を拡張します。
-
$request_length
ヘッダーと本文を含むリクエストの合計サイズ(バイト単位) -
$request_time
ミリ秒単位のリクエスト処理時間 -
$upstream_response_length
デバッグサーバーから受信した応答の長さ(バイト単位) -
$upstream_response_time
デバッグサーバーから応答を受信するのにかかった時間(ミリ秒) -
$upstream_status
デバッグサーバーからの応答ステータスコード
ログの上記の追加フィールドは、誤ったクエリや実行時間が長いクエリを検出するのに非常に役立ちます。 後者は、アプリケーションの不正なタイムアウトまたは他のプロセス間通信の問題を示している可能性があります。
リクエストをリダイレクトするときのdem等性
おそらく、場合によっては、デバッグサーバーへの要求のリダイレクトを避けたいと思うでしょう。 たとえば、データベース内の複数のレコードを変更しようとしているときにアプリケーションでエラーが発生した場合、新しい要求がデータベースへの呼び出しを繰り返し、再度変更を加えることがあります。 これにより、データベースが混乱する可能性があります。
したがって、リクエストがべき等である場合にのみリクエストを再送信しても安全です。つまり、再送信されたときに結果が同じになるリクエストです。 HTTPの
GET
、
PUT
、および
DELETE
メソッドはi等ですが、
POST
はそうではありません。 ただし、HTTPメソッドのべき等性はアプリケーションの実装に依存し、正式に定義されたものとは異なる可能性があることに注意してください。
デバッグサーバーでべき等性を処理するには、次の3つのオプションがあります。
- データベースの読み取り専用モードでデバッグサーバーを実行します。 この場合、リクエストを再送信しても変更は行われないため、安全です。 デバッグサーバーでのログリクエストは変更されませんが、問題を診断するために利用できる情報は少なくなります(読み取り専用モードのため)。
- べき等の要求のみがデバッグサーバーに転送されます。
- データベースに対して2番目のデバッグサーバーを読み取り専用モードで展開し、非べき等の要求をデータベースに転送します。一方、べき等の要求はメインサーバーにデバッグを送信し続けます。 この場合、すべての要求が処理されますが、追加の構成が必要になります。
完全を期すために、3番目のオプションの構成を検討してください。
upstream app_server { server 172.16.0.1; server 172.16.0.2; server 172.16.0.3; } upstream debug_server { server 172.16.0.9 max_conns=20; } upstream readonly_server { server 172.16.0.10 max_conns=20; } map $request_method $debug_location { 'POST' @readonly; 'LOCK' @readonly; 'PATCH' @readonly; default @debug; } server { listen *:80; location / { proxy_pass http://app_server; proxy_intercept_errors on; error_page 500 503 504 $debug_location; } location @debug { proxy_pass http://debug_server; access_log /var/log/nginx/access_debug_server.log detailed; error_log /var/log/nginx/error_debug_server.log; } location @readonly { proxy_pass http://readonly_server; access_log /var/log/nginx/access_readonly_server.log detailed; error_log /var/log/nginx/error_readonly_server.log; } }
$ request_method変数でmapディレクティブを使用すると、メソッドの
$debug_location
等性に応じて、新しい
$debug_location
変数の値が
$debug_location
ます。
error_page
ディレクティブが
error_page
と、
$debug_location
変数が計算され、リクエストが転送されるデバッグサーバーが決定されます。
多くの場合、 proxy_next_upstreamディレクティブは、(デバッグサーバーに送信する前に)失敗した要求を上流の他のサーバーに再送信するために使用されます。 原則として、これはネットワークレベルのエラーに使用されますが、5xxエラー用に拡張することもできます。 NGINXバージョン1.9.13以降、デフォルトでは5xxエラーを引き起こす非べき等のリクエストは転送されません。 この動作を有効にするには、
non_idempotent
ディレクティブに
non_idempotent
パラメーターを追加する必要があります。 バージョンR9(2016年4月)以降、同じ動作がNGINX Plusに実装されています。
location / { proxy_pass http://app_server; proxy_next_upstream http_500 http_503 http_504 non_idempotent; proxy_intercept_errors on; error_page 500 503 504 @debug; }
おわりに
5xxエラーを無視しないでください。 DevOpsモデルを使用している場合でも、継続的デリバリーを試している場合でも、アップグレードのリスクを軽減したい場合でも、NGINXは問題への対応を改善するツールを提供します。