我正在嘗試將 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 會在第一個請求時依序嘗試伺服器,但不會在後續請求中嘗試,無論是否有任何backup
、weight
或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
!
答案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;
}
}