nginx 反向代理 - 嘗試上游 A,然後再嘗試 B,然後再嘗試 A

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 重定向發送到相同位置(以便客戶端重試相同的請求,但現在後端伺服器已啟動)。然而,現在 nginx 愚蠢地用從後端請求嘗試(502)獲得的狀態代碼覆蓋狀態代碼,儘管控制伺服器正在發送“Location”標頭。我最終通過將 error_page 行更改為 來使其“正常工作” error_page 502 =307 @handle_502;,從而強制所有控制伺服器回複使用 307 代碼發送回客戶端。這是非常hacky和不可取的,因為1)無法根據控制伺服器的回應來控制nginx下一步應該做什麼(理想情況下我們只想在控制伺服器報告成功時重試後端),2)並非所有HTTP用戶端支援HTTP 重定向(例如,curl 使用者和使用libcurl 的應用程式需要明確啟用以下重定向)。

讓 nginx 嘗試代理上游伺服器 A,然後代理 B,然後再次代理 A(理想情況下,僅當 B 返回特定狀態代碼時)的正確方法是什麼?

答案1

關鍵點:

  • 如果 ping 一台伺服器會啟動另一台伺服器,請不要upstream為故障轉移區塊而煩惱 - 沒有辦法告訴 nginx(至少不是 FOSS 版本)第一台伺服器再次啟動。 nginx 會在第一個請求時依序嘗試伺服器,但不會在後續請求中嘗試,無論是否有任何backupweightfail_timeout設定。
  • 必須使用命名位置recursive_error_pages實施故障轉移時啟用。error_page
  • 啟用proxy_intercept_errors處理從上游伺服器傳送的錯誤代碼。
  • 需要使用語法(例如 )來正確處理指定位置中的錯誤代碼=error_page 502 = @handle_502;如果=不使用,nginx 將使用上一個區塊中的錯誤代碼。

總結如下:

server {
    listen ...;
    server_name $DOMAINS;

    recursive_error_pages on;

    # First, try "Upstream A"
    location / {
        error_page 418 = @backend;
        return 418;
    }

    # Define "Upstream A"
    location @backend {
        proxy_pass http://$IP:81;
        proxy_set_header  X-Real-IP     $remote_addr;
        # Add your proxy_* options here
    }

    # On error, go to "Upstream B"
    error_page 502 @handle_502;

    # Fallback static error page, in case "Upstream B" fails
    root /home/nginx/www;
    location = /_static_error.html {
        internal;
    }

    # Define "Upstream B"
    location @handle_502 { # What to do when the backend server is not up
        proxy_pass ...;
        # Add your proxy_* options here
        proxy_intercept_errors on;          # Look at the error codes returned from "Upstream B"
        error_page 502 /_static_error.html; # Fallback to error page if "Upstream B" is down
        error_page 451 = @backend;          # Try "Upstream A" again
    }
}

原始答案/研究日誌如下:


這是我發現的更好的解決方法,這是一個改進,因為它不需要客戶端重定向:

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到第一個條目但沒有效果。到目前為止,我還沒有找到任何不涉及客戶端重定向的解決方案。


編輯:我希望知道的另一件事 - 要從error_page處理程序獲取錯誤狀態,請使用以下語法:error_page 502 = @handle_502;- 等號將導致 nginx 從處理程序獲取錯誤狀態。


編輯:我成功了!除了error_page上述修復之外,所需要做的就是啟用recursive_error_pages

答案2

你可以嘗試類似下面的方法

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

}

相關內容