深入淺出Docker 讀書筆記(五)

                                         第12章 Docker 網絡

Docker 在容器內部運行應用,這些應用之間的交互依賴於大量不同的網絡,這意味着 Docker 需要強大的網絡功能。幸運的是Docker 對於容器之間、容器與外部網絡和 VLAN 之間的連接均有相應的解決方案。Docker 網絡架構源自一種叫作容器網絡模型(CNM)的方案,該方案是開源的並且支持插接式連接。Libnetwork 是 Docker 對 CNM 的一種實現,提供了 Docker 核心網絡架構的全部功能。不同的驅動可以通過插拔的方式接入 Libnetwork 來提供定製化的網絡拓撲。爲了實現開箱即用的效果,Docker 封裝了一系列本地驅動,覆蓋了大部分常見的網絡需求。其中包括單機橋接網絡(Single-Host Bridge Network)、多機覆蓋網絡(Multi-Host Overlay),並且支持接入現有 VLAN。Docker 生態系統中的合作伙伴通過提供驅動的方式,進一步拓展了 Docker 的網絡功能。Libnetwork 提供了本地服務發現和基礎的容器負載均衡解決方案。在頂層設計中,Docker 網絡架構由 3 個主要部分構成:1)CNM :一種設計標準。在 CNM中規定了 Docker 網絡架構的基礎組成要素。抽象來講CNM 定義了 3 個基本要素:沙盒(Sandbox)、終端(Endpoint)和網絡(Network)。

  • 沙盒是一個獨立的網絡棧。其中包括以太網接口、端口、路由表以及 DNS 配置。
  • 終端就是虛擬網絡接口。就像普通網絡接口一樣,終端主要職責是負責創建連接。在CNM中終端負責將沙盒連接到網絡。
  • 網絡是 802.1d 網橋(類似的交換機)的軟件實現。因此網絡就是需要交互的終端的集合,並且終端之間相互獨立。

CNM組件與容器進行關聯

​Docker 環境中最小的調度單位就是容器,CNM就負責爲容器提供網絡功能。上圖展示了CNM組件是如何與容器進行關聯的—沙盒被放置在容器內部,爲容器提供網絡連接。容器 A 只有一個接口(終端)並連接到了網絡 A。容器 B 有兩個接口(終端)並且分別接入了網絡 A 和網絡 B。容器 A 與 B 之間是可以相互通信的,因爲都接入了網絡 A。但是如果沒有三層路由器的支持,容器 B 的兩個終端之間是不能進行通信的。終端與常見的網絡適配器類似,這意味着終端只能接入某一個網絡。因此如果容器需要接入到多個網絡,就需要多個終端

2)Libnetwork 是 CNM 的具體實現,並且被 Docker 採用,它通過 Go 語言編寫,並實現了 CNM 中列舉的核心組件。CNM 是設計規範文檔,Libnetwork 是標準的實現。Libnetwork 一個開源、Go編寫、跨平臺且被 Docker 所使用。在 Docker 早期階段,網絡部分代碼都存在於 daemon 當中。daemon 變得臃腫,並且不符合 UNIX 工具模塊化設計原則,即既能獨立工作,又易於集成到其他項目。所以Docker 將該網絡部分從 daemon 中拆分,並重構爲一個叫作 Libnetwork 的外部類庫。現在,Docker 核心網絡架構代碼都在 Libnetwork 當中。Libnetwork 實現了 CNM 中定義的全部 3 個組件。此外它還實現了本地服務發現(Service Discovery)、基於 Ingress 的容器負載均衡,以及網絡控制層和管理層功能。

3)驅動通過實現特定網絡拓撲的方式來拓展該模型的能力。如果說 Libnetwork 實現了控制層和管理層功能,那麼驅動就負責實現數據層。比如網絡連通性和隔離性是由驅動來處理的,驅動層實際創建網絡對象也是如此,其關係如下圖所示。

控制層、管理層與數據層的關係

Docker 封裝了原生驅動或者本地驅動若干內置驅動,在 Linux 上包括 Bridge、Overlay 以及 Macvlan,在 Windows 上包括 NAT、Overlay、Transport 以及 L2 Bridge。第三方也可以編寫 Docker 網絡驅動。這些驅動叫作遠程驅動,例如 Calico、Contiv、Kuryr 以及 Weave。每個驅動都負責其上所有網絡資源的創建和管理。舉例說明,一個叫作“prod-fe-cuda”的覆蓋網絡由 Overlay 驅動所有並管理。這意味着 Overlay 驅動會在創建、管理和刪除其上網絡資源的時候被調用。爲了滿足複雜且不固定的環境需求,Libnetwork 支持同時激活多個網絡驅動。這意味着 Docker 環境可以支持一個龐大的異構網下圖展示了頂層設計中的每個部分是如何組裝在一起的。

頂層設計

​最簡單的Docker網絡就是單機橋接網絡。單機意味着該網絡只能在單個 Docker 主機上運行,並且只能與所在 Docker 主機上的容器進行連接,橋接意味着這是 802.1.d 橋接的一種實現(二層交換機)。Linux Docker 創建單機橋接網絡採用內置的橋接驅動,而 Windows Docker 創建時使用內置的 NAT 驅動。實際上,這兩種驅動工作起來毫無差異。下圖展示了兩個均包含相同本地橋接網絡 mynet 的 Docker 主機。雖然網絡是相同的,但卻是兩個獨立的網絡。這意味着圖中容器無法直接進行通信,因爲並不在一個網絡當中。

容器無法直接進行通信

 

每個 Docker 主機都有一個默認的單機橋接網絡。在 Linux 上網絡名稱爲 bridge,在 Windows 上叫作 nat。除非讀者通過命令行創建容器時指定參數--network,否則默認情況下新創建的容器都會連接到該網絡。在 Linux 主機上Docker 網絡由 Bridge 驅動創建,而 Bridge 底層是基於 Linux 內核意味着 Bridge 是高性能並且非常穩定的,可以通過標準的 Linux 工具來查看這些網絡$ ip link show docker0     在 Linux Docker 主機之上,默認的“bridge”網絡被映射到內核中爲“docker0”的 Linux 網橋。使用 docker network create 命令創建新的單機橋接網絡,名爲“localnet”。//Linux $ docker network create -d bridge localnet //Windows   > docker network create -d nat localnet。如果使用 Linux主機內核中還會創建一個新的 Linux 網橋。使用 Linux brctl 工具來查看系統中的 Linux 網橋。$ brctl show 輸出內容中包含了兩個網橋。第一行是前文提過的 docker0 網橋,該網橋對應 Docker 中的默認網絡 bridge;第二個網橋(br-20c)與新建的“localnet”Docker 橋接網絡相對應。兩個網橋目前都沒有開啓 STP,並且也都沒有任何設備接入(對應的 interfaces 列爲空)。接下來創建一個新的容器,並接入到新建橋接網絡 localnet 當中。在Windows 上進行操作需要將命令中“alpine sleep 1d”替換爲“microsoft/powershell:nanoserver pwsh.exe -Command Start-Sleep 86400”。$ docker container run -d --name c1 \        --network localnet \             alpine sleep 1d    容器現在接入了 localnet 網絡當中。

 

c1的網絡接口連接到了br-20c2e8ae4bbb網橋

 

如果在相同網絡中繼續接入新的容器,那麼在新接入容器中是可以通過“c1”的容器名稱來 ping 通的。這是因爲新容器都註冊到了指定的 Docker DNS 服務,所以相同網絡中的容器可以解析其他容器的名稱。Linux 上默認的 Bridge 網絡是不支持通過 Docker DNS 服務進行域名解析的。自定義橋接網絡可以, 到目前爲止前面提到的橋接網絡中的容器只能與位於相同網絡中的容器進行通信。但是,可以使用端口映射(Port Mapping)來繞開這個限制。端口映射允許將某個容器端口映射到 Docker 主機端口上。對於配置中指定的 Docker 主機端口,任何發送到該端口的流量,都會被轉發到容器。下圖中展示了具體流量動向。

 

具體流量動向

 

如上圖所示,容器內部應用開放端口爲 80。該端口被映射到了 Docker 主機的 10.0.0.15 接口的 5000 端口之上。最終結果就是訪問 10.0.0.15:5000 的所有流量都被轉發到了容器的 80 端口。能夠將容器化應用連接到外部系統以及物理網絡的能力是非常必要的。常見的例子是部分容器化的應用——應用中已容器化的部分需要與那些運行在物理網絡和 VLAN 上的未容器化部分進行通信。Docker內置的 Macvlan 驅動(Windows 上是 Transparent)通過爲容器提供 MAC 和 IP 地址,讓容器在物理網絡上成爲“一等公民”。下圖展示了具體內容。

 

將容器化應用連接到網絡

 

Macvlan 的優點是性能優異,因爲無須端口映射或者額外橋接,可以直接通過主機接口(或者子接口)訪問容器接口。但是,Macvlan 的缺點是需要將主機網卡(NIC)設置爲混雜模式(Promiscuous Mode),這在大部分公有云平臺上是不允許的。
所以 Macvlan 對於公司內部的數據中心網絡來說很棒(假設公司網絡組能接受 NIC 設置爲混雜模式),但是 Macvlan 在公有云上並不可行。假設有一個物理網絡,其上配置了兩個 VLAN 100:10.0.0.0/24 和 VLAN 200:192.168.3.0/24接下來,添加一個 Docker 主機並連接到該網絡,如下圖所示。
 

添加Docker主機並連接到該網絡

 

有一個需求是將容器接入 VLAN 100。爲了實現該需求,首先使用 Macvlan 驅動創建新的 Docker 網絡。但是,Macvlan 驅動在連接到目標網絡前,需要設置幾個參數。比如以下幾點。1) 子網信息。2) 網關。3) 可分配給容器的IP範圍。4) 主機使用的接口或者子接口。Docker Macvlan 驅動基於穩定可靠的同名 Linux內核驅動構建而成。因此,Macvlan 也支持 VLAN 的 Trunk 功能。這意味着可以在相同的 Docker 主機上創建多個 Macvlan 網絡,並且將容器按照下圖的方式連接起來。

 

連接容器


以上內容基本能涵蓋 Macvlan。Windows 也提供了類似的解決方案 Transparent 驅動。用於故障排除的容器和服務日誌當認爲遇到容器間網絡連接問題時,檢查 daemon 日誌以及容器日誌(應用日誌)是非常有必要的。在 Windows 上,daemon 日誌存儲在 ∼AppData\Local\Docker,可以通過 Windows 事件查看器來瀏覽。在 Linux 上,daemon 日誌的存儲位置取決於當前系統正在使用的初始化方式。如果是 Systemd,日誌會存儲在 Journald,可以通過 journalctl -u docker.service 命令查看。

服務發現:Libnetwork 還提供了一些重要的網絡服務——服務發現。它允許容器和 Swarm 服務通過名稱互相定位。唯一的要求就是需要處於同一個網絡當中。其底層實現是利用了 Docker 內置的 DNS 服務器,爲每個容器提供 DNS 解析功能。下圖展示了容器“c1”通過名稱 ping 容器“c2”的過程。Swarm 服務原理相同。
 

容器“c1”通過名稱ping容器“c2”


1) ping c2 命令調用本地 DNS 解析器,嘗試將“c2”解析爲具體 IP 地址。每個 Docker 容器都有本地 DNS 解析器。
2) 如果本地解析器在本地緩存中沒有找到“c2”對應的 IP 地址,本地解析器會向 Docker DNS 服務器發起一個遞歸查詢。本地服務解析器是預先配置好並知道 Docker DNS 服務器細節的。
3) Docker DNS 服務器記錄了全部容器名稱和 IP 地址的映射關係,其中容器名稱是容器在創建時通過 --name 或者 --net-alias 參數設置的。這意味着 Docker DNS 服務器知道容器“c2”的 IP 地址。
4) DNS 服務返回“c2”對應的 IP 地址到“c1”本地 DNS 解析器。之所以會這樣是因爲兩個容器位於相同的網絡當中,如果所處網絡不同則該命令不可行。
5) ping 命令被髮往“c2”對應的 IP 地址。
每個啓動時使用了 --name 參數的 Swarm 服務或者獨立的容器,都會將自己的名稱和 IP 地址註冊到 Docker DNS 服務。這意味着容器和服務副本可以通過 Docker DNS 服務互相發現。但是,服務發現是受網絡限制的,這意味着名稱解析只對位於同一網絡中的容器和服務生效。如果兩個容器在不同的網絡,那麼就不能互相解析。用戶可以爲 Swarm 服務和獨立容器進行自定義DNS 配置。舉個例子,--dns 參數允許讀者指定自定義的 DNS 服務列表,以防出現內置的 Docker DNS 服務器解析失敗的況。此外也可以使用 --dns-search 參數指定自定義查詢時所使用的域名(例如當查詢名稱並非完整域名的時候)。

服務發佈: Docker Swarm 支持兩種服務發佈模式,兩種模式均保證服務從集羣外可訪問。1) Ingress模式(默認)。2)Host模式。通過 Ingress 模式發佈的服務,可以保證從 Swarm 集羣內任一節點(即使沒有運行服務的副本)都能訪問該服務;以 Host 模式發佈的服務只能通過運行服務副本的節點來訪問。下圖展示了兩種模式的區別。

Ingress模式與Host模式

Ingress 模式是默認方式,這意味着任何時候讀者通過 -p 或者 --publish 發佈服務的時候,默認都是 Ingress 模式;如果需要以 Host 模式發佈服務,則讀者需要使用 --publish 參數的完整格式,並添加 mode=host。下面一起來看 Host 模式的例子。

$ docker service create -d --name svc1 \                 published=5000 表示服務通過端口 5000 提
--publish published=5000,target=80,mode=host \  供外部服務target=80表示發送到published
nginx                                                                       端口5000的請求會映射到服務副本的 80 端口之上mode=host 表示只有外部請求發送到運行了服務副本的節點纔可以訪問該服務。通常使用 Ingress 模式。在底層,Ingress 模式採用名爲 Service Mesh 或者 Swarm Mode Service Mesh 的四層路由網絡來實現。下圖展示了 Ingress 模式下一個外部請求是如何流轉,最終訪問到服務的。

Ingress模式下訪問服務


上圖中最上方命令部署了一個名爲“svc1”的 Swarm 服務。該服務連接到了 overnet 網絡,併發布到 5000 端口。按上述方式發佈 Swarm 服務(--publish published=5000,target=80)會在 Ingress 網絡的 5000 端口進行發佈。因爲 Swarm 全部節點都接入了 Ingress 網絡,所以這個端口被髮布到了Swarm範圍內。集羣確保到達 Ingress 網絡中任意節點的 5000 端口的流量,都會被路由到 80 端口的“svc1”服務。當前“svc1”服務只部署了一個副本,集羣中有一條映射規則:“所有訪問 Ingress 網絡 5000 端口的流量都需要路由到運行了“svc1”服務副本的節點之上”。紅線展示了訪問 Node 的 15000 端口的流量,通過 Ingress 網絡,被路由到了 Node2 節點正在運行的服務副本之上。入站流量可能訪問 4 個 Swarm 節點中的任意一個,但是結果都是一樣的,瞭解這一點很重要。這是因爲服務通過 Ingress 網絡實現了 Swarm 範圍內的發佈。此外:如果存在多個運行中的副本,流量會平均到每個副本之上,如下圖中展示的一樣。

流量平均到每個副本之上

以下是Docker網絡子命令如下表所示

子命令 說明
docker network connect 將容器連接到網絡。
docker network create 創建新的 Docker 網絡。默認情況下,在 Windows 上會採用 NAT 驅動,在 Linux 上會採用
Bridge 驅動。可以使用 -d 參數指定驅動(網絡類型)。
docker network disconnect 斷開容器的網絡。
docker network inspect 提供 Docker 網絡的詳細配置信息。
docker network ls 用於列出運行在本地 Docker 主機上的全部網絡。
docker network prune 刪除 Docker 主機上全部未使用的網絡。
docker network rm 刪除 Docker 主機上指定網絡。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章