Docker容器網絡通信的那些事兒

  • 2020 年 2 月 17 日
  • 筆記

戳藍字「TopCoder」關注我們哦!

編者註:Docker是基於go語言開發,Linux下的底層技術主要基於cgroups、namespace以及聯合文件技術實現的一種進程級別的輕量級虛擬化解決方案。由於Docker進程隔離獨立於宿主機上其他進程,因此也稱為容器,Docker在容器的基礎上,進行了更進一步的封裝,從文件系統、網絡到進程隔離等,極大簡化了容器的創建管理維護工作,降低了開發者使用門檻,因此才在近幾年流行開來(畢竟Docker的底層技術在Docker出現之前就已經存在了)。

Docker作為一種容器技術,在目前的分佈式和微服務系統中被廣泛使用,因為要在多個容器或機器間進行通信,因此Docker網絡通信是一個重要的技術點。從網絡架構的角度來看,所有的容器實際上是通過本地主機的網橋接口(docker0)進行相互通信,就像物理機器通過物理交換機通信一樣

Docker服務啟動時會首先在主機上自動創建一個docker0虛擬網橋,實際上是一個Linux網橋。網橋可以理解為一個軟件交換機,負責掛載其上的接口之間進行包轉發。同時,Docker隨機分配一個本地未佔用的私有網段(在RFC1918中定義)中的一個地址給docker0接口。比如典型的172.17.0.0/16網段,掩碼為255.255.0.0,此後啟動的容器內的網口也會自動分配一個該網段的地址。

當創建一個Docker容器的時候,同時會創建了一對veth pair互聯接口。當向任一個接口發送包時,另外一個接口自動收到相同的包。互聯接口的一端位於容器內,即eth0;另一端在本地並被掛載到docker0網橋,名稱以veth開頭。通過這種方式,主機可以與容器通信,容器之間也可以相互通信。如此一來,Docker就創建了在主機和所有容器之間一個虛擬共享網絡:

默認情況下,Docker容器可以主動訪問到外部網絡的連接,但是外部網絡無法訪問到容器,可通過命令 docker port container查看對應容器的端口映射信息。Docker容器使用的是私有網絡IP,那麼容器訪問外部流程是什麼樣的呢?

假設容器內部的網絡地址為172.17.0.2,本地網絡地址為10.0.2.2,容器要能訪問外部網絡,源地址不能為172.17.0.2,需要進行源地址映射(Source NAT, SNAT),修改為本地系統的IP地址10.0.2.2。映射是通過iptables的源地址偽裝操作實現的。查看主機nat表上POSTROUTING鏈的規則。該鏈負責網包要離開主機前,改寫其源地址。其中,上述規則將所有源地址在172.17.0.0/16網段,且不是從docker0接口發出的流量(即從容器中出來的流量),動態偽裝為從系統網卡發出。MASQUERADE行動與傳統SNAT行動相比,好處是能動態地從網卡獲取地址。

知道了容器內部訪問外部流程,如果外部想要訪問內部該如何實現呢?容器允許外部訪問,可以在docker [container] run時候通過-p或-P參數來啟用容器內外端口的映射配置。不管用哪種辦法,其實也是在本地的iptable的nat表中添加相應的規則,將訪問外部IP地址的包進行目標地址DNAT,將目標地址修改為容器的IP地址。每次創建一個新容器的時候,Docker從可用的地址段中選擇一個空閑的IP地址分配給容器的eth0端口,並且使用本地主機上docker0接口的IP作為容器的默認網關。目前docker不支持啟動時配置ip地址。

上面所說的是docker容器的默認網絡通信模式—bridge模式,容器擁有獨立的網絡命名空間和網絡協議棧,如果容器啟動過程中不添加--net參數配置,則默認採用這種網絡通信默認。除了bridge模式之外,還可以配置host網絡模式,直接使用容器宿主機的網絡命名空間,該模式下容器不再擁有自己獨立的網絡環境,直接使用宿主機的IP和端口。當然用戶也可自定義網絡模式或者none模式等。