Nginx

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

  • May 17, 2021

我正在嘗試將 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) 獲得的狀態程式碼覆蓋狀態程式碼,儘管控制伺服器正在發送“位置”標頭。我終於通過將 error_page 行更改為“工作”了error_page 502 =307 @handle_502;,從而強制所有控制伺服器回复都以 307 程式碼發送回客戶端。這是非常hacky和不可取的,因為1)根據控制伺服器的響應,無法控制nginx下一步應該做什麼(理想情況下,我們只想在控制伺服器報告成功時重試後端),以及2)不是所有的HTTP客戶端支持 HTTP 重定向(例如 curl 使用者和使用 libcurl 的應用程序需要顯式啟用以下重定向)。

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

關鍵點:

  • 不要upstream為故障轉移而煩惱,如果 ping 一個伺服器會啟動另一個伺服器 - 沒有辦法告訴 nginx(至少,不是 FOSS 版本)第一台伺服器再次啟動。nginx 將在第一個請求時按順序嘗試伺服器,但不會嘗試後續請求,儘管有任何backupweight設置fail_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

引用自:https://serverfault.com/questions/537269