代理應用程式
反向代理會在一個或多個應用程式執行個體前面放上一個公開位址,而且不必修改應用程式。本頁從單一上游開始,逐步建立一個具備健康檢查、逾時與備援的上游池,最後說明應用程式那一端看到的是什麼。
📌 本頁描述 v0.2.2。
🧾 開始之前
Section titled “🧾 開始之前”- 已安裝並執行 Pingclair(安裝),實驗期間先停掉服務:
sudo pc service stop。 - 一個在本機連接埠上監聽的應用程式。這裡的範例使用
127.0.0.1:3000。 - 代理本身要用的連接埠:範例中是
:8080。
🔀 單一上游
Section titled “🔀 單一上游”{ admin 127.0.0.1:2019}
http://:8080 { reverse_proxy 127.0.0.1:3000}sudo cp Pingclairfile /etc/Pingclair/Pingclairfilesudo pingclair validate /etc/Pingclair/Pingclairfilesudo kill -USR1 "$(systemctl show -p MainPID --value pingclair)"curl -i http://localhost:8080/回應來自應用程式,帶著它自己的標頭。admin 選項讓 pingclair reload 能連到執行中的伺服器;上面的 SIGUSR1 重載則不需要它(重載意味著什麼)。
⚖️ 多個上游
Section titled “⚖️ 多個上游”用 to 列出各個執行個體,再選擇流量如何分配:
http://:8080 { reverse_proxy { to 127.0.0.1:3000 to 127.0.0.1:3001 lb_policy round_robin }}若應用程式會回報是哪個連接埠回應的,六個請求會在兩個執行個體之間輪流:
3000 3001 3000 3001 3000 3001lb_policy |
行為 |
|---|---|
round_robin |
依序輪流選擇上游。 |
random |
隨機選擇上游,預設策略。 |
least_conn |
進行中連線最少的上游。 |
ip_hash |
同一個用戶端位址永遠連到同一個上游。 |
first |
第一個可用的上游。 |
header <name>、cookie <name>、query <name> |
依該欄位雜湊,讓同一個工作階段固定在一個執行個體上。 |
weighted_round_robin <w> … |
每個上游一個權重,寫在同一行。 |
權重 0 會排空該上游;大於 100 的權重及所有主要上游權重皆為 0 的集區會被拒絕。lb_try_duration 只限制新重試開始的時間,不截斷已開始的回應。上游可能已收到請求後,自動重試只重複冪等方法。
權重也可以設定在每個上游上,當每個執行個體各有理由時,這樣寫比較好讀:
http://:8080 { reverse_proxy { to 127.0.0.1:3000 { weight 3 } to 127.0.0.1:3001 }}⚠️ lb_policy weighted_round_robin 3 1 會把權重對應到寫在它上方的上游,所以 to 這幾行必須寫在它 之前。順序反過來的話,validate 會以 2 weights were given for 0 upstreams 拒絕這個檔案。
標記為 backup 的上游,只有在其他所有上游都無法使用時才會被用到:
http://:8080 { reverse_proxy { to 127.0.0.1:3000 to 127.0.0.1:3001 { backup } }}兩者都正常時,每個請求都會送到 3000。停掉那個行程,下一個請求就由 3001 回應。
🩺 健康檢查
Section titled “🩺 健康檢查”沒有健康檢查時,上游要等到有請求失敗後才會被移出輪替。健康檢查會在背景探測每個上游,在使用者的請求抵達之前就把失敗的上游移除:
http://:8080 { reverse_proxy { to 127.0.0.1:3000 to 127.0.0.1:3001 health_check { path /health interval 2s timeout 1s status 200 consecutive_failure 2 consecutive_success 1 } }}應用程式需要一個回應成本低的端點,這裡是 /health。每次狀態改變都會被記錄下來,要知道某個執行個體何時離開輪替,就看這裡:
INFO pingclair_proxy::health_check: 🩺 Active upstream health changed backend=Inet(127.0.0.1:3001) healthy=falseINFO pingclair_proxy::health_check: 🩺 Active upstream health changed backend=Inet(127.0.0.1:3001) healthy=true以這份設定實測:停掉第二個執行個體後,所有流量都送往第一個;它恢復後,經過 consecutive_success 次成功的探測就重新加入。Caddy 的扁平寫法(health_uri、health_interval、health_timeout、health_status、health_fails、health_passes)設定的是同一套檢查。
逾時設定寫在 reverse_proxy 裡面的 transport http 區塊中,而不是直接寫在 reverse_proxy 底下:
http://:8080 { reverse_proxy { to 127.0.0.1:3099 to 127.0.0.1:3000 transport http { connect_timeout 1s first_byte_timeout 1s read_timeout 30s write_timeout 30s } }}實測:127.0.0.1:3099 不接受任何連線時,connect_timeout 1s 會花掉一秒,接著請求改送到第二個上游重試,得到 200。若應用程式接受了連線,卻等 3 秒才送出本文,則改由 first_byte_timeout 1s 生效,用戶端會收到 504。
dial_timeout 不是 reverse_proxy 的選項;寫在那裡的話,validate 會以 Unknown directive 'reverse_proxy: dial_timeout' 拒絕這個檔案。transport http 接受 dial_timeout 與 connect_timeout。
🔁 以主機名稱指定上游
Section titled “🔁 以主機名稱指定上游”上游可以是主機名稱,而不是位址。容器重啟後換了 IP 位址時,需要的就是這個:
{ dns_refresh 5s}
http://:8080 { reverse_proxy { to api.internal:3000 }}名稱會依該間隔重新解析,每次重新解析都會記錄下來:
INFO pingclair_proxy::dns: 🔄 Upstream DNS scheduler enabled interval_secs=5 pools=1INFO pingclair_proxy::dns: 🔄 Upstream DNS refresh changed=1 adopted=0 kept_stale=0 unresolved=0以 /etc/hosts 作為唯一依據實測:api.internal 指向 127.0.0.1 時由第一個執行個體回應;把檔案改成 127.0.0.2 後,在間隔時間內就改由第二個回應,不需重啟,也沒有任何請求失敗。查詢失敗時,會保留先前的位址繼續輪替。
📨 上游看到的是什麼
Section titled “📨 上游看到的是什麼”應用程式會收到原本的 Host,用戶端位址則放在常見的標頭裡:
{ "host": "127.0.0.1:8080", "x_forwarded_for": "127.0.0.1", "x_forwarded_proto": "http", "x_real_ip": "127.0.0.1"}如果前面還有另一個代理,除非它列在 trusted_proxies 中,否則這些標頭裡的位址會是那個代理的位址;Cloudflare Tunnel 指南說明了這種情況。
⚠️ 無法運作時
Section titled “⚠️ 無法運作時”- 代理回傳
502。 沒有任何上游回應。請確認應用程式正在監聽(sudo ss -ltnp | grep :3000),而且位址相符。由 Pingclair 產生的502或504會帶有Proxy-Status: pingclair; error=…;自訂handle_errors回應也不帶此欄位,不能只以缺少欄位判定來自應用程式。 - 停頓一陣子後出現
504。 有逾時觸發了:後端太慢是first_byte_timeout,本文太慢是read_timeout,主機始終不接受連線則是connect_timeout。 Unknown directive 'reverse_proxy: …'。 這個選項屬於某個巢狀區塊——逾時放在transport http底下,檢查放在health_check底下——validate會指出它拒絕的確切寫法。- 設定變更沒有生效。 重載無法新增或搬移監聽器。新檔案有這類變更時,unit 的狀態列會列出變動的位址,執行
sudo pc service restart即可套用。請見以服務方式執行。 - 每個請求都落在同一個執行個體上。 它是唯一健康的那一個。健康檢查的日誌會說明其他執行個體何時、為何離開輪替(
ConnectRefused、failure_statuses等)。
- 提供靜態網站:壓縮、快取,以及單頁應用程式的後備路由。
reverse_proxy:指令參考。- 以服務方式執行:重載、重啟與日誌。
📌 連線建立後才失敗的後端——截斷的本文、回應結束前重設、格式錯誤的回應——會計入被動健康狀態,並依 Caddy 的 max_fails 與 fail_duration 決定留在輪替外的時間。 CHANGELOG.
