一文明白calico的IPIP網(wǎng)絡(luò)模式
前言
本文主要分析k8s中網(wǎng)絡(luò)組件calico的 IPIP網(wǎng)絡(luò)模式。旨在理解IPIP網(wǎng)絡(luò)模式下產(chǎn)生的calixxxx,tunl0等設(shè)備以及跨節(jié)點(diǎn)網(wǎng)絡(luò)通信方式??赡芸粗悬c(diǎn)枯燥,但是請(qǐng)花幾分鐘時(shí)間堅(jiān)持看完,如果看到后面忘了前面,請(qǐng)反復(fù)看兩遍,這幾分鐘時(shí)間一定你會(huì)花的很值。
一、calico介紹
Calico是Kubernetes生態(tài)系統(tǒng)中另一種流行的網(wǎng)絡(luò)選擇。雖然Flannel被公認(rèn)為是最簡(jiǎn)單的選擇,但Calico以其性能、靈活性而聞名。Calico的功能更為全面,不僅提供主機(jī)和pod之間的網(wǎng)絡(luò)連接,還涉及網(wǎng)絡(luò)安全和管理。Calico CNI插件在CNI框架內(nèi)封裝了Calico的功能。
Calico是一個(gè)基于BGP的純?nèi)龑拥木W(wǎng)絡(luò)方案,與OpenStack、Kubernetes、AWS、GCE等云平臺(tái)都能夠良好地集成。Calico在每個(gè)計(jì)算節(jié)點(diǎn)都利用Linux Kernel實(shí)現(xiàn)了一個(gè)高效的虛擬路由器vRouter來(lái)負(fù)責(zé)數(shù)據(jù)轉(zhuǎn)發(fā)。每個(gè)vRouter都通過(guò)BGP1協(xié)議把在本節(jié)點(diǎn)上運(yùn)行的容器的路由信息向整個(gè)Calico網(wǎng)絡(luò)廣播,并自動(dòng)設(shè)置到達(dá)其他節(jié)點(diǎn)的路由轉(zhuǎn)發(fā)規(guī)則。Calico保證所有容器之間的數(shù)據(jù)流量都是通過(guò)IP路由的方式完成互聯(lián)互通的。Calico節(jié)點(diǎn)組網(wǎng)時(shí)可以直接利用數(shù)據(jù)中心的網(wǎng)絡(luò)結(jié)構(gòu)(L2或者L3),不需要額外的NAT、隧道或者Overlay Network,沒(méi)有額外的封包解包,能夠節(jié)約CPU運(yùn)算,提高網(wǎng)絡(luò)效率。
此外,Calico基于iptables還提供了豐富的網(wǎng)絡(luò)策略,實(shí)現(xiàn)了Kubernetes的Network Policy策略,提供容器間網(wǎng)絡(luò)可達(dá)性限制的功能。
calico官網(wǎng):https://www.projectcalico.org/
二、calico架構(gòu)及核心組件
架構(gòu)圖如下:
calico核心組件:
- Felix:運(yùn)行在每個(gè)需要運(yùn)行workload的節(jié)點(diǎn)上的agent進(jìn)程。主要負(fù)責(zé)配置路由及 ACLs(訪問(wèn)控制列表) 等信息來(lái)確保 endpoint 的連通狀態(tài),保證跨主機(jī)容器的網(wǎng)絡(luò)互通;
- etcd:強(qiáng)一致性、高可用的鍵值存儲(chǔ),持久存儲(chǔ)calico數(shù)據(jù)的存儲(chǔ)管理系統(tǒng)。主要負(fù)責(zé)網(wǎng)絡(luò)元數(shù)據(jù)一致性,確保Calico網(wǎng)絡(luò)狀態(tài)的準(zhǔn)確性;
- BGP Client(BIRD):讀取Felix設(shè)置的內(nèi)核路由狀態(tài),在數(shù)據(jù)中心分發(fā)狀態(tài)。
- BGP Route Reflector(BIRD):BGP路由反射器,在較大規(guī)模部署時(shí)使用。如果僅使用BGP Client形成mesh全網(wǎng)互聯(lián)就會(huì)導(dǎo)致規(guī)模限制,因?yàn)樗蠦GP client節(jié)點(diǎn)之間兩兩互聯(lián),需要建立N^2個(gè)連接,拓?fù)湟矔?huì)變得復(fù)雜。因此使用reflector來(lái)負(fù)責(zé)client之間的連接,防止節(jié)點(diǎn)兩兩相連。
三、calico工作原理
Calico把每個(gè)操作系統(tǒng)的協(xié)議棧認(rèn)為是一個(gè)路由器,然后把所有的容器認(rèn)為是連在這個(gè)路由器上的網(wǎng)絡(luò)終端,在路由器之間跑標(biāo)準(zhǔn)的路由協(xié)議——BGP的協(xié)議,然后讓它們自己去學(xué)習(xí)這個(gè)網(wǎng)絡(luò)拓?fù)湓撊绾无D(zhuǎn)發(fā)。所以Calico方案其實(shí)是一個(gè)純?nèi)龑拥姆桨?,也就是說(shuō)讓每臺(tái)機(jī)器的協(xié)議棧的三層去確保兩個(gè)容器,跨主機(jī)容器之間的三層連通性。
四、calico的兩種網(wǎng)絡(luò)方式
1)IPIP
把 IP 層封裝到 IP 層的一個(gè) tunnel。它的作用其實(shí)基本上就相當(dāng)于一個(gè)基于IP層的網(wǎng)橋!一般來(lái)說(shuō),普通的網(wǎng)橋是基于mac層的,根本不需 IP,而這個(gè) ipip 則是通過(guò)兩端的路由做一個(gè) tunnel,把兩個(gè)本來(lái)不通的網(wǎng)絡(luò)通過(guò)點(diǎn)對(duì)點(diǎn)連接起來(lái)。ipip 的源代碼在內(nèi)核 net/ipv4/ipip.c 中可以找到。
2)BGP
邊界網(wǎng)關(guān)協(xié)議(Border Gateway Protocol, BGP)是互聯(lián)網(wǎng)上一個(gè)核心的去中心化自治路由協(xié)議。它通過(guò)維護(hù)IP路由表或‘前綴’表來(lái)實(shí)現(xiàn)自治系統(tǒng)(AS)之間的可達(dá)性,屬于矢量路由協(xié)議。BGP不使用傳統(tǒng)的內(nèi)部網(wǎng)關(guān)協(xié)議(IGP)的指標(biāo),而使用基于路徑、網(wǎng)絡(luò)策略或規(guī)則集來(lái)決定路由。因此,它更適合被稱(chēng)為矢量性協(xié)議,而不是路由協(xié)議。
五、IPIP網(wǎng)絡(luò)模式分析
由于個(gè)人環(huán)境中使用的是IPIP模式,因此接下來(lái)這里分析一下這種模式。
- # kubectl get po -o wide -n paas | grep hello
- demo-hello-perf-d84bffcb8-7fxqj 1/1 Running 0 9d 10.20.105.215 node2.perf <none> <none>
- demo-hello-sit-6d5c9f44bc-ncpql 1/1 Running 0 9d 10.20.42.31 node1.sit <none> <none>
進(jìn)行ping測(cè)試
這里在demo-hello-perf這個(gè)pod中ping demo-hello-sit這個(gè)pod。
- root@demo-hello-perf-d84bffcb8-7fxqj:/# ping 10.20.42.31
- PING 10.20.42.31 (10.20.42.31) 56(84) bytes of data.
- 64 bytes from 10.20.42.31: icmp_seq=1 ttl=62 time=5.60 ms
- 64 bytes from 10.20.42.31: icmp_seq=2 ttl=62 time=1.66 ms
- 64 bytes from 10.20.42.31: icmp_seq=3 ttl=62 time=1.79 ms
- ^C
- --- 10.20.42.31 ping statistics ---
- 3 packets transmitted, 3 received, 0% packet loss, time 6ms
- rtt min/avg/max/mdev = 1.662/3.015/5.595/1.825 ms
進(jìn)入pod demo-hello-perf中查看這個(gè)pod中的路由信息
- root@demo-hello-perf-d84bffcb8-7fxqj:/# route -n
- Kernel IP routing table
- Destination Gateway Genmask Flags Metric Ref Use Iface
- 0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
- 169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
根據(jù)路由信息,ping 10.20.42.31,會(huì)匹配到第一條。
第一條路由的意思是:去往任何網(wǎng)段的數(shù)據(jù)包都發(fā)往網(wǎng)關(guān)169.254.1.1,然后從eth0網(wǎng)卡發(fā)送出去。
demo-hello-perf所在的node node2.perf 宿主機(jī)上路由信息如下:
- # route -n
- Kernel IP routing table
- Destination Gateway Genmask Flags Metric Ref Use Iface
- 0.0.0.0 172.16.36.1 0.0.0.0 UG 100 0 0 eth0
- 10.20.42.0 172.16.35.4 255.255.255.192 UG 0 0 0 tunl0
- 10.20.105.196 0.0.0.0 255.255.255.255 UH 0 0 0 cali4bb1efe70a2
- 169.254.169.254 172.16.36.2 255.255.255.255 UGH 100 0 0 eth0
- 172.16.36.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
- 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
可以看到一條Destination為 10.20.42.0的路由。
意思是:當(dāng)ping包來(lái)到master節(jié)點(diǎn)上,會(huì)匹配到路由tunl0。該路由的意思是:去往10.20.42.0/26的網(wǎng)段的數(shù)據(jù)包都發(fā)往網(wǎng)關(guān)172.16.35.4。因?yàn)閐emo-hello-perf的pod在172.16.36.5上,demo-hello-sit的pod在172.16.35.4上。所以數(shù)據(jù)包就通過(guò)設(shè)備tunl0發(fā)往到node節(jié)點(diǎn)上。
demo-hello-sit所在的node node1.sit 宿主機(jī)上路由信息如下:
- # route -n
- Kernel IP routing table
- Destination Gateway Genmask Flags Metric Ref Use Iface
- 0.0.0.0 172.16.35.1 0.0.0.0 UG 100 0 0 eth0
- 10.20.15.64 172.16.36.4 255.255.255.192 UG 0 0 0 tunl0
- 10.20.42.31 0.0.0.0 255.255.255.255 UH 0 0 0 cali04736ec14ce
- 10.20.105.192 172.16.36.5 255.255.255.192 UG 0 0 0 tunl0
當(dāng)node節(jié)點(diǎn)網(wǎng)卡收到數(shù)據(jù)包之后,發(fā)現(xiàn)發(fā)往的目的ip為10.20.42.31,于是匹配到Destination為10.20.42.31的路由。
該路由的意思是:10.20.42.31是本機(jī)直連設(shè)備,去往設(shè)備的數(shù)據(jù)包發(fā)往cali04736ec14ce
為什么這么奇怪會(huì)有一個(gè)名為cali04736ec14ce的設(shè)備呢?這是個(gè)啥玩意兒呢?
其實(shí)這個(gè)設(shè)備就是veth pair的一端。在創(chuàng)建demo-hello-sit 時(shí)calico會(huì)給demo-hello-sit創(chuàng)建一個(gè)veth pair設(shè)備。一端是demo-hello-sit 的網(wǎng)卡,另一端就是我們看到的cali04736ec14ce
接著驗(yàn)證一下。我們進(jìn)入demo-hello-sit 的pod,查看到 4 號(hào)設(shè)備后面的編號(hào)是:122964
- root@demo-hello-sit--6d5c9f44bc-ncpql:/# ip a
- 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
- link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
- inet 127.0.0.1/8 scope host lo
- valid_lft forever preferred_lft forever
- 2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
- link/ipip 0.0.0.0 brd 0.0.0.0
- 4: eth0@if122964: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1380 qdisc noqueue state UP group default
- link/ether 9a:7d:b2:26:9b:17 brd ff:ff:ff:ff:ff:ff link-netnsid 0
- inet 10.20.42.31/32 brd 10.20.42.31 scope global eth0
- valid_lft forever preferred_lft forever
然后我們登錄到demo-hello-sit這個(gè)pod所在的宿主機(jī)查看
- # ip a | grep -A 5 "cali04736ec14ce"
- 122964: cali04736ec14ce@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1380 qdisc noqueue state UP group default
- link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 16
- inet6 fe80::ecee:eeff:feee:eeee/64 scope link
- valid_lft forever preferred_lft forever
- 120918: calidd1cafcd275@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1380 qdisc noqueue state UP group default
- link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 2
發(fā)現(xiàn)pod demo-hello-sit中 的另一端設(shè)備編號(hào)和這里在node上看到的cali04736ec14ce編號(hào)122964是一樣的
所以,node上的路由,發(fā)送cali04736ec14ce網(wǎng)卡設(shè)備的數(shù)據(jù)其實(shí)就是發(fā)送到了demo-hello-sit的這個(gè)pod中去了。到這里ping包就到了目的地。
注意看 demo-hello-sit這個(gè)pod所在的宿主機(jī)的路由,有一條 Destination為10.20.105.192的路由
- # route -n
- Kernel IP routing table
- Destination Gateway Genmask Flags Metric Ref Use Iface
- ...
- 0.0.0.0 172.16.35.1 0.0.0.0 UG 100 0 0 eth0
- 10.20.105.192 172.16.36.5 255.255.255.192 UG 0 0 0 tunl0
- ...
再查看一下demo-hello-sit的pod中路由信息,和demo-hello-perf的pod中是一樣的。
所以綜合上述例子來(lái)看,IPIP的網(wǎng)絡(luò)模式就是將IP網(wǎng)絡(luò)封裝了一層。特點(diǎn)就是所有pod的數(shù)據(jù)流量都從隧道tunl0發(fā)送,并且tunl0這里增加了一層傳輸層的封包操作。
六、抓包分析
在demo-hello-perf這個(gè)pod中ping demo-hello-sit這個(gè)pod,接著在demo-hello-sit這個(gè)pod所在的宿主機(jī)進(jìn)行tcpdump
- # tcpdump -i eth0 -nn -w icmp_ping.cap
- tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
在demo-hello-perf這個(gè)pod中進(jìn)行ping demo-hello-sit的操作
- root@demo-hello-perf-d84bffcb8-7fxqj:/# ping 10.20.42.31
- PING 10.20.42.31 (10.20.42.31) 56(84) bytes of data.
- 64 bytes from 10.20.42.31: icmp_seq=1 ttl=62 time=5.66 ms
- 64 bytes from 10.20.42.31: icmp_seq=2 ttl=62 time=1.68 ms
- 64 bytes from 10.20.42.31: icmp_seq=3 ttl=62 time=1.61 ms
- ^C
- --- 10.20.42.31 ping statistics ---
- 3 packets transmitted, 3 received, 0% packet loss, time 6ms
- rtt min/avg/max/mdev = 1.608/2.983/5.659/1.892 ms
結(jié)束抓包后下載icmp_ping.cap到本地windows進(jìn)行抓包分析
能看到該數(shù)據(jù)包一共5層,其中IP(Internet Protocol)所在的網(wǎng)絡(luò)層有兩個(gè),分別是pod之間的網(wǎng)絡(luò)和主機(jī)之間的網(wǎng)絡(luò)封裝。
紅色框選的是兩個(gè)pod所在的宿主機(jī),藍(lán)色框選的是兩個(gè)pod的ip,src表示發(fā)起ping操作的pod所在的宿主機(jī)ip以及發(fā)起ping操作的pod的ip,dst表示被ping的pod所在的宿主機(jī)ip及被ping的pod的ip
根據(jù)數(shù)據(jù)包的封裝順序,應(yīng)該是在demo-hello-perf ping demo-hello-sit的ICMP包外面多封裝了一層主機(jī)之間的數(shù)據(jù)包。
可以看到每個(gè)數(shù)據(jù)報(bào)文共有兩個(gè)IP網(wǎng)絡(luò)層,內(nèi)層是Pod容器之間的IP網(wǎng)絡(luò)報(bào)文,外層是宿主機(jī)節(jié)點(diǎn)的網(wǎng)絡(luò)報(bào)文(2個(gè)node節(jié)點(diǎn))。之所以要這樣做是因?yàn)閠unl0是一個(gè)隧道端點(diǎn)設(shè)備,在數(shù)據(jù)到達(dá)時(shí)要加上一層封裝,便于發(fā)送到對(duì)端隧道設(shè)備中。
兩層封包的具體內(nèi)容如下:
Pod間的通信經(jīng)由IPIP的三層隧道轉(zhuǎn)發(fā),相比較VxLAN的二層隧道來(lái)說(shuō),IPIP隧道的開(kāi)銷(xiāo)較小,但其安全性也更差一些。
七、pod到svc的訪問(wèn)
查看service
- # kubectl get svc -o wide -n paas | grep hello
- demo-hello-perf ClusterIP 10.10.255.18 <none> 8080/TCP 10d appEnv=perf,appName=demo-hello
- demo-hello-sit ClusterIP 10.10.48.254 <none> 8080/TCP 10d appEnv=sit,appName=demo-hello
在pod demo-hello-sit 的宿主機(jī)上抓包
- # tcpdump -i eth0 -nn -w svc.cap
- tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
測(cè)試訪問(wèn),在demo-hello-sit中curl demo-hello-perf的svc的地址和端口
- root@demo-hello-perf-d84bffcb8-7fxqj:/# curl -I http://10.10.48.254:8080/actuator/health
- HTTP/1.1 200
- Content-Type: application/vnd.spring-boot.actuator.v3+json
- Transfer-Encoding: chunked
- Date: Fri, 30 Apr 2021 01:42:56 GMT
- root@demo-hello-perf-d84bffcb8-7fxqj:/# curl -I http://10.10.48.254:8080/actuator/health
- HTTP/1.1 200
- Content-Type: application/vnd.spring-boot.actuator.v3+json
- Transfer-Encoding: chunked
- Date: Fri, 30 Apr 2021 01:42:58 GMT
- root@demo-hello-perf-d84bffcb8-7fxqj:/# curl -I http://10.10.48.254:8080/actuator/health
- HTTP/1.1 200
- Content-Type: application/vnd.spring-boot.actuator.v3+json
- Transfer-Encoding: chunked
- Date: Fri, 30 Apr 2021 01:42:58 GMT
結(jié)束抓包,下載svc.cap文件放到wireshark中打開(kāi)查看

可以看到wireshark中Src和Dst的結(jié)果。任然是和上面pod中訪問(wèn)pod的ip地址一樣。這里Src和Dst任然是兩個(gè)pod的宿主機(jī)的內(nèi)網(wǎng)ip和兩個(gè)pod自己的ip地址。是用ipip的方式進(jìn)行通信的。
通過(guò)以上例子演示,應(yīng)該就看明白了IPIP網(wǎng)絡(luò)模式的通信方式了吧!