使用IPVS實現Kubernetes入口流量負載均衡_Kubernetes中文社群
新搭建的Kubernetes叢集如何承接外部訪問的流量,是剛上手Kubernetes時常常會遇到的問題。 在公有云上,官方給出了比較直接的答案,使用LoadBalancer
型別的Service,利用公有云提供的負載均衡服務來承接流量, 同時在多臺伺服器之間進行負載均衡。而在私有環境中,如何正確的將外部流量引入到叢集內部,卻暫時沒有標準的做法。 本文將介紹一種基於IPVS來承接流量並實現負載均衡的方法,供大家參考。
IPVS
IPVS是LVS專案的一部分,是一款執行在Linux kernel當中的4層負載均衡器,效能異常優秀。 根據這篇文章的介紹,使用調優後的核心,可以輕鬆處理每秒10萬次以上的轉發請求。目前在中大型網際網路專案中, IPVS被廣泛的使用,用於承接網站入口處的流量。
Kubernetes Service
Service是Kubernetes的基礎概念之一,它將一組Pod抽象成為一項服務,統一的對外提供服務,在各個Pod之間實現負載均衡。 Service有多種型別,最基本的ClusterIP
型別解決了叢集內部訪問服務的需求,NodePort
型別通過Node節點的埠暴露服務, 再配合上LoadBalancer
型別所定義的負載均衡器,實現了流量經過前端負載均衡器分發到各個Node節點暴露出的埠, 再通過iptables
進行一次負載均衡,最終分發到實際的Pod上這個過程。
在Service的Spec中,externalIPs
欄位平常鮮有人提到,當把IP地址填入這個欄位後,kube-proxy
iptables
規則, 當有以對應IP為目標的流量傳送到Node節點時,iptables
將進行NAT,將流量轉發到對應的服務上。一般情況下, 很少會遇到伺服器接受非自身繫結IP流量的情況,所以externalIPs
不常被使用,但配合網路層的其他工具,它可以實現給Service繫結外部IP的效果。
今天我們將使用externalIPs
配合IPVS的DR(Direct Routing)模式實現將外部流量引入到叢集內部,同時實現負載均衡。
環境搭建
為了演示,我們搭建了4臺伺服器組成的叢集。一臺伺服器執行IPVS,扮演負載均衡器的作用,一臺伺服器執行Kubernetes Master元件, 其他兩臺伺服器作為Node加入到Kubernetes叢集當中。搭建過程這裡不詳細介紹,大家可以參考相關的文件。
所有伺服器在172.17.8.0/24
這個網段中。服務的VIP我們設定為172.17.8.201
。整體架構入下圖所示:
接下來讓我們來配置IPVS和Kubernetes。
使用externalIPs暴露Kubernetes Service
首先在叢集內部執行2個nginx Pod用作演示。
$ kubectl run nginx --image=nginx --replicas=2
再將它暴露為Service,同時設定externalIPs
欄位
$ kubectl expose deployment nginx --port 80 --external-ip 172.17.8.201
檢視iptables
配置,確認對應的iptables
規則已經被加入。
$ sudo iptables -t nat -L KUBE-SERVICES -n Chain KUBE-SERVICES (2 references) target prot opt source destination KUBE-SVC-4N57TFCL4MD7ZTDA tcp -- 0.0.0.0/0 10.3.0.156 /* default/nginx: cluster IP */ tcp dpt:80 KUBE-MARK-MASQ tcp -- 0.0.0.0/0 172.17.8.201 /* default/nginx: external IP */ tcp dpt:80 KUBE-SVC-4N57TFCL4MD7ZTDA tcp -- 0.0.0.0/0 172.17.8.201 /* default/nginx: external IP */ tcp dpt:80 PHYSDEV match ! --physdev-is-in ADDRTYPE match src-type !LOCAL KUBE-SVC-4N57TFCL4MD7ZTDA tcp -- 0.0.0.0/0 172.17.8.201 /* default/nginx: external IP */ tcp dpt:80 ADDRTYPE match dst-type LOCAL KUBE-SVC-NPX46M4PTMTKRN6Y tcp -- 0.0.0.0/0 10.3.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443 KUBE-NODEPORTS all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
配置IPVS實現流量轉發
首先在IPVS伺服器上,開啟ipv4_forward
。
$ sudo sysctl -w net.ipv4.ip_forward=1
接下來載入IPVS核心模組。
$ sudo modprobe ip_vs
將VIP繫結在網絡卡上。
$ sudo ifconfig eth0:0 172.17.8.201 netmask 255.255.255.0 broadcast 172.17.8.255
再使用ipvsadm
來配置IPVS,這裡我們直接使用Docker映象,避免和特定發行版繫結。
$ docker run --privileged -it --rm --net host luizbafilho/ipvsadm / # ipvsadm IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn / # ipvsadm -A -t 172.17.8.201:80 / # ipvsadm -a -t 172.17.8.201:80 -r 172.17.8.11:80 -g / # ipvsadm -a -t 172.17.8.201:80 -r 172.17.8.12:80 -g / # ipvsadm IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 172.17.8.201:http wlc -> 172.17.8.11:http Route 1 0 0 -> 172.17.8.12:http Route 1 0 0
可以看到,我們成功建立了從VIP到後端伺服器的轉發。
驗證轉發效果
首先使用curl
來測試是否能夠正常訪問nginx服務。
$ curl http://172.17.8.201 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
接下來在172.17.8.11
上抓包來確認IPVS的工作情況。
$ sudo tcpdump -i any port 80 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes 04:09:07.503858 IP 172.17.8.1.51921 > 172.17.8.201.http: Flags [S], seq 2747628840, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 1332071005 ecr 0,sackOK,eol], length 0 04:09:07.504241 IP 10.2.0.1.51921 > 10.2.0.3.http: Flags [S], seq 2747628840, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 1332071005 ecr 0,sackOK,eol], length 0 04:09:07.504498 IP 10.2.0.1.51921 > 10.2.0.3.http: Flags [S], seq 2747628840, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 1332071005 ecr 0,sackOK,eol], length 0 04:09:07.504827 IP 10.2.0.3.http > 10.2.0.1.51921: Flags [S.], seq 3762638044, ack 2747628841, win 28960, options [mss 1460,sackOK,TS val 153786592 ecr 1332071005,nop,wscale 7], length 0 04:09:07.504827 IP 10.2.0.3.http > 172.17.8.1.51921: Flags [S.], seq 3762638044, ack 2747628841, win 28960, options [mss 1460,sackOK,TS val 153786592 ecr 1332071005,nop,wscale 7], length 0 04:09:07.504888 IP 172.17.8.201.http > 172.17.8.1.51921: Flags [S.], seq 3762638044, ack 2747628841, win 28960, options [mss 1460,sackOK,TS val 153786592 ecr 1332071005,nop,wscale 7], length 0 04:09:07.505599 IP 172.17.8.1.51921 > 172.17.8.201.http: Flags [.], ack 1, win 4117, options [nop,nop,TS val 1332071007 ecr 153786592], length 0
可以看到,由客戶端172.17.8.1
傳送給172.17.8.201
的封包,經過IPVS的中轉發送給了172.17.8.11
這臺伺服器, 並經過NAT後傳送給了10.2.0.3
這個Pod。返回的封包不經過IPVS伺服器直接從172.17.8.11
傳送給了172.17.8.1
。 說明IPVS的DR模式工作正常。重複多次測試可以看到流量分別從172.17.8.11
和172.17.8.12
進入,再分發給不同的Pod, 說明負載均衡工作正常。
與傳統的IPVS DR模式配置不同的是,我們並未在承接流量的伺服器上執行繫結VIP,再關閉ARP的操作。 那是因為對VIP的處理直接發生在iptables上,我們無需在伺服器上執行程式來承接流量,iptables會將流量轉發到對應的Pod上。
使用這種方法來承接流量,僅需要配置externalIPs
為VIP即可,無需對伺服器做任何特殊的設定,使用起來相當方便。
總結
在本文中演示了使用IPVS配合externalIPs實現將外部流量匯入到Kubernetes叢集中,並實現負載均衡的方法。 希望可以幫助大家理解IPVS和externalIPs的工作原理,以便在恰當的場景下合理使用這兩項技術解決問題。 實際部署時,還需要考慮後臺伺服器可用性檢查,IPVS節點主從備份,水平擴充套件等問題。在這裡就不詳細介紹了。
在Kubernetes中還有許多與externalIPs類似的非常用功能,有些甚至是使用Annotation來進行配置,將來有機會再進一步分享。
最後插播下廣告,為了實現私有環境下的Kubernetes叢集自動化部署和運維,我們為Archon系統增加了PXE管理物理機的支援, 相應的配置案例在這裡。如果使用過程中有任何問題,歡迎跟我們聯絡。