2020 還沒來得及品味就即将過去一個季度,願剩下的時光不被辜負。進入正題,docker container是單程序模式,能夠解決一些單一的問題,在現實中,我們常常需要多個程序放在一個「盒子」裡、或者多個節點共同完成通信過程,接下來,說下這個過程的網絡通信是如何實作的?
1、docker 網絡模式
可以通過如下指令行檢視docker網絡模式
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c250329fad3c bridge bridge local
c7c3d1f77969 compose_extnetwork bridge local
199b85fbf2fa host host local
b488be9da3d6 none null local
複制
- 共享主機網絡模式 - host
host指的是共享主機的網絡和端口,但是破壞了 container 的隔離性;
- 無網絡模式 - none
其中無網絡模式是指加入此模式下的容器都不能通信,比較雞肋;
- 自定義
自定義主要用于實作DNS解析和服務發現,特殊場景下定制使用;
- 預設網絡模式 - bridge
網橋是 docker 預設網絡模式,也是平時用的最多的一種,這裡主要對 docker 的 bridge 模式做詳細講解。
2、docker 橋接如何實作同一個主控端不同容器之間的通信
其實主要用到兩個技術知識點:
- docker啟動後建立名為docker0的虛拟網橋。
- 容器啟動時在主機上建立一對虛拟網卡veth pair裝置。這一對虛拟裝置完成一組資料完整流通的鍊路,資料從一個裝置進入,從另一個裝置出來。容器中重命名為eth0,主控端上的以veth*顯示并插在docker0網橋上。可以通過如下指令檢視,docker0上插了 veth42f3f11 和 vethe8589bd 兩張虛拟裝置,見(a)圖。
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
br-c7c3d1f77969 8000.02429160f0dd no
docker0 8000.02420a13dd3a no veth42f3f11
vethe8589bd
複制

(a)
那麼你可能會有疑問,多個容器之間又是如何通信的呢?如下圖所示:
(b)
其原理也非常簡單,如圖(b)所示,container1 ping container2(172.0.0.3)網絡時,同一主控端中的兩個容器網絡預設是互通的,其中docker0扮演二層交換機的角色。
通過指令檢視container路由資訊表,如下所示:
bash-4.4# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 * 255.255.0.0 U 0 0 0 eth0
bash-4.4#
複制
container1 通路172.17.0.3比對到第二條路由規則;網關是0.0.0.0,這是一條直連規則,意思是比對到該規則的網絡位址經過本機eth0網卡,再通過二層網絡doceker0 直接發送到目的主機。docker0之是以能夠做到從veth虛拟裝置中接受資料和發送資料,是因為veth相當于docker0網橋的從裝置,故docker0能夠直接處理來自于veth上網絡資料包,進而直接轉發到container2,就完成從一個容器到另外一個容器的通信。見(b)圖所示。
如果通路外部網絡,也非常簡單,資料包先經過docker0網卡,根據主控端路由規則連接配接到eth0網卡,轉發到外部網絡。見(c)圖所示。
(c)
docker 在預設網絡設定情況下,節點A 的docker0 跟節點B 的docker0 沒有任何關聯,網絡也是不通的,這就導緻不能滿足我們跨節點通信要求。Kubernetes 的 Pod 又是通過何種方式實作的呢?
3、pod 通信機制
如果要說明 pod 的通信機制,要從一個鏡像說起,在 kubectl 安裝kubernetes 的時候一定會看到 k8s.gcr.io/pause 這個鏡像,不知道有沒有疑問,這個到底是幹嘛的?其它幾個鏡像顧名思義,但是這個【暫停】是什麼?沒錯,他就是用來 hold 一個 Pod 内部多個 Container 網絡通信。
如果在計算節點上運作 docker ps 指令
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c250329fad3c bridge bridge local
c7c3d1f77969 compose_extnetwork bridge local
199b85fbf2fa host host local
b488be9da3d6 none null local
複制
如上所示,你可以看到在建立 nginx pod 過程中,不僅建立了一個nginx 容器,并附帶了一個 pause 容器,而且 pause 容器是在 nginx容器之前建立的,這個被暫停的容器把所有的容器收納到一起,一個基礎容器,唯一目的就是儲存所有的命名空間。容器中 pod 共享同一個 IP 位址。故同一個 Pod 中 Container 可以做到直接通過 localhost 直接通信,那麼同一個節點多個 Pod 之間如何通信的呢?
(d)
pause 容器啟動之前,會為容器建立虛拟一對 ethernet 接口,一個保留在主控端 vethxxx(插在網橋上),一個保留在容器網絡命名空間内,并重命名為eth0。兩個虛拟接口的兩端,從一端進入,另一端出來。任何 Pod 連接配接到該網橋的 Pod 都可以收發資料。如(d)圖所示。
4、跨 node pod 通信
跨節點 Pod 通信,相當于建立一個整個叢集公用的【 網橋 】然後把叢集中所有的 Pod 連接配接起來,就可以通信了。
(e)
其中跨整個叢集的 Pod ip 是唯一的,當封包從一個節點轉發到另外一個節點時,封包首先通過 veth,然後通過網橋,轉發到實體擴充卡網卡,最後轉發到其它節點的虛拟網橋,進而到達 veth 目标容器。如(e)圖所示。
其實作方式有 Flannel、calico、weave 等。
注意 k8s 的網橋跟 docker0 網橋功能類似,但是 k8s 并沒有複用 docker0 網橋,其原因是 Kubernetes 為了連接配接 infra 容器更加友善,而是重新實作了 CNI 網絡接口功能,它允許網絡插件使用 CNI 接口,比如 flannel,它本身實作也經過幾個過程,其本質上來說,是基于「隧道」機制實作。示意圖(f)所示:
(f)
5、總結
本文由淺到深的講解了 docker 網絡模式實作以及 Kubernetes Pod 跨節點之間通信原理和實作方式。簡單介紹了dockers、Kubernetes網絡通信方式,其内部通過網卡、回環裝置、路由表、iptables等實作,如果你是個喜歡深究的人,可以研究下組網過程。