SLB 是一種針對 UDP 流量的無會話負載平衡器,解決了針對此類流量使用傳統(功能豐富)負載平衡器所固有的問題。
對於簡單、無狀態的 UDP 協議,嘗試維持客戶端和後端實例之間的「親和性」(也稱為「會話」)沒有任何優勢。傳統的負載平衡器認為關聯性很有幫助,因此它們會嘗試將封包從客戶端路由到一致的後端伺服器。相較之下,SLB 在所有可用後端上均勻(隨機)地分發資料包。這會導致後端負載均勻,並在一個後端執行個體發生故障時提高穩健性(所有客戶端的資料包遺失都會增加,而不是某些客戶端的流量全部遺失)。
預設情況下,SLB 將在連接埠1812
和1813
上偵聽傳入的 UDP 封包,並將它們中繼到它所知道的隨機後端目標。它偵聽的連接埠可以使用--server-port-range
選項,該選項接受單一連接埠(例如541
)或一系列連接埠(例如4000-5000
)。
要讓 SLB 感知後端,需要將「看門狗」(又稱「保持活動」)資料包傳送到管理連接埠(更多內容請見下文)。預設情況下,管理埠為1111
,但可以使用--admin-port
選項進行設定。如果您的系統中存在多個網路卡,您可以使用--admin-ip
選項指定 IP。如果使用--admin-ip
指定的 IP 在多播 CIDR 範圍 ( 244.0.0.0/4
) 內,SLB 將自動加入該多播群組(更多資訊請見下文)。
其他選項在命令幫助中描述:
SimplestLoadBalancer:
Sessionless UDP Load Balancer sends packets to backends without session affinity.
Usage:
SimplestLoadBalancer [options]
Options:
--server-port-range <server-port-range> Set the ports to listen to and forward to backend targets
(default "1812-1813") [default: 1812-1813]
--admin-ip <admin-ip> Set the IP to listen on for watchdog events [default is first private IP]
--admin-port <admin-port> Set the port that targets will send watchdog events [default: 1111]
--client-timeout <client-timeout> Seconds to allow before cleaning-up idle clients [default: 30]
--target-timeout <target-timeout> Seconds to allow before removing target missing watchdog events [default: 30]
--default-target-weight <default-target-weight> Weight to apply to targets when not specified [default: 100]
--unwise Allows public IP addresses for targets [default: False]
--stats-period-ms <stats-period-ms> Sets the number of milliseconds between statistics messages printed to the
console (disable: 0, max: 65535) [default: 1000]
--default-group-id <default-group-id> Sets the group ID to assign to backends that when a registration packet doesn't
include one, and when port isn't assigned a group [default: 0]
--version Show version information
-?, -h, --help Show help and usage information
後端不是在命令列中配置的。相反,它們使用發送到管理連接埠 ( --admin-port
) 的定期 UDP 封包動態註冊和取消註冊。這些資料包的內容可能會有所不同,具體取決於您在環境中使用 SLB 的方式。
如果您執行 SLB 伺服器,則可以將後端設定為將封包傳送至該 IP 和管理連接埠。這是最簡單的場景。每個後端將發送帶有兩個“魔法位元組”的訊息來指示內容的“後端註冊”:
0x11 0x11
SLB 會將此類資料包解釋為「將發送方註冊為後端」。可選地,訊息可以包含一兩個附加位元組(權重和群組ID),其目的將在下面更詳細地討論。
0x11 0x11 [X] [X]
^ ^
| |
| one byte for group id
|
one byte for weight
在某些環境中,註冊資料包不會從後端本身發送,且 SLB 支援此類用例。當從「第三方」發送註冊資料包時,內容需要包含正在註冊的後端的 IP 位址:
0x11 0x11 X X X X [X] [X]
^ ^ ^
| | |
| | one byte for group id
| |
| one byte for weight
|
four bytes for ip to add
同樣,可以選擇性地附加權重和群組ID位元組。
當需要使用多個 SLB 進行更強大的 HA 部署時,可以透過使用多播群組 IP 來簡化後端和 SLB 之間的通訊。這很有幫助,因為每個 SLB 必須了解每個後端。在這種情況下,SLB 伺服器應使用--admin-ip
選項來指定多播位址,這將導致 SLB 加入多播群組,從而全部接收發送到該 IP 的任何訊息。後端可以使用該單一 IP 進行配置,從而最大程度地減少其工作負載並簡化其配置(特別是當 SLB 由於自動擴展和/或使用 Spot 實例而輪流提供和停止服務時)。
請注意,使用多播 IP 需要支援多播的交換機,或者(更有可能)在配置了多播域的 AWS VPC 中執行。
從 2.0 版本開始,管理資料包格式非常簡單。在最簡單的單 SLB 用例中,來自後端的註冊資料包可能只包含兩個幻位元組 ( 0x11
0x11
)。或者,封包可以來自不同的來源(例如管理伺服器)並包含四個位元組來指定後端的 ipv4 位址。在任何一種情況下,都可以附加兩個額外的可選字節,用於相對於其他後端的流量“權重”,以及分配給後端的“群組”(下面將詳細介紹群組)。在 ASCII 藝術中:
0x11 0x11 [X X X X] [X] [X]
^ ^ ^
| | |
| | one byte for group id
| |
| one byte for weight
|
four bytes for ip to add
若要立即刪除目標,請傳送第一個位元組為0x86
而不是0x11
的封包(如果從管理伺服器傳送,請附加要刪除的後端的 IP):
0x86 0x11 [X X X X]
^
|
four bytes for ip to remove
權重用於控制傳送到每個後端的相對流量。如果未指定權重,則預設值 100(可使用--default-target-weight
配置)將套用於後端,並且每個後端將接收相同數量的資料包。也就是說,預計(並建議)後端根據其處理流量的能力調整其管理資料包中的權重值(當 CPU 較高、正在應用更新等時可能會減少)。例如:
100
、 50
和50
,那麼第一個後端將獲得 50% 的流量,第二個和第三個後端將各獲得 25% 的流量。31
和31
,則每個後端將接收 50% 的流量。使用組別時,相對權重是與同一組中的其他後端(並非跨所有組)進行評估的。
以足夠的節奏可靠地發送管理資料包非常重要。每次 SLB 收到資料包時,後端的「最後查看」時間都會更新。如果 30 秒(可使用
--target-timeout
配置)過去了而沒有看到後端,則它將被刪除,並且不會再向其發送任何流量。
預設情況下,所有後端將用於為負載平衡器服務的所有連接埠提供服務。
但是,可以使用 SLB 連接埠分配訊息並在註冊訊息中提供群組 ID 將各個連接埠指派給後端子集。例如,假設您希望對連接埠 1812-1813 進行 SLB 負載平衡流量,但將到達每個連接埠的流量分配給一組不同的伺服器。為此:
x66 x11
)。這些訊息不需要重複,並且可以在需要更改連接埠群組分配時發送(但是,重複這些訊息沒有壞處,這可以方便地確保在服務重新啟動後連接埠被正確分配到群組)。 0x66 0x11 X X X
^ ^
| |
| one byte for group ID
|
two bytes for port number, litten endian
使用 Linux bash
發送管理資料包非常簡單。這可以使用netcat
(又稱nc
)命令或/dev/udp
檔案系統來完成。例如,如果您的負載平衡器正在偵聽預設管理連接埠1111
並且您想要新增 IP 為192.168.1.22
目標:
$ echo -e $( echo " x11x11 $( echo " 192.168.1.22 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.0.0.1/1111
由於手動發送這些資料包來註冊一組目標可能很乏味,因此您可以建立一個小的 shell 腳本,例如lb.sh
:
#! /bin/bash
echo -ne $( echo " x11x11 $( echo " 192.168.1.22 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.23 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.24 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.25 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.26 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.27 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
然後使用watch
命令每隔幾秒鐘調用該腳本:
$ watch -n10 ./lb.sh
適用於 Linux 和 Windows x64 以及 Linux ARM 的預先建置二進位檔案可作為 GitHub「版本」取得。這是一個非常簡單的 .Net 8.0 項目,因此要建置它並運行(假設您已經安裝了 dotnet-sdk-8.0):
dotnet build
您可能希望生成本機二進位可執行文件,這很方便並且提供一些效能優勢。
對於Linux:
dotnet publish -o ./ -c Release -r linux-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true --self-contained
對於 Windows:
dotnet publish -o ./ -c Release -r win10-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true --self-contained
同樣,在專案目錄中使用dotnet run
運行也很簡單:
$ dotnet run
或者,如果您已經建立了本機可執行檔:
$ ./SimplestLoadBalancer
請隨時在 GitHub 上建立問題以取得問題和錯誤報告。沒有提供模板或要求,但請嘗試盡可能具有描述性 - 這將有助於確保我們能夠以合理的方式做出回應。另請參閱貢獻。
享受!