nginxリバースプロキシ-上流A、B、Aをもう一度試してください


22

多数のバックエンドサーバーを使用して、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リダイレクトを送信するように制御サーバーを変更しようとしました(クライアントが同じリクエストを再試行するようになりましたが、今はバックエンドサーバーが起動しています)。ただし、nginxは、コントロールサーバーが "Location"ヘッダーを送信しているにもかかわらず、バックエンド要求の試行から取得したステータスコード(502)でステータスコードを愚かに上書きしています。error_pageの行をerror_page 502 =307 @handle_502;、したがって、すべての制御サーバーの応答が307コードでクライアントに送り返されます。1)制御サーバーの応答に応じて、nginxが次に何をすべきかを制御できない(理想的には、制御サーバーが成功を報告した場合にのみバックエンドを再試行したい)、2)すべてのHTTPではないためクライアントはHTTPリダイレクトをサポートします(curlユーザーとlibcurlを使用するアプリケーションは、明示的に次のリダイレクトを有効にする必要があります)。

nginxが上位サーバーA、B、Aの順にプロキシしようとする適切な方法は何ですか(理想的には、Bが特定のステータスコードを返す場合のみ)。

回答:


20

キーポイント:

  • upstream1つのサーバーにpingを実行しても別のサーバーが起動する場合、フェールオーバーのブロックを気にしないでください。最初のサーバーが再び起動することをnginx(少なくとも、FOSSバージョンではない)に通知する方法はありません。nginxは最初のリクエストでサーバーを順番に試行しますがbackup、設定weightfail_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です!


1
私にとってはproxy_next_upstreamこれ私が追加する必要がありました、(よく私のシナリオはあなたのような複雑なものではなかった)、私はちょうど、エラーが発生した場合は次のサーバを試してみnginxの望んでいたトリックをしましたproxy_next_upstream error timeout invalid_header non_idempotent;non_idempotent私は主に楽しみたいので、POSTリクエスト)。
フィリップ

1

次のようなものを試すことができます

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;
    }

}

nginxはa.example.net、同じリクエストで1回失敗すると再試行しません。に接続しようとしたときに発生したエラーをクライアントに送信しますb.example.net。これは、制御サーバーにもプロキシを実装しない限り、期待したものにはなりません。
ウラジミールパンテレーエフ

そして、次の状況ではあなたの設定はどうなりますか:上流Aへのリクエストは失敗し、上流Bは失敗します、それから我々は再び上流Aを試し、また失敗します(502)?
ALex_hha

アップストリームBは制御サーバーです。その目的は、アップストリームAへの次のリクエストが成功することを確認することです。目標は、アップストリームAを試し、失敗した場合はアップストリームBを試し、「成功」した場合(「成功」の内部規則を使用)、アップストリームAをもう一度試します。私の質問が十分に明確ではなかった場合、どうすれば改善できるか教えてください。
ウラジミールパンテレーエフ

うーん、たとえばハードウェアの問題など、アップストリームAがダウンしていると仮定しましょう。アップストリームBは何になりますか?クライアントからのリクエストに対する応答を返すことはできますか?
ALex_hha

この問題は、ハードウェア障害のフェイルオーバーに関するものではありません。この問題は、アップストリームバックエンドをオンデマンドで起動することに関するものです。制御サーバー(アップストリームB)がバックエンド(アップストリームA)を再アクティブ化できない場合、理想的にはユーザーに適切なエラーメッセージが表示されますが、解決しようとしている問題ではありません-問題はnginxに再試行を取得しています同じリクエスト内で、再びBの後。
ウラジミールパンテレーエフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.