web-dev-qa-db-ja.com

nginxリバースプロキシ-アップストリームA、B、Aの順に試します

多数のバックエンドサーバーを使用して、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が特定のステータスコードを返す場合のみ)。

23

キーポイント:

  • 1つのサーバーにpingを実行すると別のサーバーが起動する場合は、フェイルオーバーのためにupstreamブロックを気にしないでください。最初のサーバーが再び起動したことをnginx(少なくともFOSSバージョンではありません)に伝える方法はありません。 nginxは、backupweightまたは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

20

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

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

}
1
ALex_hha