多数のバックエンドサーバーを使用して、nginxをリバースプロキシとして設定しようとしています。バックエンドをオンデマンドで(最初に受信したリクエストで)起動したいので、受信したリクエストに応じてバックエンドを起動する(HTTPリクエストによって制御される)制御プロセスがあります。
私の問題は、それを行うようにnginxを構成することです。ここに私がこれまで持っているものがあります:
server {
listen 80;
server_name $DOMAINS;
location / {
# redirect to named location
#error_page 418 = @backend;
#return 418; # doesn't work - error_page doesn't work after redirect
try_files /nonexisting-file @backend;
}
location @backend {
proxy_pass http://$BACKEND-IP;
error_page 502 @handle_502; # Backend server down? Try to start it
}
location @handle_502 { # What to do when the backend server is not up
# Ping our control server to start the backend
proxy_pass http://127.0.0.1:82;
# Look at the status codes returned from control server
proxy_intercept_errors on;
# Fallback to error page if control server is down
error_page 502 /fatal_error.html;
# Fallback to error page if control server ran into an error
error_page 503 /fatal_error.html;
# Control server started backend successfully, retry the backend
# Let's use HTTP 451 to communicate a successful backend startup
error_page 451 @backend;
}
location = /fatal_error.html {
# Error page shown when control server is down too
root /home/nginx/www;
internal;
}
}
これは機能しません-nginxはコントロールサーバーから返されたステータスコードを無視するようです。 error_page
ロケーションの@handle_502
ディレクティブは機能せず、451コードはそのままクライアントに送信されます。
私はこれに内部のnginxリダイレクションの使用をやめ、同じ場所への307リダイレクトを発行するようにコントロールサーバーを変更しようとしました(クライアントが同じリクエストを再試行するようになりましたが、バックエンドサーバーが起動した状態です)。ただし、コントロールサーバーが "Location"ヘッダーを送信しているにもかかわらず、nginxはステータスコードをバックエンドリクエストの試行(502)から取得したもので愚かに上書きしています。 error_page行をerror_page 502 =307 @handle_502;
に変更することで、ようやく「機能」し、すべてのコントロールサーバーの応答が307コードでクライアントに送信されるように強制しました。これは非常にハックで望ましくないため、1)コントロールサーバーの応答に応じてnginxが次に何を実行するかを制御できない(理想的には、コントロールサーバーが成功を報告した場合にのみバックエンドを再試行する)、2)すべてのHTTPではないクライアントはHTTPリダイレクトをサポートします(たとえば、curlユーザーとlibcurlを使用するアプリケーションは、次のリダイレクトを明示的に有効にする必要があります)。
Nginxが上流のサーバーA、B、次にAにプロキシを試行する適切な方法は何ですか(理想的には、Bが特定のステータスコードを返す場合のみ)。
キーポイント:
upstream
ブロックを気にしないでください。最初のサーバーが再び起動したことをnginx(少なくともFOSSバージョンではありません)に伝える方法はありません。 nginxは、backup
、weight
またはfail_timeout
の設定にかかわらず、最初のリクエストでサーバーを順番に試行しますが、フォローアップリクエストは試行しません。recursive_error_pages
および名前付きの場所を使用してフェイルオーバーを実装する場合は、error_page
を有効にします。proxy_intercept_errors
を有効にして、上流サーバーから送信されたエラーコードを処理します。=
構文(error_page 502 = @handle_502;
など)が必要です。 =
を使用しない場合、nginxは前のブロックのエラーコードを使用します。元の回答/調査ログは次のとおりです。
ここにあります より良い 私が見つけた回避策は、クライアントのリダイレクトを必要としないため、改善されています。
upstream aba {
server $BACKEND-IP;
server 127.0.0.1:82 backup;
server $BACKEND-IP backup;
}
...
location / {
proxy_pass http://aba;
proxy_next_upstream error http_502;
}
次に、コントロールサーバーに "成功"で502を返させ、コードがバックエンドから返されないようにしてください。
更新:nginxはupstream
ブロックの最初のエントリをダウンとしてマークし続けるため、連続するリクエストでサーバーを順番に試行しません。最初のエントリにweight=1000000000 fail_timeout=1
を追加しても効果はありません。これまでのところ、クライアントのリダイレクトを含まない解決策はありません。
編集:もう1つ知っておきたいこと-error_page
ハンドラーからエラーステータスを取得するには、次の構文を使用します:error_page 502 = @handle_502;
-等号は、nginxがハンドラーからエラーステータスを取得する原因になります。
編集:そして私はそれを動かしました!上記のerror_page
修正に加えて、必要なのはrecursive_error_pages
!
次のようなものを試すことができます
upstream backend {
server a.example.net;
server b.example.net backup;
}
server {
listen 80;
server_name www.example.net;
proxy_next_upstream error timeout http_502;
location / {
proxy_pass http://backend;
proxy_redirect off;
proxy_set_header Host $Host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $remote_addr;
}
}