Calico 是一個純三層的數據中心網絡方案,並且無縫集成像 OpenStack 這種 Iaas 雲架構,可以提供可控的 VM、容器、裸機之間的 IP 通訊。爲何說它是純三層呢?由於全部的數據包都是經過路由的形式找到對應的主機和容器的,而後經過 BGP 協議來將全部路由同步到全部的機器或數據中心,從而完成整個網絡的互聯。緩存
簡單來講,Calico 在主機上建立了一堆的 veth pair,其中一端在主機上,另外一端在容器的網絡命名空間裏,而後在容器和主機中分別設置幾條路由,來完成網絡的互聯。服務器
下面咱們經過具體的例子來幫助你們理解 Calico 網絡的通訊原理。任意選擇 k8s 集羣中的一個節點做爲實驗節點,進入容器 A,查看容器 A 的 IP 地址:網絡
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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
3: eth0@if771: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1440 qdisc noqueue state UP
link/ether 66:fb:34:db:c9:b4 brd ff:ff:ff:ff:ff:ff
inet 172.17.8.2/32 scope global eth0
valid_lft forever preferred_lft forever複製代碼
這裏容器獲取的是 /32 位主機地址,表示將容器 A 做爲一個單點的局域網。架構
瞄一眼容器 A 的默認路由:tcp
$ ip route
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link複製代碼
如今問題來了,從路由表能夠知道 169.254.1.1
是容器的默認網關,但卻找不到任何一張網卡對應這個 IP 地址,這是個什麼鬼?oop
莫慌,先回憶一下,當一個數據包的目的地址不是本機時,就會查詢路由表,從路由表中查到網關後,它首先會經過 ARP
得到網關的 MAC 地址,而後在發出的網絡數據包中將目標 MAC 改成網關的 MAC,而網關的 IP 地址不會出如今任何網絡包頭中。也就是說,沒有人在意這個 IP 地址到底是什麼,只要能找到對應的 MAC 地址,能響應 ARP 就好了。測試
想到這裏,咱們就能夠繼續往下進行了,能夠經過 ip neigh
命令查看一下本地的 ARP 緩存:spa
$ ip neigh
169.254.1.1 dev eth0 lladdr ee:ee:ee:ee:ee:ee REACHABLE複製代碼
這個 MAC 地址應該是 Calico 硬塞進去的,並且還能響應 ARP。但它到底是怎麼實現的呢?代理
咱們先來回想一下正常狀況,內核會對外發送 ARP 請求,詢問整個二層網絡中誰擁有 169.254.1.1
這個 IP 地址,擁有這個 IP 地址的設備會將本身的 MAC地址返回給對方。但如今的狀況比較尷尬,容器和主機都沒有這個 IP 地址,甚至連主機上的端口 calicba2f87f6bb
,MAC 地址也是一個無用的 ee:ee:ee:ee:ee:ee
。按道理容器和主機網絡根本就沒法通訊纔對呀!因此 Calico 是怎麼作到的呢?code
這裏我就不繞彎子了,實際上 Calico 利用了網卡的代理 ARP 功能。代理 ARP 是 ARP 協議的一個變種,當 ARP 請求目標跨網段時,網關設備收到此 ARP 請求,會用本身的 MAC 地址返回給請求者,這即是代理 ARP(Proxy ARP)。舉個例子:
上面這張圖中,電腦發送 ARP 請求服務器 8.8.8.8 的 MAC 地址,路由器(網關)收到這個請求時會進行判斷,因爲目標 8.8.8.8 不屬於本網段(即跨網段),此時便返回本身的接口 MAC 地址給 PC,後續電腦訪問服務器時,目標 MAC 直接封裝爲 MAC254。
如今咱們知道,Calico 本質上仍是利用了代理 ARP 撒了一個「善意的謊話」,下面咱們來確認一下。
查看宿主機的網卡信息和路由信息:
$ ip addr
...
771: calicba2f87f6bb@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 14
inet6 fe80::ecee:eeff:feee:eeee/64 scope link
valid_lft forever preferred_lft forever
...
$ ip route
...
172.17.8.2 dev calicba2f87f6bb scope link
...複製代碼
查看是否開啓代理 ARP:
$ cat /proc/sys/net/ipv4/conf/calicba2f87f6bb/proxy_arp
1複製代碼
若是還不放心,能夠經過 tcpdump 抓包驗證一下:
$ tcpdump -i calicba2f87f6bb -e -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on calicba2f87f6bb, link-type EN10MB (Ethernet), capture size 262144 bytes
14:27:13.565539 ee:ee:ee:ee:ee:ee > 0a:58:ac:1c:ce:12, ethertype IPv4 (0x0800), length 4191: 10.96.0.1.443 > 172.17.8.2.36180: Flags [P.], seq 403862039:403866164, ack 2023703985, win 990, options [nop,nop,TS val 331780572 ecr 603755526], length 4125
14:27:13.565613 0a:58:ac:1c:ce:12 > ee:ee:ee:ee:ee:ee, ethertype IPv4 (0x0800), length 66: 172.17.8.2.36180 > 10.96.0.1.443: Flags [.], ack 4125, win 2465, options [nop,nop,TS val 603758497 ecr 331780572], length 0複製代碼
總結:
既然咱們已經掌握了 Calico 的組網原理,接下來就能夠手動模擬驗證了。架構如圖所示:
先在 Host0 上執行如下命令:
$ ip link add veth0 type veth peer name eth0
$ ip netns add ns0
$ ip link set eth0 netns ns0
$ ip netns exec ns0 ip a add 10.20.1.2/24 dev eth0
$ ip netns exec ns0 ip link set eth0 up
$ ip netns exec ns0 ip route add 169.254.1.1 dev eth0 scope link
$ ip netns exec ns0 ip route add default via 169.254.1.1 dev eth0
$ ip link set veth0 up
$ ip route add 10.20.1.2 dev veth0 scope link
$ ip route add 10.20.1.3 via 192.168.1.16 dev ens192
$ echo 1 > /proc/sys/net/ipv4/conf/veth0/proxy_arp複製代碼
在 Host1 上執行如下命令:
$ ip link add veth0 type veth peer name eth0
$ ip netns add ns1
$ ip link set eth0 netns ns1
$ ip netns exec ns1 ip a add 10.20.1.3/24 dev eth0
$ ip netns exec ns1 ip link set eth0 up
$ ip netns exec ns1 ip route add 169.254.1.1 dev eth0 scope link
$ ip netns exec ns1 ip route add default via 169.254.1.1 dev eth0
$ ip link set veth0 up
$ ip route add 10.20.1.3 dev veth0 scope link
$ ip route add 10.20.1.2 via 192.168.1.32 dev ens192
$ echo 1 > /proc/sys/net/ipv4/conf/veth0/proxy_arp複製代碼
網絡連通性測試:
# Host0
$ ip netns exec ns1 ping 10.20.1.3
PING 10.20.1.3 (10.20.1.3) 56(84) bytes of data.
64 bytes from 10.20.1.3: icmp_seq=1 ttl=62 time=0.303 ms
64 bytes from 10.20.1.3: icmp_seq=2 ttl=62 time=0.334 ms複製代碼
實驗成功!
具體的轉發過程以下:
10.20.1.3 via 192.168.1.16 dev ens192
發送給對端 Host1,若是配置了 BGP,這裏就會看到 proto 協議爲 BIRD。 10.20.1.3 dev veth0 scope link
,將數據包轉發到對應的 veth0 端,從而到達 ns1。