Kubernetes學(xué)習(xí)筆記之kube-proxy service實現(xiàn)原理
1. Overview
我們生產(chǎn)k8s對外暴露服務(wù)有多種方式,其中一種使用external-ips?clusterip?service?ClusterIP Service方式對外暴露服務(wù),kube-proxy使用iptables mode。這樣external ips可以指定固定幾臺worker節(jié)點的IP地址(worker節(jié)點服務(wù)已經(jīng)被驅(qū)逐,作為流量轉(zhuǎn)發(fā)節(jié)點不作為計算節(jié)點),并作為lvs vip下的rs來負(fù)載均衡。根據(jù)vip:port來訪問服務(wù),并且根據(jù)port不同來區(qū)分業(yè)務(wù)。相比于NodePort Service那樣可以通過所有worker節(jié)點的node_ip:port來訪問更高效,也更容易落地生產(chǎn)。但是,traffic packet是怎么根據(jù)集群外worker節(jié)點的node_ip:port或者集群內(nèi)cluster_ip:port訪問方式找到pod ip的?
并且,我們生產(chǎn)k8s使用calico來作為cni插件,采用 Peered with TOR (Top of Rack) routers方式部署,每一個worker node和其置頂交換機(jī)建立bgp peer配對,置頂交換機(jī)會繼續(xù)和上層核心交換機(jī)建立bgp peer配對,這樣可以保證pod ip在公司內(nèi)網(wǎng)可以直接被訪問。
但是,traffic packet知道了pod ip,又是怎么跳到pod的呢?
以上問題可以歸并為一個問題:數(shù)據(jù)包是怎么一步步跳轉(zhuǎn)到pod的?很長時間以來,一直在思考這些問題。
2. 原理分析
實際上答案很簡單:訪問業(yè)務(wù)服務(wù)vip:port或者說node_ip:port,當(dāng)packet到達(dá)node_ip所在機(jī)器如worker A節(jié)點時,會根據(jù)iptable rules一步步找到pod ip;找到了pod ip后,由于使用calico bgp部署方式,核心交換機(jī)和置頂交換機(jī)都有該pod ip所在的ip段的路由,packet最后會跳轉(zhuǎn)到某一個worker節(jié)點比如worker B,而worker B上有calico早就寫好的路由規(guī)則route和虛擬網(wǎng)卡virtual interface,再根據(jù)veth pair從而由host network namespace跳轉(zhuǎn)到pod network namespace,從而跳轉(zhuǎn)到對應(yīng)的pod。
首先可以本地部署個k8s集群模擬測試下,這里使用 install minikube with calico :
然后部署個業(yè)務(wù)pod,這里使用nginx為例,副本數(shù)為2,并創(chuàng)建ClusterIP Service with ExternalIPs和NodePort Service:
部署完成后,就可以通過 ExternalIP ClusterIP Service或者NodePort Service兩種方式訪問業(yè)務(wù)服務(wù):
3. iptables寫自定義規(guī)則
當(dāng)數(shù)據(jù)包通過node_ip:port或者cluster_ip:port訪問服務(wù)時,會在當(dāng)前worker節(jié)點被內(nèi)核DNAT(Destination Network Address Translation)為pod ip,反向packet又會被SNAT(Source Network Address Translation)。這里借用calico官網(wǎng)的非常生動的兩張圖說明About?Kubernetes?Services?:
cluster-ip service 訪問流程:
node-port service 訪問流程:
由于我們生產(chǎn)k8s的kube-proxy使用iptables mode,所以這些snat/dnat規(guī)則是kube-proxy進(jìn)程通過調(diào)用iptables命令來實現(xiàn)的。iptables使用各種chain來管理大量的iptable rules,主要是五鏈四表,五鏈包括:prerouting/input/output/forward/postrouting chain,四表包括:
raw/mangle/nat/filter table,同時也可以用戶自定義chain。數(shù)據(jù)包packet進(jìn)過內(nèi)核時經(jīng)過五鏈四表流程圖如下:
而kube-proxy進(jìn)程會在nat table內(nèi)自定義KUBE-SERVICES chain,并在PREROUTING內(nèi)生效,可以通過命令查看,然后在查看KUBE-SERVICES chain中的規(guī)則:
可以看到,如果在集群內(nèi)通過cluster_ip:port即10.196.52.1:8088,或者在集群外通過external_ip:port即192.168.64.57:8088方式訪問服務(wù),都會在內(nèi)核里匹配到 KUBE-SVC-JKOCBQALQGD3X3RT chain的規(guī)則,這個對應(yīng)nginx-demo-1 service;如果是在集群內(nèi)通過cluster_ip:port即10.196.89.31:8089,或者集群外通過nodeport_ip:port即192.168.64.57:31755方式訪問服務(wù),會匹配到 KUBE-SVC-6JCCLZMUQSW27LLD chain的規(guī)則,這個對應(yīng)nginx-demo-2 service: 然后繼續(xù)查找 KUBE-SVC-JKOCBQALQGD3X3RT chain和 KUBE-SVC-6JCCLZMUQSW27LLD chain的規(guī)則,發(fā)現(xiàn)每一個 KUBE-SVC-xxx 都會跳轉(zhuǎn)到 KUBE-SEP-xxx chain上,并且因為pod副本數(shù)是2,這里就會有兩個 KUBE-SEP-xxx chain,并且以50%概率跳轉(zhuǎn)到任何一個 KUBE-SEP-xxx chain,即rr(round robin)負(fù)載均衡算法,這里kube-proxy使用iptables statistic module來設(shè)置的,最后就會跳轉(zhuǎn)到pod ip 10.217.120.72:80(這里假設(shè)訪問這個pod)。總之,經(jīng)過kube-proxy調(diào)用iptables命令,根據(jù)service/endpoint設(shè)置對應(yīng)的chain,最終一步步跳轉(zhuǎn)到pod ip,從而數(shù)據(jù)包packet下一跳是該pod ip:
總之,不管是通過cluster_ip:port、external_ip:port還是node_ip:port方式訪問業(yè)務(wù)服務(wù),packet通過kube-proxy進(jìn)程自定義的各種chain找到了下一跳pod ip地址。
但是,packet如何知道這個pod ip在哪個節(jié)點呢?
4. calico寫自定義routers和virtual interface
上文已經(jīng)說過,我們部署calico方式可以保證pod ip在集群外是可以被路由的,這是因為交換機(jī)上會有node level的路由規(guī)則,在交換機(jī)上執(zhí)行 dis bgp routing-table會有類似如下路由規(guī)則。表示如果訪問 10.20.30.40/26 pod網(wǎng)段下一跳是worker B的IP地址。這些路由規(guī)則是部署在每一個worker節(jié)點的bird進(jìn)程(bgp client)分發(fā)的,交換機(jī)通過BGP學(xué)習(xí)來的:
所以,packet在知道了pod ip 10.217.120.72:80?后(這里假設(shè)訪問了pod nginx-demo-1-7f67f8bdd8-fxptt),很容易找到了worker B節(jié)點,本文章示例即是minikube節(jié)點。查看該節(jié)點的路由表和網(wǎng)卡,找到了在host network namespace這一側(cè)是網(wǎng)卡 cali1087c975dd9,編號是13,這個編號很重要,可以通過編號知道這個veth pair的另一端在哪個pod network namespace。發(fā)現(xiàn) pod nginx-demo-1-7f67f8bdd8-fxptt 的網(wǎng)卡eth0就是veth pair的另一端,并且編號也是13:
以上路由表規(guī)則和虛擬網(wǎng)卡是calico cni的calico network plugin創(chuàng)建的,而pod ip以及每一個node的pod ip cidr網(wǎng)段都是由calico ipam plugin創(chuàng)建管理的,并且這些數(shù)據(jù)會寫入calico datastore內(nèi)。至于calico network plugin和calico ipam plugin具體是如何做的,后續(xù)有時間再記錄學(xué)習(xí)。
5. 總結(jié)
不管集群內(nèi)cluster_ip:port,還是集群外external_ip:port或node_ip:port方式訪問服務(wù),都是會通過kube-proxy進(jìn)程設(shè)置的各種iptables rules后跳轉(zhuǎn)到對應(yīng)的pod ip,然后借助于calico bgp部署方式跳轉(zhuǎn)到目標(biāo)pod所在worker節(jié)點,并通過該節(jié)點的路由表和虛擬網(wǎng)卡,找到對應(yīng)的那個pod,packet由host network namespace再跳轉(zhuǎn)到pod network namespace。一直以來的有關(guān)service和calico疑問也算是搞明白了。
參考鏈接
-
https://docs.projectcalico.org/about/about-kubernetes-service -
https://mp.weixin.qq.com/s/bYZJ1ipx7iBPw6JXiZ3Qu -
https://mp.weixin.qq.com/s/oaW87xLnlUYYrwVjBnqee -
https://mp.weixin.qq.com/s/RziLRPYqNoQEQuncm47rHg
文章轉(zhuǎn)載:360云計算
(版權(quán)歸原作者所有,侵刪)