日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

K8S kube-proxy iptables 原理分析

發(fā)布時(shí)間:2024/1/8 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 K8S kube-proxy iptables 原理分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  • 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:

haofan-test-1haofan-test-2
192.168.3.233192.168.3.232

搭建一個(gè)GuestBook 例子

kubectl apply -f guestbook-all-in-one.yaml
guestbook-all-in-one.yaml 如下:

apiVersion: v1 kind: Service metadata:name: redis-masterlabels:app: redisrole: mastertier: backend spec:ports:- port: 6379targetPort: 6379selector:app: redisrole: mastertier: backend --- apiVersion: extensions/v1beta1 kind: Deployment metadata:name: redis-master spec:replicas: 1template:metadata:labels:app: redisrole: mastertier: backendspec:containers:- name: masterimage: hub.baidubce.com/public/guestbook-redis-master:e2e # or just image: redisresources:requests:cpu: 100mmemory: 100Miports:- containerPort: 6379 --- apiVersion: v1 kind: Service metadata:name: redis-slavelabels:app: redisrole: slavetier: backend spec:ports:- port: 6379selector:app: redisrole: slavetier: backend --- apiVersion: extensions/v1beta1 kind: Deployment metadata:name: redis-slave spec:selector:matchLabels:app: redisrole: slavetier: backendreplicas: 2template:metadata:labels:app: redisrole: slavetier: backendspec:containers:- name: slaveimage: hub.baidubce.com/public/guestbook-redis-slave:v1resources:requests:cpu: 100mmemory: 100Mienv:- name: GET_HOSTS_FROMvalue: dns# Using `GET_HOSTS_FROM=dns` requires your cluster to# provide a dns service. As of Kubernetes 1.3, DNS is a built-in# service launched automatically. However, if the cluster you are using# does not have a built-in DNS service, you can instead# instead access an environment variable to find the master# service's host. To do so, comment out the 'value: dns' line above, and# uncomment the line below:# value: envports:- containerPort: 6379 --- apiVersion: v1 kind: Service metadata:name: frontendlabels:app: guestbooktier: frontend spec:# comment or delete the following line if you want to use a LoadBalancertype: NodePort# if your cluster supports it, uncomment the following to automatically create# an external load-balanced IP for the frontend service.ports:- port: 80selector:app: guestbooktier: frontend --- apiVersion: apps/v1beta2 kind: Deployment metadata:name: frontend spec:selector:matchLabels:app: guestbooktier: frontendreplicas: 3template:metadata:labels:app: guestbooktier: frontendspec:containers:- name: php-redisimage: hub.baidubce.com/public/guestbook-frontend:v4resources:requests:cpu: 100mmemory: 100Mienv:- name: GET_HOSTS_FROMvalue: dns# Using `GET_HOSTS_FROM=dns` requires your cluster to# provide a dns service. As of Kubernetes 1.3, DNS is a built-in# service launched automatically. However, if the cluster you are using# does not have a built-in DNS service, you can instead# instead access an environment variable to find the master# service's host. To do so, comment out the 'value: dns' line above, and# uncomment the line below:# value: envports:- containerPort: 80

分析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訪問

思考:

  • 如果在集群中通過cluster_ip:port 是如何訪問到frontend 172.18.1.22:80
    方法:
    要進(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。
  • iptables -t nat -I OUTPUT -p tcp -m comment --comment "this is clusterip demo" -d 172.16.92.224/32 --dport 80 -j DNAT --to-destination 172.18.1.22:80
  • 如果在集群外部中通過node_ip: port 是如何訪問到frontend 172.18.1.22:80
    同樣要進(jìn)行DNAT轉(zhuǎn)換,為了讓消息能返回客戶端,還需要進(jìn)行SNAT轉(zhuǎn)換。思考為什么要進(jìn)行SNAT ?
  • iptables -t nat -I POSTROUTING -p tcp -d 172.18.1.22 --dport 80 -j MASQUERADE iptables -t nat -I PREROUTING -p tcp -m comment --comment "this is nodeport demo" --dport 30784 -j DNAT --to-destination 172.18.1.22:80

    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)用。

    client\ ^\ \v \(eth0:192.168.3.1)node 1 <--- node 2(eth0: 192.168.2.1)| ^ SNAT| | --->v |endpoint

    假設(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分析

  • 數(shù)據(jù)包是通過本地協(xié)議發(fā)出的,然后需要更改NAT表,k8s只能在OUTPUT這個(gè)鏈上來動(dòng)手
  • 到達(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 0
    2.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 0

    2.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 LOCAL

    KUBE-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 0

    2.4 總結(jié)

  • (inbound)在PREROUTING階段,將所有報(bào)文轉(zhuǎn)發(fā)到KUBE-SERVICES
  • (outbound)在OUTPUT階段,將所有報(bào)文轉(zhuǎn)發(fā)到KUBE-SERVICES
  • (outbound)在POSTROUTING階段,將所有報(bào)文轉(zhuǎn)發(fā)到KUBE-POSTROUTING
  • 圖中共有三個(gè)地方看到了KUBE-MARK-MASQ,前2個(gè)的原因是為了防止上面所說的"三角流量", 最后一個(gè)的原因,是進(jìn)行SNAT,將pod的地址,轉(zhuǎn)成網(wǎng)關(guān)地址,類似容器網(wǎng)關(guān)地址,然后再通過SNAT轉(zhuǎn)成node ip地址,最后轉(zhuǎn)發(fā)出去。圖中共有三個(gè)地方看到了KUBE-MARK-MASQ,前2個(gè)的原因是為了防止上面所說的"三角流量", 最后一個(gè)的原因,是進(jìn)行SNAT,將pod的地址,轉(zhuǎn)成網(wǎng)關(guān)地址,類似容器網(wǎng)關(guān)地址,然后再通過SNAT轉(zhuǎn)成node ip地址,最后轉(zhuǎn)發(fā)出去。
  • 這樣如上圖描述,每添加一個(gè)有N個(gè)endpoints的nodeport類型service:port,新增(2 + 2 + (1 + 2) * N)條規(guī)則,新增1+N條鏈。
  • Refer

  • https://zhuanlan.zhihu.com/p/28289080
  • http://cizixs.com/2017/03/30/kubernetes-introduction-service-and-kube-proxy/
  • refer: https://k8smeetup.github.io/docs/tutorials/services/source-ip/
    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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。