溫馨提示×

溫馨提示×

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

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

如何分析Kubernetes中的容器網(wǎng)絡(luò)

發(fā)布時間:2021-12-27 16:57:43 來源:億速云 閱讀:134 作者:柒染 欄目:云計算

這篇文章將為大家詳細講解有關(guān)如何分析Kubernetes中的容器網(wǎng)絡(luò),文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

| 前言

隨著云計算的興起,各大平臺之爭也落下了帷幕,Kubernetes作為后起之秀已經(jīng)成為了事實上的PaaS平臺標(biāo)準(zhǔn),而網(wǎng)絡(luò)又是云計算環(huán)境當(dāng)中最復(fù)雜的部分,總是讓人琢磨不透。圍繞在Kubernetes環(huán)境當(dāng)中同一個節(jié)點(work node)上的Pod之間是如何進行網(wǎng)絡(luò)通信的這個問題進行展開,暫且不考慮跨節(jié)點網(wǎng)絡(luò)通信的情況。

| Network Namespace

Namespace


提到容器就不得不提起容器的核心底層技術(shù)Namespace,Namespace為Linux內(nèi)核當(dāng)中提供的一種隔離機制,最初在2002年引入到Linux 2.4.19當(dāng)中,且只有Mount Namespace用于文件系統(tǒng)隔離。截止目前,Linux總共提供了7種Namespace(since Linux 4.6),系統(tǒng)當(dāng)中運行的每一個進程都與一個Namespace相關(guān)聯(lián),且該進程只能看到和使用該Namespace下的資源??梢院唵螌amespace理解為操作系統(tǒng)對進程實現(xiàn)的一種“障眼法”,比如通過UTS Namespace可以實現(xiàn)讓運行在同一臺機器上的進程看到不同的Hostname。正是由于Namespace開創(chuàng)性地隔離方式才讓容器的實現(xiàn)得以變?yōu)榭赡?,才能讓我們的軟件真正地實現(xiàn)“build once, running everywhere”。
如何分析Kubernetes中的容器網(wǎng)絡(luò)

Network Namespace

Network Namespace是Linux 2.6.24才開始引入的,直到Linux 2.6.29才完成的特性。Network Namespace實際上實現(xiàn)的是對網(wǎng)絡(luò)棧的虛擬化,且在創(chuàng)建出來時默認(rèn)只有一個回環(huán)網(wǎng)絡(luò)接口lo,每一個網(wǎng)絡(luò)接口(不管是物理接口還是虛擬化接口)都只能存在于一個Network Namespace當(dāng)中,但可以在不同的Network Namespace之間切換,每一個Network Namespace都有自己獨立的IP地址、路由表、防火墻以及套接字列表等網(wǎng)絡(luò)相關(guān)資源。當(dāng)刪除一個Network Namespace時,其內(nèi)部的網(wǎng)絡(luò)資源(網(wǎng)路接口等)也會同時被刪掉,而物理接口則會被切換回之前的Network Namespace當(dāng)中。

容器與Pod

在Kubernetes的定義當(dāng)中,Pod為一組不可分離的容器,且共享同一個Network Namespace,故不存在同一個Pod當(dāng)中容器間網(wǎng)絡(luò)通信的問題,對于同一個Pod當(dāng)中的容器來講,通過Localhost即可與其他的容器進行網(wǎng)絡(luò)通信。

所以同一個節(jié)點上的兩個Pod如何進行網(wǎng)絡(luò)通信的問題可以轉(zhuǎn)變?yōu)?,同一個節(jié)點上的兩個容器如何進行網(wǎng)絡(luò)通信。

Namespace實操

在回答上面提出的Network Namespace網(wǎng)絡(luò)通信的問題前,我們先來做一些簡單的命令行操作,先對Namespace有一個感性地認(rèn)識,實驗環(huán)境如下: 

如何分析Kubernetes中的容器網(wǎng)絡(luò)

通過命令lsns可以查看到宿主機上所有的Namespace(注意需要使用root用戶執(zhí)行,否則可能會出現(xiàn)有些Namespace看不到的情況): 

如何分析Kubernetes中的容器網(wǎng)絡(luò)

lsns默認(rèn)會輸出所有可以看到的Namespace,簡單解釋一下lsns命令各個輸出列的含義:

如何分析Kubernetes中的容器網(wǎng)絡(luò)

與Network Namespace相關(guān)性較強的還有另外一個命令 ip netns,主要用于持久化命名空間的管理,包括Network Namespace的創(chuàng)建、刪除以和配置等。 ip netns命令在創(chuàng)建Network Namespace時默認(rèn)會在/var/run/netns目錄下創(chuàng)建一個bind mount的掛載點,從而達到持久化Network Namespace的目的,即允許在該命名空間當(dāng)中沒有進程的情況下依然保留該命名空間。Docker當(dāng)中由于缺少了這一步,玩過Docker的同學(xué)就會發(fā)現(xiàn)通過Docker創(chuàng)建容器后并不能在宿主機上通過 ip netns查看到相關(guān)的Network Namespace(這個后面會講怎么才能夠看到,稍微小操作一下就行)。

與Network Namespace相關(guān)操作命令:

ip netns add < namespace name > # 添加network namespace
ip netns list  # 查看Network Namespace
ip netns delete < namespace name > # 刪除Network Namespace
ip netns exec < namespace name > <command> # 進入到Network Namespace當(dāng)中執(zhí)行命令

創(chuàng)建名為netA的Network Namespace: 

如何分析Kubernetes中的容器網(wǎng)絡(luò)

查看創(chuàng)建的Network Namespace: 

如何分析Kubernetes中的容器網(wǎng)絡(luò)

可以看到Network Namespace netA當(dāng)中僅有一個環(huán)回網(wǎng)絡(luò)接口lo,且有獨立的路由表(為空)。

宿主機(root network namespace)上有網(wǎng)絡(luò)接口eth0(10.10.88.170)和eth2(172.16.130.164),此時可以直接ping通IP 172.16.130.164。

如何分析Kubernetes中的容器網(wǎng)絡(luò)

嘗試將root network namespace當(dāng)中的eth0接口添加到network namespce netA當(dāng)中:

ip link set dev eth0 netns netA

如何分析Kubernetes中的容器網(wǎng)絡(luò)

將宿主機上的網(wǎng)絡(luò)接口eth0(10.10.88.170)加入到網(wǎng)絡(luò)命名空間netA后:


1. 宿主機上看不到eth0網(wǎng)絡(luò)接口了(同一時刻網(wǎng)絡(luò)接口只能在一個Network Namespace)

2. netA network namespace里面無法ping通root namespace當(dāng)中的eth2(網(wǎng)絡(luò)隔離)


從上面的這些操作我們只是知道了Network Namespace的隔離性,但仍然無法達到我們想要的結(jié)果,即讓兩個容器或者說兩個不同的Network Namespace進行網(wǎng)絡(luò)通信。在真實的生活場景中,當(dāng)我們要連接同一個集團兩個相距千里的分公司的局域網(wǎng)時,我們有3種解決方案:第一種是對數(shù)據(jù)比較隨意的,直接走公網(wǎng)連接,但存在網(wǎng)絡(luò)安全的問題。第二種是不差錢的,直接拉一根專線將兩個分公司的網(wǎng)絡(luò)連接起來,這樣雖然遠隔千里,但仍然可以處于一個網(wǎng)絡(luò)當(dāng)中。另外一種是兼顧網(wǎng)絡(luò)安全集和性價比的VPN連接,但存在性能問題。很顯然,不管是哪一種方案都需要有一根“線”將兩端連接起來,不管是虛擬的VPN還是物理的專線。

| vEth(Virtual Ethernet Device)

前面提到了容器通過Network Namespace進行網(wǎng)絡(luò)隔離,但是又由于Network Namespace的隔離導(dǎo)致兩個不同的Network Namespace無法進行通信,這個時候我們聯(lián)想到了實際生活場景中連接一個集團的兩個分公司局域網(wǎng)的處理方式。實際上Linux當(dāng)中也存在類似像網(wǎng)線一樣的虛擬設(shè)備vEth(此時是不是覺得Linux簡直無所不能?),全稱為Virtual Ethernet Device,是一種虛擬的類似于以太網(wǎng)絡(luò)的設(shè)備。

vEth有以下幾個特點:


  • vEth作為一種虛擬以太網(wǎng)絡(luò)設(shè)備,可以連接兩個不同的Network Namespace。

  • vEth總是成對創(chuàng)建,所以一般叫veth pair。(因為沒有只有一頭的網(wǎng)線)。

  • vEth當(dāng)中一端收到數(shù)據(jù)包后另一端也會立馬收到。

  • 可以通過ethtool找到vEth的對端接口。(注意后面會用到)


理解了以上幾點對于我們后面理解容器間的網(wǎng)絡(luò)通信就容易多了。




vEth實操

創(chuàng)建vEth:

ip link add < veth name > type veth peer name < veth peer name >

創(chuàng)建名為veth0A,且對端為veth0B的vEth設(shè)備。

如何分析Kubernetes中的容器網(wǎng)絡(luò)

可以看到root network namespace當(dāng)中多出來了兩個網(wǎng)絡(luò)接口veth0A和veth0B,網(wǎng)絡(luò)接口名稱@后面的接的正是對端的接口名稱。

創(chuàng)建Network Namespace netA和netB:

如何分析Kubernetes中的容器網(wǎng)絡(luò)

分別將接口veth0A加入到netA,將接口veth0B加入到netB:

ip link set veth0A netns netA
ip link set veth0B netns netB

如何分析Kubernetes中的容器網(wǎng)絡(luò)

這個時候通過IP a查看宿主機(root network namespace)網(wǎng)絡(luò)接口時可以發(fā)現(xiàn),已經(jīng)看不到接口veth0A和veth0B了(同一時刻一個接口只能處于一個Network Namespace下面)。

再分別到netA和netB兩個Network Namespace當(dāng)中去查看,可以看到兩個Network Namespace當(dāng)中都多了一個網(wǎng)絡(luò)接口。

如何分析Kubernetes中的容器網(wǎng)絡(luò)

分別拉起兩個網(wǎng)絡(luò)接口并配上IP,這里將為veth0A配置IP 192.168.100.1,veth0B配置IP 192.168.100.2:

ip netns exec netA ip link set veth0A up
ip netns exec netA ip addr add 192.168.100.1/24 dev veth0A

如何分析Kubernetes中的容器網(wǎng)絡(luò)

ip netns exec netB ip addr add 192.168.100.2/24 dev veth0B

如何分析Kubernetes中的容器網(wǎng)絡(luò)

測試通過veth pair連接的兩個Network Namespace netA和netB之間的網(wǎng)絡(luò)連接。

在netA(192.168.100.1)當(dāng)中ping netB(192.168.100.2): 

如何分析Kubernetes中的容器網(wǎng)絡(luò)

在netB(192.168.100.2)當(dāng)中ping netA(192.168.100.1): 

如何分析Kubernetes中的容器網(wǎng)絡(luò)

可以發(fā)現(xiàn)netA跟netB這兩個Network Namespace在通過veth pair連接后已經(jīng)可以進行正常的網(wǎng)絡(luò)通信了。

解決了容器Network Namespace隔離的問題,這個時候有云計算經(jīng)驗或者熟悉OpenStack和ZStack的同學(xué)就會發(fā)現(xiàn),現(xiàn)在的場景跟虛擬機之間的網(wǎng)絡(luò)互聯(lián)是不是簡直一模一樣了?

vEth作為一個二層網(wǎng)絡(luò)設(shè)備,當(dāng)需要跟別的網(wǎng)絡(luò)設(shè)備相連時該怎么處理呢?在現(xiàn)實生活場景當(dāng)中我們會拿一個交換機將不同的網(wǎng)線連接起來。實際上在虛擬化場景下也是如此,Linux Bridge和Open vSwith(OVS)是當(dāng)下比較常用的兩種連接二層網(wǎng)絡(luò)的解決方案,而Docker當(dāng)中采用的是Linux Bridge。

| Docker與Kubernetes

Kubernetes作為一個容器編排平臺,在容器引擎方面既可以選擇Docker也可以選擇rkt,這里直接分別通過Docker和Kubernetes創(chuàng)建容器來進行簡單比對。 Kubernetes在創(chuàng)建Pod時首先會創(chuàng)建一個pause容器,它的唯一作用就是保留和占據(jù)一個被Pod當(dāng)中所有容器所共享的網(wǎng)絡(luò)命名空間(Network Namespace),就這樣,一個Pod IP并不會隨著Pod當(dāng)中的一個容器的起停而改變。

Docker下的容器網(wǎng)絡(luò)

我們先來看一下在沒有Kubernetes的情況下是什么樣子的。在Docker啟動的時候默認(rèn)會創(chuàng)建一個名為docker0的網(wǎng)橋,且默認(rèn)配置下采用的網(wǎng)絡(luò)段為172.17.0.0/16,每一個在該節(jié)點上創(chuàng)建的容器都會被分配一個在該網(wǎng)段下的IP。容器通過連接到docker0上進行相互通信。

手動創(chuàng)建兩個容器:

docker run -it --name testA busybox sh
docker run -it --name testB busybox sh

查看網(wǎng)絡(luò)接口狀況。

容器testA: 

如何分析Kubernetes中的容器網(wǎng)絡(luò)

容器testB: 

如何分析Kubernetes中的容器網(wǎng)絡(luò)

查看網(wǎng)橋狀態(tài): 

如何分析Kubernetes中的容器網(wǎng)絡(luò)

可以發(fā)現(xiàn)docker0上面已經(jīng)連接了兩個虛擬網(wǎng)絡(luò)接口(vEth)。

在docker0上通過tcpdump抓包:

tcpdump -n -i docker0

如何分析Kubernetes中的容器網(wǎng)絡(luò)

可以發(fā)現(xiàn)容器testA和容器testB正是通過docker0網(wǎng)橋進行網(wǎng)絡(luò)包轉(zhuǎn)發(fā)的。

加入Kubernetes后的容器網(wǎng)絡(luò)

其實加入Kubernetes后本質(zhì)上容器網(wǎng)絡(luò)通信模式并沒有發(fā)生變更,但Kubernetes出于網(wǎng)絡(luò)地址規(guī)劃的考慮,重新創(chuàng)建了一個網(wǎng)橋cni0用于取代docker0,來負責(zé)本節(jié)點上網(wǎng)絡(luò)地址的分配,而實際的網(wǎng)絡(luò)段管理由Flannel處理。

下面還是以創(chuàng)建2個運行BusyBox鏡像的Pod作為例子進行說明。

先給Kubernetes集群當(dāng)中的兩個work node打上label以方便將Pod調(diào)度到相同的節(jié)點上面進行測試:

[root@10-10-88-192 network]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
10-10-88-170 Ready <none> 47d v1.10.5-28+187e1312d40a02 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10-10-88-170
10-10-88-192 Ready master 47d v1.10.5-28+187e1312d40a02 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10-10-88-192,node-role.kubernetes.io/master=
10-10-88-195 Ready <none> 47d v1.10.5-28+187e1312d40a02 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10-10-88-195
[root@10-10-88-192 network]#
[root@10-10-88-192 network]# kubectl label --overwrite node 10-10-88-170 host=node1node "10-10-88-170" labeled
[root@10-10-88-192 network]#
[root@10-10-88-192 network]# kubectl label --overwrite node 10-10-88-195 host=node2node "10-10-88-195" labeled
[root@10-10-88-192 network]#
[root@10-10-88-192 network]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
10-10-88-170 Ready <none> 47d v1.10.5-28+187e1312d40a02 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,host=node1,kubernetes.io/hostname=10-10-88-170
10-10-88-192 Ready master 47d v1.10.5-28+187e1312d40a02 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10-10-88-192,node-role.kubernetes.io/master=
10-10-88-195 Ready <none> 47d v1.10.5-28+187e1312d40a02 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,host=node2,kubernetes.io/hostname=10-10-88-195
[root@10-10-88-192 network]#

創(chuàng)建兩個Pod并通過添加nodeSelector使其調(diào)度到同一個節(jié)點(host1)。

編輯Pod的yaml配置文件:

如何分析Kubernetes中的容器網(wǎng)絡(luò)

基于yaml文件創(chuàng)建Pod:

如何分析Kubernetes中的容器網(wǎng)絡(luò)

可以看到兩個Pod都按照預(yù)期調(diào)度到了10-10-88-170這個節(jié)點上面。

通過IP a命令可以看到在Pod的宿主機上多出來了2個vethXXX樣式的網(wǎng)絡(luò)接口:

[root@10-10-88-170 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
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
inet6 ::1/128 scope host
   valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether fa:35:b6:5e:ac:00 brd ff:ff:ff:ff:ff:ff
inet 10.10.88.170/24 brd 10.10.88.255 scope global eth0
   valid_lft forever preferred_lft forever
inet6 fe80::f835:b6ff:fe5e:ac00/64 scope link
   valid_lft forever preferred_lft forever
3: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether fa:88:2a:44:2b:01 brd ff:ff:ff:ff:ff:ff
inet 172.16.130.164/24 brd 172.16.130.255 scope global eth2
   valid_lft forever preferred_lft forever
inet6 fe80::f888:2aff:fe44:2b01/64 scope link
   valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:43:a1:fc:ad brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
   valid_lft forever preferred_lft forever
5: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN
link/ether 0e:c4:2c:84:a5:ea brd ff:ff:ff:ff:ff:ff
inet 10.244.2.0/32 scope global flannel.1
   valid_lft forever preferred_lft forever
inet6 fe80::cc4:2cff:fe84:a5ea/64 scope link
   valid_lft forever preferred_lft forever
6: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP qlen 1000
link/ether 0a:58:0a:f4:02:01 brd ff:ff:ff:ff:ff:ff
inet 10.244.2.1/24 scope global cni0
   valid_lft forever preferred_lft forever
inet6 fe80::f0a0:7dff:feec:3ffd/64 scope link
   valid_lft forever preferred_lft forever
9: veth3a69de99@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP
link/ether 86:70:76:4f:de:2b brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::8470:76ff:fe4f:de2b/64 scope link
   valid_lft forever preferred_lft forever
10: vethc8ca82e9@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP
link/ether 76:ad:89:ae:21:68 brd ff:ff:ff:ff:ff:ff link-netnsid 3
inet6 fe80::74ad:89ff:feae:2168/64 scope link
   valid_lft forever preferred_lft forever
39: veth786e1634@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP
link/ether 66:99:fe:30:d2:e1 brd ff:ff:ff:ff:ff:ff link-netnsid 4
inet6 fe80::6499:feff:fe30:d2e1/64 scope link
   valid_lft forever preferred_lft forever
40: vethef16d6b0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP
link/ether c2:7f:73:93:85:fc brd ff:ff:ff:ff:ff:ff link-netnsid 5
inet6 fe80::c07f:73ff:fe93:85fc/64 scope link
   valid_lft forever preferred_lft forever
[root@10-10-88-170 ~]#
[root@10-10-88-170 ~]# brctl show
bridge name    bridge id   STP enabled interfaces
cni0    8000.0a580af40201   no  veth3a69de99
   veth786e1634
   vethc8ca82e9
   vethef16d6b0
docker0    8000.024243a1fcad   no
[root@10-10-88-170 ~]#

此時兩個Pod的網(wǎng)絡(luò)連接如圖所示:

如何分析Kubernetes中的容器網(wǎng)絡(luò)

網(wǎng)絡(luò)包從Container A發(fā)送到Container B的過程如下:


1. 網(wǎng)絡(luò)包從busybox1的eth0發(fā)出,并通過vethef16d6b0進入到root netns(網(wǎng)絡(luò)包從vEth的一端發(fā)送后另一端會立馬收到)。

2. 網(wǎng)絡(luò)包被傳到網(wǎng)橋cni0,網(wǎng)橋通過發(fā)送“who has this IP?”的ARP請求來發(fā)現(xiàn)網(wǎng)絡(luò)包需要轉(zhuǎn)發(fā)到的目的地(10.244.2.208)。

3. busybox2回答到它有這個IP,所以網(wǎng)橋知道應(yīng)該把網(wǎng)絡(luò)包轉(zhuǎn)發(fā)到veth786e1634(busybox2)。

4. 網(wǎng)絡(luò)包到達veth786e1634接口,并通過vEth進入到busybox2的netns,從而完成網(wǎng)絡(luò)包從一個容器busybox1到另一個容器busybox2的過程。


對于以上流程有疑問的同學(xué)也可以自己動手驗證一下結(jié)論,最好的方式就是通過tcpdump命令在各個網(wǎng)絡(luò)接口上進行抓包驗證,看網(wǎng)絡(luò)包是如何經(jīng)過網(wǎng)橋再由veth pair流轉(zhuǎn)到另一個容器網(wǎng)絡(luò)當(dāng)中的。

容器網(wǎng)絡(luò)在很大程度上依托于虛擬網(wǎng)絡(luò)的發(fā)展,這也正是技術(shù)發(fā)展的趨勢所在,正所謂站在巨人的肩膀上。

關(guān)于如何分析Kubernetes中的容器網(wǎng)絡(luò)就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI