筆者在《Docker 基礎 : 網絡配置》一文中簡單介紹了容器網絡的基本用法,當時網絡的基本使用方式還處於 --link 階段。時過境遷,隨着 docker 的快速發展,其網絡架構也在不斷的演進。本文主要介紹 docker 的 CNM 網絡模型以及新引入的 network 子命令。
Docker 網絡架構
Docker 在 1.9 版本中引入了一整套的 docker network 子命令和跨主機網絡支持。這允許用戶可以根據他們應用的拓撲架構創建虛擬網絡並將容器接入其所對應的網絡。其實,早在 docker 1.7 版本中,網絡部分代碼就已經被抽離並單獨成為了 docker 的網絡庫,即 libnetwork。在此之后,容器的網絡模式也被抽象變成了統一接口的驅動。
為了標准化網絡驅動的開發步驟和支持多種網絡驅動,docker 公司在 libnetwork 中使用了 CNM(Container Network Model)。CNM 定義了構建容器虛擬化網絡的模型,同時還提供了可以用於開發多種網絡驅動的標准化接口和組件。Libnetwork 和 docker daemon 及各個網絡驅動的關系可以通過下圖形象的表示:
上圖中,docker daemon 通過調用 libnetwork 對外提供的 API 完成網絡的創建和管理等功能。Libnetwork 內部則使用了 CNM 來實現網絡功能。CNM 中主要有沙盒(sandbox)、端點(endpoint) 和網絡(network) 3 種組件。Libnetwork 中內置的 5 種驅動則為 libnetwork 提供了不同類型的網絡服務。下面分別對 CNM 中的 3 個核心組件和 libnetwork 中的 5 種內置驅動進行介紹。
CNM 中的 3 個核心組件如下
- 沙盒:一個沙盒包含了一個容器網絡棧的信息。沙盒可以對容器的接口(interface)、路由和 DNS 設置等進行管理。沙盒的實現可以是 Linux network namespace、FreeBSD Jail 或者類似的機制。一個沙盒可以有多個端點和多個網絡。
- 端點:一個端點可以加入一個沙盒和一個網絡。端點的實現可以是 veth pair、Open vSwitch 內部端口或者相似的設備。一個端點可以屬於一個網絡並且只屬於一個沙盒。
- 網絡:一個網絡是一組可以直接互相聯通的端點。網絡的實現可以是 Linux bridge、VLAN等。一個網絡可以包含多個端點。
Libnetwork 中的 5 中內置驅動如下
bridge 驅動:這是 docker 設置的默認驅動。當使用 bridge 驅動時,libnetwork 將創建出來的 docker 容器連接到 docker0 網橋上。對於單機模式,bridge 驅動已經可以滿足基本的需求了。但是這種模式下容器使用 NAT 方式與外界通信,這就增加了通信的復雜性。
host 驅動:使用 host 驅動的時候,libnetwork 不會為容器創建網絡協議棧,即不會創建獨立的 network namespace。Docker 容器中的進程處於宿主機的網絡環境中,相當於容器和宿主機共用同一個 network namespace,容器共享使用宿主機的網卡、IP 和端口等資源。Host 模式很好的解決了容器與外界通信的地址轉換問題,可以直接使用宿主機的 IP 進行通信,不存在虛擬化網絡帶來的開銷。但是 host 驅動也降低了容器與容器之間、容器與宿主機之間網絡的隔離性,引起網絡資源的競爭和沖突。因此可以認為 host 驅動適用於對容器集群規模不大的場景。
overlay 驅動:overlay 驅動采用 IETF 標准的 VXLAN 方式,並且是 VXLAN 中被普遍認為最適合大規模的雲計算虛擬化環境的 SDN controller 模式。在使用的過程中,還需要一個額外的配置存儲服務,比如 Consul、etcd 或 ZooKeeper 等。並且在啟動 docker daemon 的時候需要添加額外的參數來指定所使用的配置存儲服務地址。
remote 驅動:這個驅動實際上並未做真正的網絡服務實現,而是調用了用戶自行實現的網絡驅動插件,是 libnetwork 實現了驅動的插件化,更好地滿足了用戶的多樣化需求。用戶只要根據 libnetwork 提供的協議標准實現其接口並注冊即可。
null 驅動:使用這種驅動的時候,docker 容器擁有字段的 network namespace,但是並不為 docker 容器進行任何網絡配置。也就是說,這個容器除了 network namespace 自帶的 loopback 網卡外,沒有任何其它網卡、IP、路由等信息,需要用戶為該容器添加網卡、配置 IP 等。這種模式如果不進行特定的配置是無法正常使用網絡的,但是優點也非常明顯,它給了用戶最大的自由度來自定義容器的網絡環境。
CNM 網絡示例
這里我們介紹一個 libnetwork 示例的搭建過程,並在搭建成功后對其中容器之間的連通性進行驗證。下圖展示了 CNM 網絡示例的組成結構:
在本例中,我使用 docker 默認的 bridge 驅動創建了一個網絡拓撲應用:
- 它有兩個網絡,其中 backend network 為后端網絡,frontend network 則為前端網絡,兩個網絡互不連通。
- 其中 con1 和 con3 各擁有一個端點,並且分別加入到后端網絡和前端網絡中。而 con2 則有兩個端點,它們分別加入到后端網絡和前端網絡中。
下面的命令分別創建名為 backend 和 frontend 的兩個網絡:
$ docker network create backend $ docker network create frontend $ docker network ls
上圖中除了剛才創建的 backend 和 frontend 之外,還有三個網絡 bridge、host 和 none。這三個網絡是 docker daemon 默認創建的,我們無法通過 docker network rm 命令進行刪除。
在創建了所需的兩個網絡之后,我們來創建三個容器 con1、con2 和 con3,並分別把 con1 和 con2 加入到 backend 網絡中,把 con3 加入到 frontend 網絡中:
$ docker run -it --name con1 --net backend busybox $ docker run -it --name con2 --net backend busybox $ docker run -it --name con3 --net frontend busybox
接下來分別在 con1 和 con3 中 ping con2,因為 con1 和 con2 都在 backend 網絡中,所以兩者可以連通。但是 con3 和 con2 不在一個網絡中,所以它們之間不能連通。
我們查看 con2 中的網卡及其配置:
可以看到,此時容器中只有一塊名為 eth0 的網卡,並且配置了和網橋 backend 同在一個 IP 段的 IP 地址,這個網卡就是 CNM 模型中的端點。然后我們通過下面的命令把 con2 也加入到 frontend 網絡中:
$ docker network connect frontend con2
再來查看 con2 中的網卡及其配置:
這次發現多了一塊名為 eth1 的網卡,並且其 IP 和網橋 frontend 同在一個 IP 段。用 ping 命令測試 con2 與 con3 的連通性:
此時兩者已經連通。由此可知,docker network connect 命令會在所連接的容器中創建新的網卡,以完成容器與所指定網絡的連接。
總結
通過 CNM(Container Network Model),docker 為網絡驅動的開發建立了標准。對於基本的網絡應用場景,使用默認的網絡驅動就能滿足需求。如果用戶有特殊的需求,完全可以依據 CNM 標准來實現自定義的網絡驅動。
參考:
《docker 容器與容器雲》