K8S kube-proxy iptables 原理分析
- Precondition
- 什么是 Kube Proxy
- Kube Proxy 原理
- 部署環(huán)境
- 搭建一個(gè)GuestBook 例子
- 分析iptables
- 1. 創(chuàng)建iptables實(shí)現(xiàn)外網(wǎng)通過nodePort訪問
- 2. 分析k8s的 iptables
- 2.1 集群內(nèi)部通過cluster ip 訪問到Pod
- 2.1.1 iptables分析
- 2.1.2 抓包分析
- 2.1.1.1 在haofan-test-1上 直接訪問 cluster ip: port
- 2.1.1.2 在redis pod 內(nèi)上 直接訪問 cluster ip: port
- 2.2 集群外部通過node ip 訪問到Pod
- 2.2.1 iptables分析
- 2.2.2 抓包分析
- 2.4 總結(jié)
- Refer
Precondition
要理解這篇文章之前,需要先對下面這些技術(shù)有些基本的理解。
- Iptables: http://www.zsythink.net/archives/1199
- K8S 相關(guān)
- Linux 路由相關(guān)
- SNAT: 是路由之后進(jìn)行的; DNAT: 是路由之前進(jìn)行的
什么是 Kube Proxy
kube-proxy是k8s的一個(gè)核心組件。每臺機(jī)器上都運(yùn)行一個(gè)kube-proxy服務(wù),它監(jiān)聽API server中service和endpoint的變化情況,并通過iptables等來為后端服務(wù)配置負(fù)載均衡。 帶來的問題是:如果集群存在上萬的Service/Endpoint, 則需要非常多的iptables roules,這會(huì)讓性能降低。
Kube Proxy 原理
部署環(huán)境
3 master + 2 work 節(jié)點(diǎn),只有兩個(gè)work節(jié)點(diǎn)部署kubelet 和 kube-proxy。2個(gè)work節(jié)點(diǎn) ip:
| 192.168.3.233 | 192.168.3.232 |
搭建一個(gè)GuestBook 例子
kubectl apply -f guestbook-all-in-one.yaml
guestbook-all-in-one.yaml 如下:
分析iptables
查看新創(chuàng)建的service, frontend的容器端口是80, nodePort端口是30784
[root@haofan-test-2 ~]# kubectl get svc --all-namespaces default frontend NodePort 172.16.92.224 <none> 80:30784/TCP 23h default redis-master ClusterIP 172.16.155.140 <none> 6379/TCP 23h default redis-slave ClusterIP 172.16.67.204 <none> 6379/TCP 23h查看frontend pod的分布情況
[root@haofan-test-2 ~]# kubectl get pods -o wide -n production NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE frontend-5798c4bfc7-7bt7k 1/1 Running 0 23h 172.18.1.22 192.168.3.233 <none> frontend-5798c4bfc7-nbmvp 1/1 Running 0 23h 172.18.0.20 192.168.3.232 <none> frontend-5798c4bfc7-v7889 1/1 Running 0 23h 172.18.1.23 192.168.3.233 <none>1. 創(chuàng)建iptables實(shí)現(xiàn)外網(wǎng)通過nodePort訪問
思考:
方法:
要進(jìn)行DNAT轉(zhuǎn)換,因?yàn)閿?shù)據(jù)包是經(jīng)過本地協(xié)議發(fā)出 會(huì)經(jīng)過nat的OUTPUT chain. 目的是訪問cluster_ip:port時(shí)可以訪問到pod ip。
同樣要進(jìn)行DNAT轉(zhuǎn)換,為了讓消息能返回客戶端,還需要進(jìn)行SNAT轉(zhuǎn)換。思考為什么要進(jìn)行SNAT ?
MASQUERADE和SNAT的功能類似,只是SNAT需要明確指明源IP的的值,MASQUERADE會(huì)根據(jù)網(wǎng)卡IP自動(dòng)更改,所以更實(shí)用一些。
所以說,如果在開發(fā)過程中,想外部訪問某個(gè)應(yīng)用(比如redis),但是呢碰巧這個(gè)應(yīng)用的svc又沒有開啟nodeport,那你就可以模仿我剛才設(shè)置的iptable規(guī)則,從而達(dá)到不修改SVC也能通過外部應(yīng)用訪問。
這里為什么要進(jìn)行SNAT呢 ?
原因是,為了支持從任一節(jié)點(diǎn)IP+NodePort都可以訪問應(yīng)用。
假設(shè)跳過上圖的SNAT,只做DNAT。報(bào)文的確可以經(jīng)過 node 2 轉(zhuǎn)發(fā)到 node 1上的endpoint,source地址是client ip,但是endpoint如何應(yīng)答呢?endpoint內(nèi)的確有默認(rèn)路由指向 node 1,但是如果沒有做SNAT,應(yīng)答時(shí)會(huì)直接從 node 1 發(fā)送給client。
這是一個(gè)三角流量!!
跳過SNAT后,node 1 在轉(zhuǎn)發(fā)應(yīng)答流量的時(shí)候,會(huì)將應(yīng)答報(bào)文的源地址替換為 node 1的地址,這樣的報(bào)文,client是不會(huì)接受的,連接將無法建立,因?yàn)門CP協(xié)議:client request報(bào)文的源地址 和 server的response報(bào)文的目的地址要相同,否則無法建立連接。
如果有了SNAT, node 2 到 node 1的packet 到了node1之后,source地址是node 2的 eth0 地址,這樣在response后,就會(huì)從node1 到node2再轉(zhuǎn)發(fā)出去,而不會(huì)直接通過node1轉(zhuǎn)發(fā)出去。
所以,必須要做SNAT,必須要FULLNAT。
2. 分析k8s的 iptables
2.1 集群內(nèi)部通過cluster ip 訪問到Pod
2.1.1 iptables分析
到達(dá)OUPUT chain后,要經(jīng)過kube-services這個(gè)k8s自定義的鏈。
root@haofan-test-2 ~]# iptables -L OUTPUT Chain OUTPUT (policy ACCEPT) target prot opt source destination KUBE-SERVICES all -- anywhere anywhere ctstate NEW /* kubernetes service portals */然后匹配到下面兩條鏈:
[root@haofan-test-2 ~]# iptables -L KUBE-SERVICES -t nat -n --line-number | grep "80" KUBE-MARK-MASQ tcp -- !172.18.0.0/16 172.18.1.23 /* default/frontend: cluster IP */ tcp dpt:80 KUBE-SVC-GYQQTB6TY565JPRW tcp -- 0.0.0.0/0 172.18.1.23 /* default/frontend: cluster IP */ tcp dpt:80第一條chain,是對源地址不是172.18.0.0/16的,目的地址是 172.18.1.23,目的端口是80打標(biāo)簽,標(biāo)簽后的packet進(jìn)行SNAT,目的就是為了偽裝所有訪問 Service Cluster IP 的外部流量。
再看 KUBE-SVC-GYQQTB6TY565JPRW, 發(fā)現(xiàn)了probability,實(shí)現(xiàn)了svc能夠隨機(jī)訪問到后端
[root@haofan-test-1 ~]# iptables -S -t nat | grep KUBE-SVC-GYQQTB6TY565JPRW -A KUBE-SVC-GYQQTB6TY565JPRW -m comment --comment "production/frontend:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-ABPIR2RYBUVZX2WC -A KUBE-SVC-GYQQTB6TY565JPRW -m comment --comment "production/frontend:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-G27GUN3FGJW5PMGU -A KUBE-SVC-GYQQTB6TY565JPRW -m comment --comment "production/frontend:" -j KUBE-SEP-P2WQD6E6PI2AV6SJ因?yàn)橛?個(gè)pod, 只看其中一個(gè)。發(fā)現(xiàn)有兩條規(guī)則,第一條打標(biāo)簽0x4000是為了做SNAT,第二條實(shí)現(xiàn)DNAT
[root@haofan-test-2 ~]# iptables -L KUBE-SEP-P2WQD6E6PI2AV6SJ -t nat -n Chain KUBE-SEP-P2WQD6E6PI2AV6SJ (1 references) target prot opt source destination KUBE-MARK-MASQ all -- 172.18.1.23 0.0.0.0/0 /* default/frontend: */ DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 /* default/frontend: */ tcp to:172.18.1.23:80 [root@haofan-test-1 ~]# iptables -S -t nat | grep KUBE-MARK-MASQ -N KUBE-MARK-MASQ -A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000然后在KUBE-POSTROUTING鏈時(shí),只對打上了標(biāo)簽的packet進(jìn)行SNAT
root@haofan-test-1 ~]# iptables -S -t nat | grep 0x4000/0x4000 -A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000 -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE至此,一個(gè)訪問cluster ip 最終訪問到Pod的流程走完了。
2.1.2 抓包分析
抓包分析的時(shí)候,為方便,只部署了一個(gè)frontend實(shí)例, frontend實(shí)例只跑在haofan-test-1 node上。
2.1.1.1 在haofan-test-1上 直接訪問 cluster ip: port
[root@haofan-test-1 ~]# curl 172.16.92.224:80在frontend pod 中抓包如下,看到source地址是172.18.1.1,可以認(rèn)為是容器網(wǎng)關(guān)地址。在haofan-test-1上直接curl 172.18.1.23:80, source地址其實(shí)是192.168.3.233,因?yàn)槟J(rèn)路由指向了eth0。匹配到了chain <是對源地址不是172.18.0.0/16的,目的地址是 172.18.1.23,目的端口是80打標(biāo)簽,標(biāo)簽后的packet進(jìn)行SNAT> ,注意這是request packet的SNAT,不是response的SNAT。
[root@haofan-test-1 ~]# kubectl exec -it frontend-5798c4bfc7-vrwcx bashroot@frontend-5798c4bfc7-vrwcx:/var/www/html# tcpdump port 80 -n1:35:24.027687 IP 172.18.1.23.80 > 172.18.1.1.34266: Flags [P.], seq 1:1185, ack 78, win 211, options [nop,nop,TS val 2316549007 ecr 2316549007], length 1184: HTTP: HTTP/1.1 200 OK01:35:24.027728 IP 172.18.1.1.34266 > 172.18.1.23.80: Flags [.], ack 1185, win 234, options [nop,nop,TS val 2316549008 ecr 2316549007], length 001:35:24.027893 IP 172.18.1.1.34266 > 172.18.1.23.80: Flags [F.], seq 78, ack 1185, win 234, options [nop,nop,TS val 2316549008 ecr 2316549007], length 001:35:24.027957 IP 172.18.1.23.80 > 172.18.1.1.34266: Flags [F.], seq 1185, ack 79, win 211, options [nop,nop,TS val 2316549008 ecr 2316549008], length 001:35:24.027984 IP 172.18.1.1.34266 > 172.18.1.23.80: Flags [.], ack 1186, win 234, options [nop,nop,TS val 2316549008 ecr 2316549008], length 02.1.1.2 在redis pod 內(nèi)上 直接訪問 cluster ip: port
可以看到并沒有做SNAT,source 地址是redis的pod地址。
[root@haofan-test-2 ~]# kubectl exec -it redis-slave-6566d8d846-n2phs bash root@redis-slave-6566d8d846-n2phs:/data# curl 172.16.92.224:80root@frontend-5798c4bfc7-vrwcx:/var/www/html# tcpdump port 80 -n tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 01:56:01.425643 IP 172.18.1.52.41838 > 172.18.1.23.80: Flags [S], seq 4271004944, win 27200, options [mss 1360,sackOK,TS val 2317786405 ecr 0,nop,wscale 7], length 0 01:56:01.425723 IP 172.18.1.23.80 > 172.18.1.52.41838: Flags [S.], seq 3853670533, ack 4271004945, win 26960, options [mss 1360,sackOK,TS val 2317786406 ecr 2317786405,nop,wscale 7], length 0 01:56:01.425753 IP 172.18.1.52.41838 > 172.18.1.23.80: Flags [.], ack 1, win 213, options [nop,nop,TS val 2317786406 ecr 2317786406], length 02.2 集群外部通過node ip 訪問到Pod
2.2.1 iptables分析
根據(jù)iptables, 肯定是對PREROUTING鏈動(dòng)手腳
[root@haofan-test-1 ~]# iptables -S -t nat | grep PREROUTING-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES[root@haofan-test-2 ~]# iptables -L PREROUTING -t nat Chain PREROUTING (policy ACCEPT) target prot opt source destination KUBE-SERVICES all -- anywhere anywhere /* kubernetes service portals */然后又調(diào)到KUBE-SERVICES這個(gè)chain上去了,但是因?yàn)楦鶕?jù)具體的destination 地址,只能匹配到下面˙這條chain
[root@haofan-test-2 ~]# iptables -L KUBE-SERVICES -t nat -n 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 LOCALKUBE-NODEPORTS最后還是調(diào)到之前的KUBE-SVC-PKCF2FTRAH6WFOQR
[root@haofan-test-2 ~]# iptables -L KUBE-NODEPORTS -t nat --line-number Chain KUBE-NODEPORTS (1 references) num target prot opt source destination 1 KUBE-MARK-MASQ tcp -- anywhere anywhere /* default/frontend: */ tcp dpt:30784 2 KUBE-SVC-PKCF2FTRAH6WFOQR tcp -- anywhere anywhere /* default/frontend: */ tcp dpt:30784然后這里注意,2條規(guī)則并不是匹配完第一條,就不匹配第二條了,iptables匹配完第一條不匹配第二條是對于prerouting/input/output/postrouting 的規(guī)則,自定義的規(guī)則最終都會(huì)append到這4個(gè)chain上的, 也就是在內(nèi)核中實(shí)際上是不同的chain。 第一條就是目的端口是30784的packet進(jìn)行SNAT,第二條就不用說了。SNAT的目的前面已經(jīng)說了,SNAT之后進(jìn)入pod的packet的source地址是容器網(wǎng)關(guān)地址,抓包可以驗(yàn)證。
經(jīng)過上面的描述,應(yīng)該對網(wǎng)絡(luò)packet數(shù)據(jù)轉(zhuǎn)發(fā)有一個(gè)比較清楚的認(rèn)識。
2.2.2 抓包分析
在haofan-test-1 節(jié)點(diǎn)上,訪問node1 ip:node port, 可以看到source地址是172.18.1.1, 說明做了SNAT
[root@haofan-test-1 ~]# curl 192.168.3.233:32214 root@frontend-5798c4bfc7-vrwcx:/var/www/html# tcpdump port 80 -n tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 02:02:56.127994 IP 172.18.1.1.46634 > 172.18.1.23.80: Flags [S], seq 1822455295, win 43690, options [mss 65495,sackOK,TS val 2318201108 ecr 0,nop,wscale 7], length 0 02:02:56.128057 IP 172.18.1.23.80 > 172.18.1.1.46634: Flags [S.], seq 1731607107, ack 1822455296, win 26960, options [mss 1360,sackOK,TS val 2318201108 ecr 2318201108,nop,wscale 7], length 0 02:02:56.128088 IP 172.18.1.1.46634 > 172.18.1.23.80: Flags [.], ack 1, win 342, options [nop,nop,TS val 2318201108 ecr 2318201108], length 0如果在haofan-test-1節(jié)點(diǎn)上,訪問node2 ip:node port, 可以看到source地址是node2的IP,原因就是上面解釋的為什么要做SNAT。
[root@haofan-test-1 ~]# curl 192.168.3.232:32214 root@frontend-5798c4bfc7-vrwcx:/var/www/html# tcpdump port 80 -n tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 02:21:58.521488 IP 192.168.3.232.49114 > 172.18.1.23.80: Flags [S], seq 2810163436, win 27200, options [mss 1360,sackOK,TS val 2319343500 ecr 0,nop,wscale 7], length 0 02:21:58.521561 IP 172.18.1.23.80 > 192.168.3.232.49114: Flags [S.], seq 3145056659, ack 2810163437, win 26960, options [mss 1360,sackOK,TS val 2319343501 ecr 2319343500,nop,wscale 7], length 0 02:21:58.522490 IP 192.168.3.232.49114 > 172.18.1.23.80: Flags [.], ack 1, win 213, options [nop,nop,TS val 2319343502 ecr 2319343501], length 02.4 總結(jié)
Refer
4.https://www.lijiaocn.com/%E9%A1%B9%E7%9B%AE/2017/03/27/Kubernetes-kube-proxy.html
總結(jié)
以上是生活随笔為你收集整理的K8S kube-proxy iptables 原理分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 无人机民航执照、多旋翼、固定翼视距内驾驶
- 下一篇: 小程序高级电商前端第1周走进Web全栈工