天天看點

詳解虛拟化網絡(一)--docker網絡單機模型

詳解虛拟化網絡(一)--docker網絡單機模型

本文内容較多,詳細介紹docker網絡單機模型在各個場景下的實作原理,結構如下:

詳解虛拟化網絡(一)--docker網絡單機模型

1.Docker核心原理

Docker容器的的本質就是主控端上的程序.隻不過主控端對它進行了Linux Namesapce隔離,讓它看不到外面的世界,利用cgroups實作資源的限制,通過寫時複制的機制完成高效的檔案操作.同時利用系統調用pivot_root或chroot切換了程序的根目錄,把容器鏡像挂載為根檔案系統rootfs。在rootfs中不僅有要運作的應用程式,還包含了應用的所有依賴庫,以及作業系統的目錄和檔案。rootfs打包了應用運作的完整環境,這樣就保證了在開發、測試、線上等多個場景的一緻性。上面提及的docker實作核心技術namespace和 cgroups,其實并非新技術,它們是linux的相關技術。而docker對這種技術進行了封裝,提高可操作性。

在一般情況下,大多數人會對容器與虛拟機進行比較,為什麼會需要容器?容器到底解決的是什麼問題?可以認為容器極大的提升了軟體的可移植能力。

詳解虛拟化網絡(一)--docker網絡單機模型

        從官網所給的圖檔可以看出,容器和虛拟機的最大差別就是,每個虛拟機都有獨立的作業系統核心,而容器隻是一種特殊的程序,它們共享同一個作業系統核心。直覺來說:Docker是虛拟出作業系統,實作多應用之間的隔離,讓每個應用覺得自己有一個自己的作業系統,而且彼此之間隔離。而虛拟機的思路則完全不一樣,如果程序1和程序2運作于不同的虛拟機,則作業系統都是雙份的,它們感覺自己在不同的虛拟電腦上面跑。由于可見,Docker達到了類似虛拟機的效果,但是又沒有虛拟機的開銷,它虛拟的層次更加高。Docker不虛拟機器,僅僅虛拟應用的運作環境。

        看清了容器的本質,很多問題就容易了解。例如我們執行 docker exec 指令能夠進入運作中的容器,好像登入進獨立的虛拟機一樣。實際上這隻不過是利用系統調用setns,讓目前程序進入到容器程序的Namesapce中,它就能“看到”容器内部的情況了。

2. centos7安裝docker

參照官方文檔安裝方式,輸入以下七條指令

$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2

$ sudo yum-config-manager --add-repo \

https://download.docker.com/linux/centos/docker-ce.repo

$ sudo yum-config-manager --enable docker-ce-edge

$ sudo yum-config-manager --enable docker-ce-test

$ sudo yum-config-manager --disable docker-ce-edge

$ sudo yum install -y docker-ce

$ sudo systemctl start docker

此時若安裝成功,指令行輸入docker info,可看到如下資訊:

詳解虛拟化網絡(一)--docker網絡單機模型

最好從阿裡雲鏡像加速,畢竟如果鏡像檔案長期從國外下載下傳的話,速度還是挺慢的,網址為https://cr.console.aliyun.com/cn-shanghai/repositories,通過修改daemon配置檔案/etc/docker/daemon.json來使用加速器。

執行以下四條指令:

1. sudo mkdir -p /etc/docker

2. sudo tee /etc/docker/daemon.json <<-'EOF'

{

"registry-mirrors": ["自己的容器鏡像服務位址"]

}

EOF

3. sudo systemctl daemon-reload

4. sudo systemctl restart docker

3. Docker單機網絡模型

安裝完docker後,接下來就從大多數人關注的焦點:容器的網絡通信開始講起。上面說到,容器是一種特殊的程序,它們共享同一個作業系統核心,Docker使得容器可以達到類似虛拟機的效果。那麼就牽涉到網絡,如何讓容器之間互相連接配接保持網絡通暢,Docker有多種網絡模型。對于單機上運作的多個容器,可以使用預設的bridge網絡驅動。而容器的跨主機通信,一種常用的方式是利用Overlay 網絡,基于實體網絡的虛拟化網絡來實作。

本文會在單機上實驗展示橋接網絡模型,揭示其背後的實作原理。之後文章會示範容器如何利用Overlay 網絡和路由模式進行跨主機通信。

我們按照下圖建立網絡拓撲,讓容器之間網絡互通,從容器内部可以通路外部資源,同時,容器内可以暴露服務讓外部通路。

詳解虛拟化網絡(一)--docker網絡單機模型

在開始動手實驗之前,先簡單介紹一下上圖bridge網絡模型會用到的Linux虛拟化背景網絡技術。

(1)Veth Pairs

Veth是成對出現的兩張虛拟網卡,從一端發送的資料包,總會在另一端接收到。利用Veth的特性,我們可以将一端的虛拟網卡"放入"容器内,另一端接入虛拟交換機。這樣,接入同一個虛拟交換機(Linux bridge就是虛拟交換機:br0)的容器之間就實作了網絡互通。這也是docker預設的網絡橋接技術,docker在安裝完成後會在目前系統環境下,生成預設的docker0虛拟網橋,之後docker容器運作時,會自動生成Veth Pairs,一端在容器内,另一端在主控端環境下。可以示範如下,先利用docker鏡像建立容器,之後檢視主控端和容器網橋的變化。

現在我們來建立和啟動兩個容器:container1和container2。

詳解虛拟化網絡(一)--docker網絡單機模型

容器啟動後,從主控端的視角,可以看到網絡配置有如下變化:

$ brctl show  //沒有brctl(網橋工具)的話,執行yum install bridge-utils -y下載下傳

詳解虛拟化網絡(一)--docker網絡單機模型

可以看到docker容器運作時,會自動生成Veth Pairs,一端在主控端環境下,那麼另一端我們去容器中檢視,容器中顯示如下:(容器内沒有ip指令的,運作yum -y install initscripts下載下傳)

詳解虛拟化網絡(一)--docker網絡單機模型

是以Docker Daemon建立了兩個veth網絡裝置,并将veth挂接到docker0網橋上了。

Veth Pairs挂在docker0網橋上,這對于container1和 container2來說,就好比用網線将本地網卡(eth0)與網橋裝置docker0的網口連接配接起來一樣。在docker容器網絡預設橋接模式中,veth隻是在二層起作用。

(2)Linux Bridge

交換機是工作在資料鍊路層的網絡裝置,它轉發的是二層網絡包。上面的docker0橋就可以認為是虛拟的網橋。最簡單的轉發政策是将到達交換機輸入端口的封包,廣播到所有的輸出端口。當然更好的政策是在轉發過程中進行學習,記錄交換機端口和MAC位址的映射關系,這樣在下次轉發時就能夠根據封包中的MAC位址,發送到對應的輸出端口。我們可以認為Linux bridge就是虛拟交換機,連接配接在同一個bridge上的容器組成區域網路,不同的bridge之間網絡是隔離的。docker network create [NETWORK NAME]實際上就是建立出虛拟交換機。

從容器角度看來,docker0對于通過veth pair“插在”網橋上的container1和container2來說,首先就是一個二層的交換機的角色:泛洪、維護表,在二層轉發資料包;同時由于docker0自身也具有mac位址(這個與純二層交換機不同),并且綁定了ip(這裡是172.17.0.1),是以在 container中還作為container default路由的預設Gateway而存在。

從主控端視角,docker0可以認為是網卡身份實體交換機提供了由硬體實作的高效的背闆通道,供連接配接在交換機上的主機高效實作二層通信;對于docker0而言,其負責處理二層交換機邏輯以及三層的處理程式其實就是主控端上的Linux核心 tcp/ip協定棧程式。從主控端來看,所有docker0從veth(隻是個二層的存在,沒有綁定ipv4位址)接收到的資料包都會被主控端看成從docker0這塊網卡(綁定172.17.0.1)接收進來的資料包,尤其是在進入三層時,主控端上的iptables就會 對docker0進來的資料包按照iptables rules進行相應處理。

(3)iptables

容器需要能夠通路外部世界,同時也可以暴露服務讓外界通路,這時就要用到iptables。另外,不同bridge之間的隔離也會用到iptables。

在容器化網絡場景,我們經常用到的是在nat表中設定SNAT和DNAT。源位址轉換SNAT是發生在資料包離開機器被發送之前。DNAT是對目标位址的轉換,需要在路由選擇前完成。後面内網位址映射到公網位址,即容器通路外部世界時,再詳細說明。

4. Docker網絡模型單機實驗

        通過前面的背景知識介紹,我們就可以開始動手實驗了。可能會想,既然容器建立啟動時會自動為我們建立好網絡,那麼實驗的目的何在呢?本次将不采用docker預設的網橋的自動配置設定政策,重新建立新網橋br0,手工将veth pairs加到二端,驗證之前的說法。因為涉及到很多系統網絡級設定,建議在一個“幹淨”的虛拟機内操作,以免幹擾到工作環境。(文後會有恢複方法)本次使用的實驗環境是centos7,達到類似下圖的實驗網絡拓撲。

詳解虛拟化網絡(一)--docker網絡單機模型

(1):容器間的網絡互通

 建立“容器”

從前面的背景知識了解到,容器是由 Namespace + Cgroups + rootfs組成。而且容器建立啟動時會自動為我們建立好網絡(docker預設的網橋自動配置設定政策),是以本實驗我們可以僅僅建立出Namespace網絡隔離環境來達到模拟容器的行為。

用ip netns指令操作容器的網絡。

ip netns是常用的namespace的指令,netns是在linux中提供網絡虛拟化的一個項目,使用netns網絡空間虛拟化可以在本地虛拟化出多個網絡環境,目前netns在lxc容器中被用來為容器提供網絡。使用netns建立的網絡空間獨立于目前系統的網絡空間,其中的網絡裝置以及iptables規則等都是獨立的,就好像進入了另外一個網絡一樣。

1. 建立出的網絡Namesapce并檢視

$sudo ip netns add docker0   //增加虛拟網絡命名空間

$sudo ip netns add docker1

$ls -l /var/run/netns        //顯示虛拟網絡命名空間

詳解虛拟化網絡(一)--docker網絡單機模型

2. 建立Veth pairs并檢視

$sudo ip link add veth0 type veth peer name veth1

$sudo ip link add veth2 type veth peer name veth3

$ip addr show

詳解虛拟化網絡(一)--docker網絡單機模型

3. 将Veth的一端放入“容器”

目前二對veth(可認為四張網卡)都在主控端内,設定Veth一端的虛拟網卡的Namespace,相當于将這張網卡放入“容器”内:

$ sudo ip link set veth0 netns docker0

$ sudo ip link set veth2 netns docker1

檢視“容器” docker0 内的網卡:

$ sudo ip netns exec docker0 ip addr show

$ sudo ip netns exec docker1 ip addr show

詳解虛拟化網絡(一)--docker網絡單機模型

此指令意思是在網絡Namesapce  docker0的下執行後面跟着的指令,相當于在“容器”内執行指令。可以看到,veth0已經放入了“容器”docker0内。同樣使用指令sudo ip netns exec docker1 ip addr show檢視“容器”docker1内的網卡。同時,在主控端上檢視網卡ip addr(等同ip a ),發現veth0和veth2已經消失,确實是放入“容器”内了。

詳解虛拟化網絡(一)--docker網絡單機模型

4. 建立bridge

安裝bridge管理工具brctl    //sudo apt-get install bridge-utils

建立bridge br0:

$ sudo brctl addbr br0

5. 将Veth的另一端接入bridge

$ sudo brctl addif br0 veth1

$ sudo brctl addif br0 veth3

檢視接入情況:

$ sudo brctl show

詳解虛拟化網絡(一)--docker網絡單機模型

可以看到兩個網卡veth1和veth3已經“插”在bridge上。

6. 為"容器“内的網卡配置設定IP位址,并激活上線

docker0容器:

sudo ip netns exec docker0 ip addr add 172.18.0.2/24 dev veth0

sudo ip netns exec docker0 ip link set veth0 up

docker1容器:

sudo ip netns exec docker1 ip addr add 172.18.0.3/24 dev veth2

sudo ip netns exec docker1 ip link set veth2 up

7.Veth另一端的網卡激活

sudo ip link set veth1 up

sudo ip link set veth3 up

8. 為bridge配置設定IP位址,激活

sudo ip addr add 172.18.0.1/24 dev br0

sudo ip link set br0 up

9. “容器”間的互通測試

我們可以先設定監聽br0:

sudo tcpdump -i br0 -n

詳解虛拟化網絡(一)--docker網絡單機模型

另外開一個新視窗,從容器docker0 ping 容器docker1:

sudo ip netns exec docker0 ping -c 3 172.18.0.3

詳解虛拟化網絡(一)--docker網絡單機模型

此時br0上監控到的網絡流量:

詳解虛拟化網絡(一)--docker網絡單機模型

可以看到,先是172.18.0.2發起的ARP請求,詢問172.18.0.3的MAC位址,然後是ICMP的請求和響應,最後是172.18.0.3的ARP請求。因為接在同一個bridge br0上,是以是二層互通的區域網路。同樣,從容器docker1 ping 容器docker0也是通的:

(2) 從主控端通路“容器”内網絡

在“容器”docker0内啟動服務,監聽80端口:

sudo ip netns exec docker0 nc -lp 80   //yum install nc -y 沒有nc指令的先安裝

詳解虛拟化網絡(一)--docker網絡單機模型

在主控端上執行telnet,可以連接配接到docker0的80端口,輸入後,看到上圖資料已經連通。

telnet 172.18.0.2 80    //yum install telnet-server -y   yum install telnet -y 安裝telnet

詳解虛拟化網絡(一)--docker網絡單機模型

(3):從“容器”内通路外網

1.配置核心參數

允許IP forwarding,即啟用網關伺服器路由轉發功能:

sudo sysctl net.ipv4.conf.all.forwarding=1

配置iptables FORWARD規則

首先确認iptables FORWARD的預設政策:

sudo iptables -L

詳解虛拟化網絡(一)--docker網絡單機模型

如果預設政策是DROP,需要設定為ACCEPT:

sudo iptables -P FORWARD ACCEPT

預設政策的含義是,在資料包沒有比對到規則時執行的預設動作。

2.将bridge設定為“容器”的預設網關

sudo ip netns exec docker0 route add default gw 172.18.0.1 veth0

sudo ip netns exec docker1 route add default gw 172.18.0.1 veth2

檢視“容器”内的路由表:

sudo ip netns exec docker0 route -n

詳解虛拟化網絡(一)--docker網絡單機模型

可以看出,“容器”内的預設Gateway是bridge的IP位址,非172.18.0.0/24網段的資料包會路由給bridge。

3.配置iptables的SNAT規則

容器的IP位址外部并不認識,如果它要通路外網,需要在資料包離開前将源位址替換為主控端的IP,這樣外部主機才能用主控端的IP作為目的位址發回響應。

需要注意的問題,核心netfilter會追蹤記錄連接配接,在增加了SNAT規則時,系統會自動增加一個隐式的反向規則,這樣傳回的包會自動将主控端的IP替換為容器IP。

sudo iptables -t nat -A POSTROUTING -s 172.18.0.0/24 ! -o br0 -j MASQUERADE

上面的指令的含義是:在nat表的POSTROUTING鍊增加規則,當資料包的源位址為172.18.0.0/24網段,出口裝置不是br0時,就執行MASQUERADE動作。MASQUERADE也是一種源位址轉換動作,它會動态選擇主控端的一個IP做源位址轉換,而SNAT動作必須在指令中指定固定的IP位址。(外網IP位址不穩定的情況下,也可使用MASQUERADE(動态僞裝),能夠自動的尋找外網位址并改為目前正确的外網IP位址。)

4.從“容器”内通路外部位址

sudo ip netns exec docker0 ping -c 3 www.baidu.com

sudo ip netns exec docker1 ping -c 3 www.baidu.com

詳解虛拟化網絡(一)--docker網絡單機模型

我們确認在“容器”内是可以通路外部網絡的。

5.附加說明docker預設政策

更加深入一點的話,我們可以通過網絡抓包,來檢視從容器内部通路外部世界這個過程,到底發生了什麼?參照上面的設定,我們從docker自身的網絡政策來進行分析,看看docker在建立容器并啟動時通路外部世界的過程。這也适用于我們實驗。

1.首先確定實體本機可以連接配接外網

2.docker run -it  busybox

Busybox将衆多的UNIX指令集合進一個很小的可執行程式中,可以用來替代GNU fileutils、shellutils等工具集。簡單來說,就算你啟動了一個新的centos容器并啟動,想檢視網絡資訊,運作指令時會發現,好多指令不存在,因為我們下載下傳的鏡像檔案中并不包含這些指令,docker的輕量化就在于它精簡了許多額外的東西,隻保留最核心的部分,而Busybox這個鏡像正好有我們需要的指令工具集。

由下圖可以看到:在容器内部可以通路外網。(busybox 位于 docker0 上)

詳解虛拟化網絡(一)--docker網絡單機模型

3.檢視iptables政策,了解政策原理。

當 busybox 從容器向外 ping 時,資料包是怎樣到達 www.baidu.com 的呢?這裡的關鍵就是 NAT。我們檢視一下 主控端 上的 iptables 規則:

詳解虛拟化網絡(一)--docker網絡單機模型

圖中規則是由

iptables -t nat -s -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

生成的。(docker自己會做好這些,手動實驗的話,需要自己執行此指令達到上圖效果)其含義是:如果網橋 docker0 收到來自 172.17.0.0/16 網段的外出包,把它交給 MASQUERADE 處理。而 MASQUERADE 的處理方式是将包的源位址替換成 host 的位址發送出去,即做了一次網絡位址轉換(NAT)。

SNAT即源位址轉換,能夠讓多個内網使用者通過一個外網位址上網,解決了IP資源匮乏的問題。一個無線路由器也就使用此技術。

在外網IP位址不穩定的情況下,即可使用MASQUERADE(動态僞裝),能夠自動的尋找外網位址并改為目前正确的外網IP位址。

4.開啟一個終端運作,監控docker0網卡:

tcpdump -i docker0 -n icmp

5.在另一個終端:進容器ping百度docker run -it busybox

ping www.baidu.com

tcpdump -i docker0 -n icmp的結果,發現容器的IP位址交給了MASQUERADE處理

詳解虛拟化網絡(一)--docker網絡單機模型

6.結束上一個終端,監控實體機網卡

tcpdump -i eth0 -n icmp

發現已經将容器的位址進行轉換了,成了實體機的IP位址。172.19.219.91

詳解虛拟化網絡(一)--docker網絡單機模型

可以看到容器内去ping外部世界,經過iptables規則轉換後,變成了主控端去通路外部世界。至于網絡源位址轉換(NAT)的中間位址103.235.46.39,可以自己手動設定,不設定的話,容器會自動的選擇。

借用用一張圖來說明這個過程:

詳解虛拟化網絡(一)--docker網絡單機模型

(4)從外部通路“容器”内暴露的服務

配置iptables的DNAT規則

當外部通過主控端的IP和端口通路容器内啟動的服務時,在資料包進入PREROUTING階段就要進行目的位址轉換,将主控端IP轉換為容器IP。同樣,系統會為我們自動增加一個隐式的反向規則,資料包在離開主控端時自動做反向轉換。

sudo iptables -t nat -A PREROUTING ! -i br0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80

上面指令的含義是:在nat表的PREROUTING鍊增加規則,當輸入裝置不是br0,目的端口為80時,做目的位址轉換,将主控端IP替換為容器IP。

從遠端通路“容器”内暴露的服務

在“容器”docker0内啟動服務:

sudo ip netns exec docker0 nc -lp 80

在和主控端同一個區域網路的遠端主機通路主控端IP:80

telnet ip 80 後再容器所在的主控端上可以看到同一個區域網路的遠端主機發送的資訊。

确認可以通路到容器内啟動的服務,原理同上。

詳解虛拟化網絡(一)--docker網絡單機模型

實驗環境恢複

删除虛拟網絡裝置

sudo ip link set br0 down

sudo brctl delbr br0

sudo ip link del veth1

sudo ip link del veth3

然後重新開機機器iptablers和Namesapce的配置在機器重新開機後被清除。

總結

在本文的虛拟化網絡詳解(1)docker網絡單機模型中,我們在介紹了veth pairs、Linux bridge、iptables等概念後,親自動手模拟出了docker bridge橋接網絡模型,并實驗了四種場景的網絡互通。實際上docker network 就是使用了上述技術,幫我們建立和維護網絡。通過動手實驗,相信你對docker bridge網絡了解的更加深入。

下一篇我将動手實驗容器如何利用Overlay 網絡以及路由模式進行跨主機通信,後續對k8s以及openshift網絡都會一一實踐,所謂萬丈高樓平地起,docker作為k8s以及oc的底層實作,弄懂它是我們了解虛拟化網絡的開始與基礎,請大家持續關注公衆号。

繼續閱讀