[Kubernetes]淺談容器網絡

Veth Pair

這部份內容主要介紹一個設備: Veth Pair .
做爲一個容器,它能夠聲明直接使用宿主機的網絡棧,即:不開啓 Network Namespace .在這種狀況下,這個容器啓動後,直接監聽的就是宿主機的 80 端口.
像這樣直接使用宿主機網絡棧的方式,雖然能夠爲容器提供良好的網絡性能,但也會不可避免地引入共享網絡資源的問題,好比端口衝突.因此,在大多數狀況下,咱們都但願容器進程能使用本身 Network Namespace 裏的網絡棧,即:擁有屬於本身的 IP 地址和端口.
可是這個時候,就有另一個問題了:這個別隔離的容器進程,該如何跟其餘 Network Namespace 裏的容器進程進行交互呢?
最直接的辦法,就是把它們用一根網線鏈接起來;而若是想要實現多臺主機之間的通訊,那就須要用網線,把它們鏈接在一臺交換機上.
在 Linux 中,可以起到虛擬交換機做用的網絡設備,是網橋( Bridge ).它是一個工做在數據鏈路層( Data Link )的設備,主要功能是根據 MAC 地址學習來將數據包轉發到網橋的不一樣端口( Port )上.
爲了實現這個目的, Docker 項目會默認在宿主機上建立一個名叫 docker0 的網橋,凡是鏈接在 docker0 網橋上的容器,均可以經過它來進行通訊.
可是,咱們又該如何把這些容器"鏈接"到 docker0 網橋上呢?此時,咱們就須要使用一種名叫 Veth Pair 的虛擬設備了.
Veth Pair 設備的特色是:它被建立出來後,老是以兩張虛擬網卡( Veth Peer )的形式成對出現的.而且,從其中一個"網卡"發出的數據包,能夠直接出如今與它對應的另外一張"網卡"上,哪怕這兩個"網卡"在不一樣的 Network Namespace 裏面.這樣,用來鏈接不一樣 Network Namespace 的"網線"就成爲了可能.
nginx

舉個例子

好比,如今咱們啓動了一個叫作 nginx-1 的容器:web

$ docker run –d --name nginx-1 nginx

而後咱們能夠進入到這個容器中,查看一下它的網絡設備:docker

# 在宿主機上
$ docker exec -it nginx-1 /bin/bash
# 在容器裏
root@2b3c181aecf1:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 0.0.0.0
        inet6 fe80::42:acff:fe11:2  prefixlen 64  scopeid 0x20<link>
        ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
        RX packets 364  bytes 8137175 (7.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 281  bytes 21161 (20.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        
$ 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      0.0.0.0         255.255.0.0     U     0      0        0 eth0

能夠看到,在這個容器中,有一張叫作 eth0 的網卡,它正是一個 Veth Pair 設備在容器裏的這一端.經過 route 命令,能夠查看到 nginx-1 容器的路由表,可以看到,這個 eth0 網卡是這個容器裏的默認路由設備;全部對 172.17.0.0/16 網段的請求,也會被交給 eth0 來處理(第二條 172.17.0.0 路由規則)
而這個 Veth Pair 設備的另外一端,則在宿主機上.能夠經過查看宿主機的網絡設備看到它,以下所示:
bash

# 在宿主機上
$ ifconfig
...
docker0   Link encap:Ethernet  HWaddr 02:42:d8:e4:df:c1  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:d8ff:fee4:dfc1/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:309 errors:0 dropped:0 overruns:0 frame:0
          TX packets:372 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:0 
          RX bytes:18944 (18.9 KB)  TX bytes:8137789 (8.1 MB)
veth9c02e56 Link encap:Ethernet  HWaddr 52:81:0b:24:3d:da  
          inet6 addr: fe80::5081:bff:fe24:3dda/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:288 errors:0 dropped:0 overruns:0 frame:0
          TX packets:371 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:0 
          RX bytes:21608 (21.6 KB)  TX bytes:8137719 (8.1 MB)
          
$ brctl show
bridge name bridge id  STP enabled interfaces
docker0  8000.0242d8e4dfc1 no  veth9c02e56

經過 ifconfig 命令的輸出,咱們能夠看到, nginx-1 容器對應的 Veth Pair 設備,在宿主機上是一張虛擬網卡,它的名字是: veth9c02e56 .而且,經過 brctl show 的輸出,能夠看到這張網卡被"插"在了 docker0 上.
若是此時,咱們在這臺宿主機上啓動另外一個 Docker 容器,好比 nginx-2 :
網絡

$ docker run –d --name nginx-2 nginx
$ brctl show
bridge name bridge id  STP enabled interfaces
docker0  8000.0242d8e4dfc1 no  veth9c02e56
       vethb4963f3

會發現,有一個新的,名叫 vethb4963f3 的虛擬網卡,也被"插"在了 docker0 網橋上.此時,若是在 nginx-1 容器中 ping 一下 nginx-2 容器的 IP 地址( 172.17.0.3 ),就會發現,在同一宿主機上的兩個容器,默認就是相互連通的.
以上整個流程,來一張示意圖:(這樣能夠更清楚一些)
在這裏插入圖片描述
此時,應該就能夠理解了,在默認狀況下,被限制在 Network Namespace 裏的容器進程,其實是經過 Veth Pair 設備 + 宿主機網橋的方式,實現了和其餘容器的數據交換.
因此,若是遇到容器連不通"外網"時,第一反應應該是先試試 docker0 網橋能不能 ping 通,而後查看一下跟 docker0 和 Veth Pair 設備相關的 iptables 規則是否是有異常,通常就可以找到問題的答案.
以上介紹的是單機容器網絡的實現原理和 docker0 網橋的做用.在這裏的關鍵在於,容器若是想要和外界進行通訊,它發出的 IP 包就必須從它的 Network Namespace 裏出來,來到宿主機上,而解決這個問題的方法就是:爲容器建立一個一端在容器裏充當默認網卡,另外一端在宿主機上的 Veth Pair 設備.
svg

思惟擴展

看到這裏,估計你會有另一個疑問,那我若是想要"跨主機通訊"呢?
在 Docker 的默認配置下,一臺宿主機上的 docker0 網橋,和其餘宿主機上的 docker0 網橋,沒有任何關聯,它們互相之間就沒辦法連通.
可是若是咱們經過軟件的方式,建立一個整個集羣"公用"的網橋呢?而後把集羣裏全部的容器都鏈接到這個網橋上,是否是就能夠相互通訊了?
一點兒毛病都沒.
這樣,咱們就能夠畫出以下的示意圖:
在這裏插入圖片描述
經過示意圖,可以看到,構建這種容器網絡的核心在於:咱們須要在已有的宿主機網絡上,再經過軟件構建出一個覆蓋在已有宿主機網絡之上的,能夠把全部容器連通在一塊兒的虛擬網絡.這種技術就是: Overlay Network (覆蓋網絡).
可是這只是理論上,落實到實際,仍是有些出入的.
後面我看時間和精力,若是可能,再寫一篇關於"跨主機"通訊的講解文章(感受有些難度,我不肯定可以講清楚,可是儘可能)
oop

以上內容來自我學習<深刻剖析Kubernetes>專欄文章以後的一些看法,有偏頗之處,還望指出.
感謝您的閱讀~
性能

相關文章
相關標籤/搜索