91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

主流Docker網絡的實現原理是什么

發布時間:2021-11-19 13:41:24 來源:億速云 閱讀:153 作者:小新 欄目:云計算

這篇文章主要介紹了主流Docker網絡的實現原理是什么,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

一、容器網絡簡介

容器網絡主要解決兩大核心問題:一是容器的IP地址分配,二是容器之間的相互通信。本文重在研究第二個問題并且主要研究容器的跨主機通信問題。

實現容器跨主機通信的最簡單方式就是直接使用host網絡,這時由于容器IP就是宿主機的IP,復用宿主機的網絡協議棧以及underlay網絡,原來的主機能通信,容器也就自然能通信,然而帶來的最直接問題就是端口沖突問題。

因此通常容器會配置與宿主機不一樣的屬于自己的IP地址。由于是容器自己配置的IP,underlay平面的底層網絡設備如交換機、路由器等完全不感知這些IP的存在,也就導致容器的IP不能直接路由出去實現跨主機通信。

要解決如上問題實現容器跨主機通信,主要有如下兩個思路:

  • 思路一:修改底層網絡設備配置,加入容器網絡IP地址的管理,修改路由器網關等,該方式主要和SDN結合。

  • 思路二:完全不修改底層網絡設備配置,復用原有的underlay平面網絡,解決容器跨主機通信,主要有如下兩種方式:

    • Overlay隧道傳輸。把容器的數據包封裝到原主機網絡的三層或者四層數據包中,然后使用原來的網絡使用IP或者TCP/UDP傳輸到目標主機,目標主機再拆包轉發給容器。Overlay隧道如Vxlan、ipip等,目前使用Overlay技術的主流容器網絡如Flannel、Weave等。

    • 修改主機路由。把容器網絡加到主機路由表中,把主機當作容器網關,通過路由規則轉發到指定的主機,實現容器的三層互通。目前通過路由技術實現容器跨主機通信的網絡如Flannel host-gw、Calico等。

本文接下來將詳細介紹目前主流容器網絡的實現原理。

在開始正文內容之前,先引入兩個后續會一直使用的腳本:

第一個腳本為 docker_netns.sh:

  1. #!/bin/bash


  2. NAMESPACE=$1


  3. if [[ -z $NAMESPACE ]]; then

  4.    ls -1 /var/run/docker/netns/

  5.    exit 0

  6. fi


  7. NAMESPACE_FILE=/var/run/docker/netns/${NAMESPACE}


  8. if [[ ! -f $NAMESPACE_FILE ]]; then

  9.    NAMESPACE_FILE=$(docker inspect -f "{{.NetworkSettings.SandboxKey}}" $NAMESPACE 2>/dev/null)

  10. fi


  11. if [[ ! -f $NAMESPACE_FILE ]]; then

  12.    echo "Cannot open network namespace '$NAMESPACE': No such file or directory"

  13.    exit 1

  14. fi


  15. shift


  16. if [[ $# -lt 1 ]]; then

  17.    echo "No command specified"

  18.    exit 1

  19. fi


  20. nsenter --net=${NAMESPACE_FILE} $@

該腳本通過指定容器id、name或者namespace快速進入容器的network namespace并執行相應的shell命令。

如果不指定任何參數,則列舉所有Docker容器相關的network namespaces。

  1. # ./docker_netns.sh # list namespaces

  2. 4-a4a048ac67

  3. abe31dbbc394

  4. default


  5. # ./docker_netns.sh busybox ip addr # Enter busybox namespace

  6. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000

  7.    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

  8.    inet 127.0.0.1/8 scope host lo

  9.       valid_lft forever preferred_lft forever

  10. 354: eth0@if355: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default

  11.    link/ether 02:42:c0:a8:64:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0

  12.    inet 192.168.100.2/24 brd 192.168.100.255 scope global eth0

  13.       valid_lft forever preferred_lft forever

  14. 356: eth2@if357: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default

  15.    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 1

  16.    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth2

  17.       valid_lft forever preferred_lft forever

另一個腳本為 find_links.sh

  1. #!/bin/bash


  2. DOCKER_NETNS_SCRIPT=./docker_netns.sh

  3. IFINDEX=$1

  4. if [[ -z $IFINDEX ]]; then

  5.    for namespace in $($DOCKER_NETNS_SCRIPT); do

  6.        printf "e[1;31m%s: e[0m
    " $namespace

  7.        $DOCKER_NETNS_SCRIPT $namespace ip -c -o link

  8.        printf "
    "

  9.    done

  10. else

  11.    for namespace in $($DOCKER_NETNS_SCRIPT); do

  12.        if $DOCKER_NETNS_SCRIPT $namespace ip -c -o link | grep -Pq "^$IFINDEX: "; then

  13.            printf "e[1;31m%s: e[0m
    " $namespace

  14.            $DOCKER_NETNS_SCRIPT $namespace ip -c -o link | grep -P "^$IFINDEX: ";

  15.            printf "
    "

  16.        fi

  17.    done

  18. fi

該腳本根據ifindex查找虛擬網絡設備所在的namespace:

# ./find_links.sh 354abe31dbbc394:354: eth0@if355: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group default     link/ether 02:42:c0:a8:64:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0

該腳本的目的是方便查找veth的對端所在的namespace位置。如果不指定ifindex,則列出所有namespaces的link設備。

二、Docker原生的Overlay

Laurent Bernaille在DockerCon2017上詳細介紹了Docker原生的Overlay網絡實現原理,作者還總結了三篇干貨文章一步一步剖析Docker網絡實現原理,最后還教大家一步一步從頭開始手動實現Docker的Overlay網絡,這三篇文章為:

  • Deep dive into docker overlay networks part 1

  • Deep dive into docker overlay networks part 2

  • Deep dive into docker overlay networks part 3

建議感興趣的讀者閱讀,本節也大量參考了如上三篇文章的內容。

2.1 Overlay網絡環境

測試使用兩個Node節點:

Node名主機IP
node-1192.168.1.68
node-2192.168.1.254

首先創建一個overlay網絡:

docker network create -d overlay --subnet 10.20.0.0/16 overlay

在兩個節點分別創建兩個busybox容器:

docker run -d --name busybox --net overlay busybox sleep 36000

容器列表如下:

Node名主機IP容器IP
node-1192.168.1.6810.20.0.3/16
node-2192.168.1.25410.20.0.2/16

主流Docker網絡的實現原理是什么

我們發現容器有兩個IP,其中eth0 10.20.0.0/16為我們創建的Overlay網絡ip,兩個容器能夠互相ping通。而不在同一個node的容器IP eth2都是172.18.0.2,因此172.18.0.0/16很顯然不能用于跨主機通信,只能用于單個節點容器通信。

2.2 容器南北流量

這里的南北流量主要是指容器與外部通信的流量,比如容器訪問互聯網。

我們查看容器的路由:

# docker exec busybox-node-1 ip rdefault via 172.18.0.1 dev eth210.20.0.0/16 dev eth0 scope link  src 10.20.0.3172.18.0.0/16 dev eth2 scope link  src 172.18.0.2

由此可知容器默認網關為172.18.0.1,也就是說容器是通過eth2出去的:

# docker exec busybox-node-1 ip link show eth277: eth2@if78: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff# ./find_links.sh 78default:78: vethf2de5d4@if77: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP mode DEFAULT group defaultlink/ether 2e:6a:94:6a:09:c5 brd ff:ff:ff:ff:ff:ff link-netnsid 1

通過 find_links.sh腳本查找ifindex為78的link在默認namespace中,并且該link的master為 docker_gwbridge,也就是說該設備掛到了 docker_gwbridgebridge。

# brctl showbridge name     bridge id               STP enabled     interfacesdocker0         8000.02427406ba1a       nodocker_gwbridge         8000.0242bb868ca3       no              vethf2de5d4

而 172.18.0.1正是bridge docker_gwbridge的IP,也就是說 docker_gwbridge是該節點的所有容器的網關。

由于容器的IP是172.18.0.0/16私有IP地址段,不能出公網,因此必然通過NAT實現容器IP與主機IP地址轉換,查看iptables nat表如下:

# iptables-save -t nat  | grep -- '-A POSTROUTING'-A POSTROUTING -s 172.18.0.0/16 ! -o docker_gwbridge -j MASQUERADE

由此可驗證容器是通過NAT出去的。

我們發現其實容器南北流量用的其實就是Docker最原生的bridge網絡模型,只是把 docker0換成了 docker_gwbridge。如果容器不需要出互聯網,創建Overlay網絡時可以指定 --internal參數,此時容器只有一個Overlay網絡的網卡,不會創建eth2。

2.3 容器東西向流量

容器東西流量指容器之間的通信,這里特指跨主機的容器間通信。

顯然容器是通過eth0實現與其他容器通信的:

  1. # docker exec busybox-node-1 ip link show eth0

  2. 75: eth0@if76: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue

  3.    link/ether 02:42:0a:14:00:03 brd ff:ff:ff:ff:ff:ff


  4. # ./find_links.sh 76

  5. 1-19c5d1a7ef:

  6. 76: veth0@if75: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP mode DEFAULT group default     link/ether 6a:ce:89:a2:89:4a brd ff:ff:ff:ff:ff:ff link-netnsid 1

eth0的對端設備ifindex為76,通過 find_links.sh腳本查找ifindex 76在 1-19c5d1a7ef namespace下,名稱為 veth0,并且master為br0,因此veth0掛到了br0 bridge下。

通過 docker_netns.sh腳本可以快速進入指定的namespace執行命令:

  1. # ./docker_netns.sh 1-19c5d1a7ef ip link show veth0

  2. 76: veth0@if75: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP mode DEFAULT group default

  3.    link/ether 6a:ce:89:a2:89:4a brd ff:ff:ff:ff:ff:ff link-netnsid 1


  4. # ./docker_netns.sh 1-19c5d1a7ef brctl show

  5. bridge name     bridge id               STP enabled     interfaces

  6. br0             8000.6ace89a2894a       no              veth0

  7.                                                        vxlan0

可見除了veth0,bridge還綁定了vxlan0:

./docker_netns.sh 1-19c5d1a7ef ip -c -d link show vxlan074: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UNKNOWN mode DEFAULT group default    link/ether 96:9d:64:39:76:4e brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 1    vxlan id 256 srcport 0 0 dstport 4789 proxy l2miss l3miss ttl inherit ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx...

vxlan0是一個VxLan虛擬網絡設備,因此可以推斷Docker Overlay是通過vxlan隧道實現跨主機通信的。這里直接引用Deep dive into docker overlay networks part 1的圖:

主流Docker網絡的實現原理是什么

圖中192.168.0.0/16對應前面的10.20.0.0/16網段。

2.4 ARP代理

如前面所述,跨主機的兩個容器雖然是通過Overlay通信的,但容器自己卻不知道,他們只認為彼此都在一個二層中(同一個子網),或者說大二層。我們知道二層是通過MAC地址識別對方的,通過ARP協議廣播學習獲取IP與MAC地址轉換。當然通過Vxlan隧道廣播ARP包理論上也沒有問題,問題是該方案將導致廣播包過多,廣播的成本會很大。

和OpenStack Neutron的L2 Population原理一樣,Docker也是通過ARP代理+靜態配置解決ARP廣播問題。我們知道,雖然Linux底層除了通過自學習方式外無法知道目標IP的MAC地址是什么,但是應用卻很容易獲取這些信息,比如Neutron的數據庫中就保存著Port信息,Port中就有IP和MAC地址。Docker也一樣會把endpoint信息保存到KV數據庫中,如etcd:

主流Docker網絡的實現原理是什么

有了這些數據完全可以實現通過靜態配置的方式填充IP和MAC地址表(neigh表)替換使用ARP廣播的方式。因此vxlan0還負責了本地容器的ARP代理:

./docker_netns.sh  2-19c5d1a7ef ip -d -o link show vxlan0 | grep proxy_arp

而vxlan0代理回復時直接查找本地的neigh表回復即可,而本地neigh表則是Docker靜態配置,可查看Overlay網絡namespaced neigh表:

# ./docker_netns.sh 3-19c5d1a7ef ip neigh20.20.0.3 dev vxlan0 lladdr 02:42:0a:14:00:03 PERMANENT10.20.0.4 dev vxlan0 lladdr 02:42:0a:14:00:04 PERMANENT

記錄中的 PERMANENT說明是靜態配置而不是通過學習獲取的,IP 10.20.0.3、10.20.0.4正是另外兩個容器的IP地址。

每當有新的容器創建時,Docker通過Serf以及Gossip協議通知節點更新本地neigh ARP表。

2.5 VTEP表靜態配置

前面介紹的ARP代理屬于L2層問題,而容器的數據包最終還是通過Vxlan隧道傳輸的,那自然需要解決的問題是這個數據包應該傳輸到哪個node節點?如果只是兩個節點,創建vxlan隧道時可以指定本地ip(local IP)和對端IP(remote IP)建立點對點通信,但實際上顯然不可能只有兩個節點。

我們不妨把Vxlan出去的物理網卡稱為VTEP(VXLAN Tunnel Endpoint),它會有一個可路由的IP,即Vxlan最終封裝后的外層IP。通過查找VTEP表決定數據包應該傳輸到哪個remote VTEP:

容器MAC地址Vxlan IDRemote VTEP
02:42:0a:14:00:03256192.168.1.254
02:42:0a:14:00:04256192.168.1.245
.........

VTEP表和ARP表類似,也可以通過廣播洪泛的方式學習,但顯然同樣存在性能問題,實際上也很少使用這種方案。在硬件SDN中通常使用BGP EVPN技術實現Vxlan的控制平面。

而Docker解決的辦法和ARP類似,通過靜態配置的方式填充VTEP表,我們可以查看容器網絡namespace的轉發表(Forward database,簡稱fdb),

./docker_netns.sh 3-19c5d1a7ef bridge fdb...02:42:0a:14:00:04 dev vxlan0 dst 192.168.1.245 link-netnsid 0 self permanent02:42:0a:14:00:03 dev vxlan0 dst 192.168.1.254 link-netnsid 0 self permanent...

可見MAC地址02:42:0a:14:00:04的對端VTEP地址為192.168.1.245,而02:42:0a:14:00:03的對端VTEP地址為192.168.1.254,兩條記錄都是 permanent,即靜態配置的,而這些數據來源依然是KV數據庫,endpoint中 locator即為容器的node IP。

2.6 總結

容器使用Docker原生Overlay網絡默認會創建兩張虛擬網卡,其中一張網卡通過bridge以及NAT出容器外部,即負責南北流量。另一張網卡通過Vxlan實現跨主機容器通信,為了減少廣播,Docker通過讀取KV數據靜態配置ARP表和FDB表,容器創建或者刪除等事件會通過Serf以及Gossip協議通知Node更新ARP表和FDB表。

三、和Docker Overlay差不多的Weave

weave是weaveworks公司提供的容器網絡方案,實現上和Docker原生Overlay網絡有點類似。

初始化三個節點192.168.1.68、192.168.1.254、192.168.1.245如下:

weave launch --ipalloc-range 172.111.222.0/24 192.168.1.68 192.168.1.254 192.168.1.245

分別在三個節點啟動容器:

# node-1docker run -d --name busybox-node-1 --net weave busybox sleep 3600# node-2docker run -d --name busybox-node-2 --net weave busybox sleep 3600# node-3docker run -d --name busybox-node-3 --net weave busybox sleep 3600

在容器中我們相互ping:

主流Docker網絡的實現原理是什么

從結果發現,Weave實現了跨主機容器通信,另外我們容器有兩個虛擬網卡,一個是Docker原生的橋接網卡eth0,用于南北通信,另一個是Weave附加的虛擬網卡ethwe0,用于容器跨主機通信。

另外查看容器的路由:

# docker exec -t -i busybox-node-$NODE ip rdefault via 172.18.0.1 dev eth0172.18.0.0/16 dev eth0 scope link  src 172.18.0.2172.111.222.0/24 dev ethwe0 scope link  src 172.111.222.128224.0.0.0/4 dev ethwe0 scope link

其中 224.0.0.0/4是一個組播地址,可見Weave是支持組播的,參考Container Multicast Networking: Docker & Kubernetes | Weaveworks.

我們只看第一個容器的ethwe0,VETH對端ifindex為14:

# ./find_links.sh 14default:14: vethwl816281577@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue    master weave state UP mode DEFAULT group default    link/ether de:12:50:59:f0:d9 brd ff:ff:ff:ff:ff:ff link-netnsid 0

可見ethwe0的對端在default namespace下,名稱為 vethwl816281577,該虛擬網卡橋接到 weave bridge下:

# brctl show weavebridge name     bridge id               STP enabled     interfacesweave           8000.d2939d07704b       no              vethwe-bridge                                                        vethwl816281577

weave bridge下除了有 vethwl816281577,還有 vethwe-bridge:

# ip link show vethwe-bridge9: vethwe-bridge@vethwe-datapath: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue    master weave state UP mode DEFAULT group default    link/ether 0e:ee:97:bd:f6:25 brd ff:ff:ff:ff:ff:ff

可見 vethwe-bridge與 vethwe-datapath是一個VETH對,我們查看對端 vethwe-datapath:

# ip -d link show vethwe-datapath8: vethwe-datapath@vethwe-bridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue    master datapath state UP mode DEFAULT group default    link/ether f6:74:e9:0b:30:6d brd ff:ff:ff:ff:ff:ff promiscuity 1    veth    openvswitch_slave addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

vethwe-datapath的master為 datapath,由 openvswitch_slave可知 datapath應該是一個openvswitch bridge,而 vethwe-datapath掛到了 datapath橋下,作為 datapath的port。

為了驗證,通過ovs-vsctl查看:

# ovs-vsctl show96548648-a6df-4182-98da-541229ef7b63    ovs_version: "2.9.2"

使用 ovs-vsctl發現并沒有 datapath這個橋。官方文檔中fastdp how it works中解釋為了提高網絡性能,沒有使用用戶態的OVS,而是直接操縱內核的datapath。使用 ovs-dpctl命令可以查看內核datapath:

# ovs-dpctl showsystem@datapath:        lookups: hit:109 missed:1508 lost:3        flows: 1        masks: hit:1377 total:1 hit/pkt:0.85        port 0: datapath (internal)        port 1: vethwe-datapath        port 2: vxlan-6784 (vxlan: packet_type=ptap)

可見datapath類似于一個OVS bridge設備,負責數據交換,該設備包含三個port:

  • port 0: datapath (internal)

  • port 1: vethwe-datapath

  • port 2: vxlan-6784

除了 vethwe-datapath,還有一個 vxlan-6784,由名字可知這是一個vxlan:

# ip -d link show vxlan-678410: vxlan-6784: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65535 qdisc noqueue    master datapath state UNKNOWN mode DEFAULT group default qlen 1000    link/ether d2:21:db:c1:9b:28 brd ff:ff:ff:ff:ff:ff promiscuity 1    vxlan id 0 srcport 0 0 dstport 6784 nolearning ttl inherit ageing 300 udpcsum noudp6zerocsumtx udp6zerocsumrx external    openvswitch_slave addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

最后Weave的網絡流量圖如下:

主流Docker網絡的實現原理是什么

四、簡單優雅的Flannel

4.1 Flannel簡介

Flannel網絡是目前最主流的容器網絡之一,同時支持overlay(如vxlan)和路由(如host-gw)兩種模式。

Flannel和Weave以及Docker原生overlay網絡不同的是,后者的所有Node節點共享一個子網,而Flannel初始化時通常指定一個16位的網絡,然后每個Node單獨分配一個獨立的24位子網。由于Node都在不同的子網,跨節點通信本質為三層通信,也就不存在二層的ARP廣播問題了。

另外,我認為Flannel之所以被認為非常簡單優雅的是,不像Weave以及Docker Overlay網絡需要在容器內部再增加一個網卡專門用于Overlay網絡的通信,Flannel使用的就是Docker最原生的bridge網絡,除了需要為每個Node配置subnet(bip)外,幾乎不改變原有的Docker網絡模型。

4.2 Flannel Overlay網絡

我們首先以Flannel Overlay網絡模型為例,三個節點的IP以及Flannel分配的子網如下:

Node名主機IP分配的子網
node-1192.168.1.6840.15.43.0/24
node-2192.168.1.25440.15.26.0/24
node-3192.168.1.24540.15.56.0/24

在三個集成了Flannel網絡的Node環境下分別創建一個 busybox容器:

docker run -d --name busybox busybox:latest sleep 36000

容器列表如下:

Node名主機IP容器IP
node-1192.168.1.6840.15.43.2/24
node-2192.168.1.25440.15.26.2/24
node-3192.168.1.24540.15.56.2/24

查看容器namespace的網絡設備:

# ./docker_netns.sh busybox ip -d -c  link416: eth0@if417: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8951 qdisc noqueue state UP mode DEFAULT group default    link/ether 02:42:28:0f:2b:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0    veth addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

和Docker bridge網絡一樣只有一張網卡eth0,eth0為veth設備,對端的ifindex為417.

我們查找下ifindex 417的link信息:

# ./find_links.sh 417default:417: veth2cfe340@if416: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8951 qdisc noqueue master docker0 state UP mode DEFAULT group default     link/ether 26:bd:de:86:21:78 brd ff:ff:ff:ff:ff:ff link-netnsid 0

可見ifindex 417在default namespace下,名稱為 veth2cfe340并且master為docker0,因此掛到了docker0的bridge下。

# brctl showbridge name     bridge id               STP enabled     interfacesdocker0         8000.0242d6f8613e       no              veth2cfe340                                                        vethd1fae9ddocker_gwbridge         8000.024257f32054       no

和Docker原生的bridge網絡沒什么區別,那它是怎么解決跨主機通信的呢?

實現跨主機通信,要么Overlay隧道封裝,要么靜態路由,顯然docker0沒有看出有什么overlay的痕跡,因此只能通過路由實現了。

不妨查看下本地路由如下:

# ip rdefault via 192.168.1.1 dev eth0 proto dhcp src 192.168.1.68 metric 10040.15.26.0/24 via 40.15.26.0 dev flannel.1 onlink40.15.43.0/24 dev docker0 proto kernel scope link src 40.15.43.140.15.56.0/24 via 40.15.56.0 dev flannel.1 onlink...

我們只關心40.15開頭的路由,忽略其他路由,我們發現除了40.15.43.0/24直接通過docker0直連外,其他均路由轉發到了 flannel.1。而40.15.43.0/24為本地Node的子網,因此在同一宿主機的容器直接通過docker0通信即可。

我們查看 flannel.1的設備類型:

413: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8951 qdisc noqueue state UNKNOWN mode DEFAULT group default    link/ether 0e:08:23:57:14:9a brd ff:ff:ff:ff:ff:ff promiscuity 0    vxlan id 1 local 192.168.1.68 dev eth0 srcport 0 0 dstport 8472 nolearning ttl inherit ageing 300     udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

可見 flannel.1是一個Linux Vxlan設備,其中 .1為VNI值,不指定默認為1。

由于不涉及ARP因此不需要proxy參數實現ARP代理,而本節點的容器通信由于在一個子網內,因此直接ARP自己學習即可,不需要Vxlan設備學習,因此有個nolearning參數。

而 flannel.1如何知道對端VTEP地址呢?我們依然查看下轉發表fdb:

bridge fdb | grep flannel.14e:55:ee:0a:90:38 dev flannel.1 dst 192.168.1.245 self permanentda:17:1b:07:d3:70 dev flannel.1 dst 192.168.1.254 self permanent

其中192.168.1.245、192.168.1.254正好是另外兩個Node的IP,即VTEP地址,而 4e:55:ee:0a:90:38以及 da:17:1b:07:d3:70為對端的 flannel.1設備的MAC地址,由于是 permanent表,因此可推測是由flannel靜態添加的,而這些信息顯然可以從etcd獲取:

# for subnet in $(etcdctl ls /coreos.com/network/subnets); do etcdctl get $subnet;done{"PublicIP":"192.168.1.68","BackendType":"vxlan","BackendData":{"VtepMAC":"0e:08:23:57:14:9a"}}{"PublicIP":"192.168.1.254","BackendType":"vxlan","BackendData":{"VtepMAC":"da:17:1b:07:d3:70"}}{"PublicIP":"192.168.1.245","BackendType":"vxlan","BackendData":{"VtepMAC":"4e:55:ee:0a:90:38"}}

因此Flannel的Overlay網絡實現原理簡化如圖:

主流Docker網絡的實現原理是什么

可見除了增加或者減少Node,需要Flannel配合配置靜態路由以及fdb表,容器的創建與刪除完全不需要Flannel干預,事實上Flannel也不需要知道有沒有新的容器創建或者刪除。

4.3 Flannel host-gw網絡

前面介紹Flannel通過Vxlan實現跨主機通信,其實Flannel支持不同的backend,其中指定backend type為host-gw支持通過靜態路由的方式實現容器跨主機通信,這時每個Node都相當于一個路由器,作為容器的網關,負責容器的路由轉發。

需要注意的是,如果使用AWS EC2,使用Flannel host-gw網絡需要禁用MAC地址欺騙功能,如圖:

主流Docker網絡的實現原理是什么

使用OpenStack則最好禁用Neutron的port security功能。

同樣地,我們在三個節點分別創建busybox容器,結果如下:

Node名主機IP容器IP
node-1192.168.1.6840.15.43.2/24
node-2192.168.1.25440.15.26.2/24
node-3192.168.1.24540.15.56.2/24

我們查看192.168.1.68的本地路由:

# ip rdefault via 192.168.1.1 dev eth0 proto dhcp src 192.168.1.68 metric 10040.15.26.0/24 via 192.168.1.254 dev eth040.15.43.0/24 dev docker0 proto kernel scope link src 40.15.43.140.15.56.0/24 via 192.168.1.245 dev eth0...

我們只關心40.15前綴的路由,發現40.15.26.0/24的下一跳為192.168.1.254,正好為node2 IP,而40.15.43.0/24的下一跳為本地docker0,因為該子網就是node所在的子網,40.15.56.0/24的下一跳為192.168.1.245,正好是node3 IP。可見,Flannel通過配置靜態路由的方式實現容器跨主機通信,每個Node都作為路由器使用。

host-gw的方式相對Overlay由于沒有vxlan的封包拆包過程,直接路由就過去了,因此性能相對要好。不過正是由于它是通過路由的方式實現,每個Node相當于是容器的網關,因此每個Node必須在同一個LAN子網內,否則跨子網由于鏈路層不通導致無法實現路由導致host-gw實現不了。

4.4 Flannel利用云平臺路由實現跨主機通信

前面介紹的host-gw是通過修改主機路由表實現容器跨主機通信,如果能修改主機網關的路由當然也是沒有問題的,尤其是和SDN結合方式動態修改路由。

目前很多云平臺均實現了自定義路由表的功能,比如OpenStack、AWS等,Flannel借助這些功能實現了很多公有云的VPC后端,通過直接調用云平臺API修改路由表實現容器跨主機通信,比如阿里云、AWS、Google云等,不過很可惜官方目前好像還沒有實現OpenStack Neutron后端。

下面以AWS為例,創建了如下4臺EC2虛擬機:

  • node-1: 197.168.1.68/24

  • node-2: 197.168.1.254/24

  • node-3: 197.168.1.245/24

  • node-4: 197.168.0.33/24

注意第三臺和其余兩臺不在同一個子網。

三臺EC2均關聯了flannel-role,flannel-role關聯了flannel-policy,policy的權限如下:

{    "Version": "2012-10-17",    "Statement": [        {            "Sid": "VisualEditor0",            "Effect": "Allow",            "Action": [                "ec2:DescribeInstances",                "ec2:CreateRoute",                "ec2:DeleteRoute",                "ec2:ModifyInstanceAttribute",                "ec2:DescribeRouteTables",                "ec2:ReplaceRoute"            ],            "Resource": "*"        }    ]}

即EC2實例需要具有修改路由表等相關權限。

之前一直很疑惑AWS的role如何與EC2虛擬機關聯起來的。換句話說,如何實現虛擬機無需配置Key和Secretd等認證信息就可以直接調用AWS API,通過awscli的 --debug信息可知awscli首先通過metadata獲取role信息,再獲取role的Key和Secret:

主流Docker網絡的實現原理是什么

關于AWS如何知道調用metadata的是哪個EC2實例,可參考之前的文章OpenStack虛擬機如何獲取metadata.

另外所有EC2實例均禁用了MAC地址欺騙功能(Change Source/Dest Check),安全組允許flannel網段40.15.0.0/16通過,另外增加了如下iptables規則:

iptables -I FORWARD --dest 40.15.0.0/16 -j ACCEPTiptables -I FORWARD --src 40.15.0.0/16 -j ACCEPT

flannel配置如下:

# etcdctl get /coreos.com/network/config | jq .{  "Network": "40.15.0.0/16",  "Backend": {    "Type": "aws-vpc"  }}

啟動flannel,自動為每個Node分配24位子網,網段如下:

Node名主機IP容器IP
node-1192.168.1.6840.15.16.0/24
node-2192.168.1.25440.15.64.0/24
node-3192.168.1.24540.15.13.0/24
node-4192.168.0.3340.15.83.0/24

我們查看node-1、node-2、node-3關聯的路由表如圖:

主流Docker網絡的實現原理是什么

node-4關聯的路由表如圖:

主流Docker網絡的實現原理是什么

由此可見,每增加一個Flannel節點,Flannel就會調用AWS API在EC2實例的子網關聯的路由表上增加一條記錄,Destination為該節點分配的Flannel子網,Target為該EC2實例的主網卡。

在4個節點分別創建一個busybox容器,容器IP如下:

Node名主機IP容器IP
node-1192.168.1.6840.15.16.2/24
node-2192.168.1.25440.15.64.2/24
node-3192.168.1.24540.15.13.2/24
node-4192.168.0.3340.15.83.2/24

所有節點ping node-4的容器,如圖:

主流Docker網絡的實現原理是什么

我們發現所有節點都能ping通node-4的容器。但是node-4的容器卻ping不通其余容器:

主流Docker網絡的實現原理是什么

這是因為每個Node默認只會添加自己所在路由的記錄。node-4沒有node-1 ~ node-3的路由信息,因此不通。

可能有人會問,node1 ~ node3也沒有node4的路由,那為什么能ping通node4的容器呢?這是因為我的環境中node1 ~ node3子網關聯的路由是NAT網關,node4是Internet網關,而NAT網關的子網正好是node1 ~ node4關聯的子網,因此node1 ~ node3雖然在自己所在的NAT網關路由沒有找到node4的路由信息,但是下一跳到達Internet網關的路由表中找到了node4的路由,因此能夠ping通,而node4找不到node1 ~ node3的路由,因此都ping不通。

以上只是默認行為,Flannel可以通過 RouteTableID參數配置Node需要更新的路由表,我們只需要增加如下兩個子網的路由如下:

# etcdctl get  /coreos.com/network/config | jq .{  "Network": "40.15.0.0/16",  "Backend": {    "Type": "aws-vpc",    "RouteTableID": [      "rtb-0686cdc9012674692",      "rtb-054dfd5f3e47102ae"    ]  }}

重啟Flannel服務,再次查看兩個路由表:

主流Docker網絡的實現原理是什么

我們發現兩個路由表均添加了node1 ~ node4的Flannel子網路由。

此時四個節點的容器能夠相互ping通。

主流Docker網絡的實現原理是什么

從中我們發現,aws-vpc解決了host-gw不能跨子網的問題,Flannel官方也建議如果使用AWS,推薦使用aws-vpc替代overlay方式,能夠獲取更好的性能:

When running within an Amazon VPC, we recommend using the aws-vpc backend which, instead of using encapsulation, manipulates IP routes to achieve maximum performance. Because of this, a separate flannel interface is not created.

The biggest advantage of using flannel AWS-VPC backend is that the AWS knows about that IP. That makes it possible to set up ELB to route directly to that container.

另外,由于路由是添加到了主機網關上,因此只要關聯了該路由表,EC2實例是可以從外面直接ping通容器的,換句話說,同一子網的EC2虛擬機可以直接與容器互通。

主流Docker網絡的實現原理是什么

不過需要注意的是,AWS路由表默認最多支持50條路由規則,這限制了Flannel節點數量,不知道AWS是否支持增加配額功能。另外目前最新版的Flannel v0.10.0好像對aws-vpc支持有點問題,再官方修復如上問題之前建議使用Flannel v0.8.0版本。

五、黑科技最多的Calico

5.1 Calico環境配置

Calico和Flannel host-gw類似都是通過路由實現跨主機通信,區別在于Flannel通過flanneld進程逐一添加主機靜態路由實現,而Calico則是通過BGP實現節點間路由規則的相互學習廣播。

這里不詳細介紹BGP的實現原理,僅研究容器是如何通信的。

創建了3個節點的calico集群,ip pool配置如下:

# calicoctl  get ipPool -o yaml- apiVersion: v1  kind: ipPool  metadata:    cidr: 197.19.0.0/16  spec:    ipip:      enabled: true      mode: cross-subnet    nat-outgoing: true- apiVersion: v1  kind: ipPool  metadata:    cidr: fd80:24e2:f998:72d6::/64  spec: {}

Calico分配的ip如下:

  1. for host in $(etcdctl --endpoints $ENDPOINTS ls /calico/ipam/v2/host/); do

  2.    etcdctl --endpoints $ENDPOINTS ls  $host/ipv4/block | awk -F '/' '{sub(/-/,"/",$NF)}{print $6,$NF}'

  3. done | sort


  4. int32bit-docker-1 197.19.38.128/26

  5. int32bit-docker-2 197.19.186.192/26

  6. int32bit-docker-3 197.19.26.0/26

由此可知,Calico和Flannel一樣,每個節點分配一個子網,只不過Flannel默認分24位子網,而Calico分的是26位子網。

三個節點分別創建busybox容器:

Node名主機IP容器IP
node-1192.168.1.68197.19.38.136
node-2192.168.1.254197.19.186.197
node-3192.168.1.245197.19.26.5/24

主流Docker網絡的實現原理是什么

相互ping通沒有問題。

5.2 Calico容器內部網絡

我們查看容器的link設備以及路由:

  1. # ./docker_netns.sh busybox ip a

  2. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000

  3.    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

  4.    inet 127.0.0.1/8 scope host lo

  5.       valid_lft forever preferred_lft forever

  6. 2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000

  7.    link/ipip 0.0.0.0 brd 0.0.0.0

  8. 14: cali0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default

  9.    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0

  10.    inet 197.19.38.136/32 brd 197.19.38.136 scope global cali0

  11.       valid_lft forever preferred_lft forever


  12. # ./docker_netns.sh busybox ip r

  13. default via 169.254.1.1 dev cali0

  14. 169.254.1.1 dev cali0 scope link

有如下幾點個人感覺很神奇:

  • 所有容器的MAC地址都是 ee:ee:ee:ee:ee:ee

  • 網關地址是169.254.1.1,然而我找盡了所有的namespaces也沒有找到這個IP。

這兩個問題在Calico官方的faq中有記錄#1 Why do all cali* interfaces have the MAC address ee:ee:ee:ee:ee:ee?、#2 Why can’t I see the 169.254.1.1 address mentioned above on my host?。

針對第一個問題,官方認為不是所有的內核都能支持自動分配MAC地址,所以干脆Calico自己指定MAC地址,而Calico完全使用三層路由通信,MAC地址是什么其實無所謂,因此直接都使用 ee:ee:ee:ee:ee:ee

第二個問題,回顧之前的網絡模型,大多數都是把容器的網卡通過VETH連接到一個bridge設備上,而這個bridge設備往往也是容器網關,相當于主機上多了一個虛擬網卡配置。Calico認為容器網絡不應該影響主機網絡,因此容器的網卡的VETH另一端沒有經過bridge直接掛在默認的namespace中。而容器配的網關其實也是假的,通過proxy_arp修改MAC地址模擬了網關的行為,所以網關IP是什么也無所謂,那就直接選擇了local link的一個ip,這還節省了容器網絡的一個IP。我們可以抓包看到ARP包:

主流Docker網絡的實現原理是什么

可以看到容器網卡的對端 calia2656637189直接代理回復了ARP,因此出去網關時容器的包會直接把MAC地址修改為 06:66:26:8e:b2:67,即偽網關的MAC地址。

有人可能會說那如果在同一主機的容器通信呢?他們應該在同一個子網,容器的MAC地址都是一樣那怎么進行二層通信呢?仔細看容器配置的IP掩碼居然是32位的,那也就是說跟誰都不在一個子網了,也就不存在二層的鏈路層直接通信了。

5.3 Calico主機路由

前面提到Calico通過BGP動態路由實現跨主機通信,我們查看主機路由如下,其中197.19.38.139、197.19.38.140是在本機上的兩個容器IP:

# ip r | grep 197.19197.19.26.0/26 via 192.168.1.245 dev eth0 proto birdblackhole 197.19.38.128/26 proto bird197.19.38.139 dev calia2656637189 scope link197.19.38.140 dev calie889861df72 scope link197.19.186.192/26 via 192.168.1.254 dev eth0 proto bird

我們發現跨主機通信和Flannel host-gw完全一樣,下一跳直接指向hostIP,把host當作容器的網關。不一樣的是到達宿主機后,Flannel會通過路由轉發流量到bridge設備中,再由bridge轉發給容器,而Calico則為每個容器的IP生成一條明細路由,直接指向容器的網卡對端。因此如果容器數量很多的話,主機路由規則數量也會越來越多,因此才有了路由反射,這里不過多介紹。

里面還有一條blackhole路由,如果來的IP是在host分配的容器子網197.19.38.128/26中,而又不是容器的IP,則認為是非法地址,直接丟棄。

5.4 Calico多網絡支持

在同一個集群上可以同時創建多個Calico網絡:

# docker network ls | grep calicoad7ca8babf01        calico-net-1        calico              global5eaf3984f69d        calico-net-2        calico              global

我們使用另一個Calico網絡calico-net-2創建一個容器:

  1. docker run -d --name busybox-3 --net calico-net-2 busybox sleep 36000

  2. # docker exec busybox-3 ip a

  3. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000

  4.    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

  5.    inet 127.0.0.1/8 scope host lo

  6.       valid_lft forever preferred_lft forever

  7. 2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop qlen 1000

  8.    link/ipip 0.0.0.0 brd 0.0.0.0

  9. 24: cali0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue

  10.    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff

  11.    inet 197.19.38.141/32 brd 197.19.38.141 scope global cali0

  12.       valid_lft forever preferred_lft forever


  13. # ip r | grep 197.19

  14. 197.19.26.0/26 via 192.168.1.245 dev eth0 proto bird

  15. blackhole 197.19.38.128/26 proto bird

  16. 197.19.38.139 dev calia2656637189 scope link

  17. 197.19.38.140 dev calie889861df72 scope link

  18. 197.19.38.141 dev calib12b038e611 scope link

  19. 197.19.186.192/26 via 192.168.1.254 dev eth0 proto bird

我們發現在同一個主機不在同一個網絡的容器IP地址在同一個子網,那不是可以通信呢?

主流Docker網絡的實現原理是什么

我們發現雖然兩個跨網絡的容器分配的IP在同一個子網,但居然實現了隔離。

如果使用諸如vxlan的overlay網絡,很好猜測是怎么實現隔離的,無非就是使用不同的VNI。但Calico沒有使用overlay,直接使用路由通信,而且不同網絡的子網還是重疊的,它是怎么實現隔離的呢。

要在同一個子網實現隔離,我們猜測實現方式只能是邏輯隔離,即通過本地防火墻如iptables實現。

查看了下Calico生成的iptables規則發現太復雜了,各種包mark。由于決定包的放行或者丟棄通常是在filter表實現,而不是發往主機的自己的包應該在FORWARD鏈中,因此我們直接研究filter表的FORWARD表。

# iptables-save -t filter | grep -- '-A FORWARD'-A FORWARD -m comment --comment "cali:wUHhoiAYhphO9Mso" -j cali-FORWARD...

Calico把cali-FORWARD子鏈掛在了FORWARD鏈上,comment中的一串看起來像隨機字符串 cali:wUHhoiAYhphO9Mso不知道是干嘛的。

# iptables-save -t filter | grep -- '-A cali-FORWARD'-A cali-FORWARD -i cali+ -m comment --comment "cali:X3vB2lGcBrfkYquC" -j cali-from-wl-dispatch-A cali-FORWARD -o cali+ -m comment --comment "cali:UtJ9FnhBnFbyQMvU" -j cali-to-wl-dispatch-A cali-FORWARD -i cali+ -m comment --comment "cali:Tt19HcSdA5YIGSsw" -j ACCEPT-A cali-FORWARD -o cali+ -m comment --comment "cali:9LzfFCvnpC5_MYXm" -j ACCEPT...

cali+表示所有以cali為前綴的網絡接口,即容器的網卡對端設備。由于我們只關心發往容器的流量方向,即從caliXXX發往容器的流量,因此我們只關心條件匹配的 -o cali+的規則,從如上可以看出所有從 cali+出來的流量都跳轉到了 cali-to-wl-dispatch子鏈處理,其中 wl是workload的縮寫,workload即容器。

# iptables-save -t filter | grep -- '-A cali-to-wl-dispatch'-A cali-to-wl-dispatch -o calia2656637189 -m comment --comment "cali:TFwr8sfMnFH3BUla" -g cali-tw-calia2656637189-A cali-to-wl-dispatch -o calib12b038e611 -m comment --comment "cali:ZbRb0ozg-GGeUfRA" -g cali-tw-calib12b038e611-A cali-to-wl-dispatch -o calie889861df72 -m comment --comment "cali:5OoGv50NzX0sKdMg" -g cali-tw-calie889861df72-A cali-to-wl-dispatch -m comment --comment "cali:RvicCiwAy9cIEAKA" -m comment --comment "Unknown interface" -j DROP

從子鏈名字也可以看出 cali-to-wl-dispatch是負責流量的分發的,即根據具體的流量出口引到具體的處理流程子鏈,從X出來的,由cali-tw-X處理,從Y出來的,由cali-tw-Y處理,依次類推,其中 tw為 to workload的簡寫。

我們假設是發往busybox 197.19.38.139這個容器的,對應的主機虛擬設備為 calia2656637189,則跳轉子鏈為 cali-tw-calia2656637189

# iptables-save -t filter | grep -- '-A cali-tw-calia2656637189'-A cali-tw-calia2656637189 -m comment --comment "cali:259EHpBvnovN8_q6" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT-A cali-tw-calia2656637189 -m comment --comment "cali:YLokMEiVkZggfg9R" -m conntrack --ctstate INVALID -j DROP-A cali-tw-calia2656637189 -m comment --comment "cali:pp8a6fGxqaALtRK5" -j MARK --set-xmark 0x0/0x1000000-A cali-tw-calia2656637189 -m comment --comment "cali:bgw2sCtlIfZjhXLA" -j cali-pri-calico-net-1-A cali-tw-calia2656637189 -m comment --comment "cali:1Z2NvhoS27pP03Ll" -m comment --comment "Return if profile accepted" -m mark --mark 0x1000000/0x1000000 -j RETURN-A cali-tw-calia2656637189 -m comment --comment "cali:mPb8hORsTXeVt7yC" -m comment --comment "Drop if no profiles matched" -j DROP

其中第1、2條規則在深入淺出OpenStack安全組實現原理中介紹過,不再贅述。

第三條規則注意使用的是 set-xmark而不是 set-mark,為什么不用 set-mark,這是由于 set-mark會覆蓋原來的值。而 set-xmark value/netmask,表示 X=(X&(~netmask))^value, --set-xmark0x0/0x1000000的意思就是把X的第25位重置為0,其他位保留不變。

這個mark位的含義我在官方中沒有找到,在Calico網絡的原理、組網方式與使用這篇文章找到了相關資料:

node一共使用了3個標記位,0x7000000對應的標記位

0x1000000: 報文的處理動作,置1表示放行,默認0表示拒絕

0x2000000: 是否已經經過了policy規則檢測,置1表示已經過

0x4000000: 報文來源,置1,表示來自host-endpoint

即第25位表示報文的處理動作,為1表示通過,0表示拒絕,第5、6條規則也可以看出第25位的意義,匹配0x1000000/0x1000000直接RETRUN,不匹配的直接DROP。

因此第3條規則的意思就是清空第25位標志位重新評估,誰來評估呢?這就是第4條規則的作用,根據虛擬網絡設備cali-XXX所處的網絡跳轉到指定網絡的子鏈中處理,由于 calia2656637189屬于calico-net-1,因此會跳轉到 cali-pri-calico-net-1子鏈處理。

我們觀察 cali-pri-calico-net-1的規則:

# iptables-save -t filter | grep -- '-A cali-pri-calico-net-1'-A cali-pri-calico-net-1 -m comment --comment "cali:Gvse2HBGxQ9omCdo" -m set --match-set cali4-s:VFoIKKR-LOG_UuTlYqcKubo src -j MARK --set-xmark 0x1000000/0x1000000-A cali-pri-calico-net-1 -m comment --comment "cali:0vZpvvDd_5bT7g_k" -m mark --mark 0x1000000/0x1000000 -j RETURN

規則很簡單,只要IP在cali4-s:VFoIKKR-LOG_UuTlYqcKubo在這個ipset集合中就設置mark第25位為1,然后RETURN,否則如果IP不在ipset中則直接DROP(子鏈的默認行為為DROP)。

# ipset list cali4-s:VFoIKKR-LOG_UuTlYqcKuboName: cali4-s:VFoIKKR-LOG_UuTlYqcKuboType: hash:ipRevision: 4Header: family inet hashsize 1024 maxelem 1048576Size in memory: 280References: 1Number of entries: 4Members:197.19.38.143197.19.26.7197.19.186.199197.19.38.144

到這里終于真相大白了,Calico是通過iptables + ipset實現多網絡隔離的,同一個網絡的IP會加到同一個ipset集合中,不同網絡的IP放到不同的ipset集合中,最后通過iptables的set模塊匹配ipset集合的IP,如果src IP在指定的ipset中則允許通過,否則DROP。

5.5 Calico跨網段通信

我們知道Flannel host-gw不支持Node主機跨網段,Calico是否支持呢,為此我增加了一個node-4(192.168.0.33/24),顯然和其他三個Node不在同一個子網。

在新的Node中啟動一個busybox:

  1. docker run -d --name busybox-node-4 --net calico-net-1 busybox sleep 36000

  2. docker exec busybox-node-4 ping -c 1 -w 1 197.19.38.144

  3. PING 197.19.38.144 (197.19.38.144): 56 data bytes

  4. 64 bytes from 197.19.38.144: seq=0 ttl=62 time=0.539 ms


  5. --- 197.19.38.144 ping statistics ---

  6. 1 packets transmitted, 1 packets received, 0% packet loss

  7. round-trip min/avg/max = 0.539/0.539/0.539 ms

驗證發現容器通信時沒有問題的。

查看node-1路由:

# ip r | grep 197.19197.19.26.0/26 via 192.168.1.245 dev eth0 proto birdblackhole 197.19.38.128/26 proto bird197.19.38.142 dev cali459cc263d36 scope link197.19.38.143 dev cali6d0015b0c71 scope link197.19.38.144 dev calic8e5fab61b1 scope link197.19.65.128/26 via 192.168.0.33 dev tunl0 proto bird onlink197.19.186.192/26 via 192.168.1.254 dev eth0 proto bird

和其他路由不一樣的是,我們發現197.19.65.128/26是通過tunl0出去的:

# ip -d link show tunl05: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000    link/ipip 0.0.0.0 brd 0.0.0.0 promiscuity 0    ipip any remote any local any ttl inherit nopmtudisc addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535# ip -d tunnel showtunl0: any/ip remote any local any ttl inherit nopmtudisc

由此可知,如果節點跨網段,則Calico通過ipip隧道傳輸,相當于走的是overlay。

對比Flannel host-gw,除了靜態與BGP動態路由配置的區別,Calico還通過iptables + ipset解決了多網絡支持問題,通過ipip隧道實現了節點跨子網通信問題。

另外,某些業務或者POD需要固定IP,比如POD從一個節點遷移到另一個節點保持IP不變,這種情況下可能導致容器的IP不在節點Node上分配的子網范圍內,Calico可以通過添加一條32位的明細路由實現,Flannel不支持這種情況。

因此相對來說Calico實現的功能相對要多些,但是,最終也導致Calico相對Flannel要復雜得多,運維難度也較大,光一堆iptables規則就不容易理清了。

六、與OpenStack網絡集成的Kuryr

Kuryr是OpenStack中一個較新的項目,其目標是“Bridge between container framework networking and storage models to OpenStack networking and storage abstractions.”,即實現容器與OpenStack的網絡集成,該方案實現了與虛擬機、裸機相同的網絡功能和互通,比如多租戶、安全組等。

網絡模型和虛擬機基本一樣,唯一區別在于虛擬機是通過TAP設備直接掛到虛擬機設備中的,而容器則是通過VETH連接到容器的namespace。

  1.    vm               Container        whatever

  2.    |                    |                |

  3.   tapX                tapY             tapZ

  4.    |                    |                |

  5.    |                    |                |

  6.  qbrX                 qbrY             qbrZ

  7.    |                    |                |

  8. ---------------------------------------------

  9.    |               br-int(OVS)           |

  10. ---------------------------------------------

  11.                         |

  12. ----------------------------------------------

  13.    |               br-tun(OVS)           |

  14. ----------------------------------------------

感謝你能夠認真閱讀完這篇文章,希望小編分享的“主流Docker網絡的實現原理是什么”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

康乐县| 简阳市| 永宁县| 芷江| 江口县| 荔波县| 富宁县| 宣武区| 舒兰市| 延川县| 怀集县| 弋阳县| 金平| 镇坪县| 博兴县| 客服| 濮阳市| 普洱| 京山县| 芮城县| 林芝县| 蛟河市| 明溪县| 青龙| 张家港市| 印江| 谷城县| 岢岚县| 许昌市| 邢台县| 黔江区| 聂拉木县| 金寨县| 普安县| 同心县| 兰坪| 德惠市| 安达市| 嫩江县| 安泽县| 徐闻县|