智匯華雲|Docker容器網絡解析

2020-12-19 華雲數據

企業IT建設開始大規模使用Docker時,你會發現需要了解很多關於網絡的知識。作為目前最火的輕量級容器技術,Docker有很多令人稱道的功能,如Docker的鏡像管理。

然而,Docker的建設也會碰到一系列的挑戰,根據調查報告指出,容器的網絡和安全實現成為容器雲平臺建設最主要的挑戰,當企業開始將重要的企業核心應用遷移至容器平臺,如果缺乏足夠的網絡和安全管控將會給業務上線帶來潛在威脅。

因此,我們有必要深入了解Docker的網絡知識,以滿足更高的網絡需求。本期「智匯華雲」專欄為您帶來「 Docker容器網絡解析」。

本期嘉賓

華雲數據計算網絡開發組網絡技術經理 黃茂峰

Docker 容器網絡的發展歷史

在 Dokcer 發布之初,Docker 是將網絡、管理、安全等集成在一起的,其中網絡模塊可以為容器提供橋接網絡、主機網絡等簡單的網絡功能。

從 1.7 版本開始,Docker正是把網絡和存儲這兩部分的功能都以插件化形式剝離出來,允許用戶通過指令來選擇不同的後端實現。剝離出來的獨立容器網絡項目叫 libnetwork。

在 1.9 版本時,Docker 又引入了一整套 network 子命令和跨主機網絡支持,這允許用戶可以根據他們應用的拓撲結構創建虛擬網絡並將容器接入其所對應的網絡。

什麼是 Docker Libnetwork

為了標準化網絡的驅動開發步驟和支持多種網絡驅動,Docker 將網絡部分代碼被抽離成為了單獨的網絡庫(Libnetwork),Libnetwork 提供了可以用於開發多種網絡驅動的標準化接口和組件。

Docker daemon 通過調用 Libnetwork 對外提供的 API 完成網絡的創建和管理等功能,Libnetwork 內置了5種驅動來提供不同類型的網絡: bridge driver, host driver, null driver, overlay driver, remote driver

bridge driver

此驅動為Docker的默認設置驅動,使用這個驅動的時候,libnetwork將創建出來的Docker容器連接到Docker網橋上。作為最常規的模式,bridge模式已經可以滿足Docker容器最基本的使用需求了。然而其與外界通信使用NAT,增加了通信的複雜性,在複雜場景下使用會有諸多限制。

host driver

使用這種驅動的時候,libnetwork將不為Docker容器創建網絡協議棧,即不會創建獨立的network namespace。Docker容器中的進程處於宿主機的網絡環境中,相當於Docker容器和宿主機共同用一個network namespace,使用宿主機的網卡、IP和埠等信息。

但是,容器其他方面,如文件系統、進程列表等還是和宿主機隔離的。host模式很好地解決了容器與外界通信的地址轉換問題,可以直接使用宿主機的IP進行通信,不存在虛擬化網絡帶來的額外性能負擔。但是host驅動也降低了容器與容器之間、容器與宿主機之間網絡層面的隔離性,引起網絡資源的競爭與衝突。

因此可以認為host驅動適用於對於容器集群規模不大的場景。

null driver

使用這種驅動的時候,Docker容器擁有自己的network namespace,但是並不為Docker容器進行任何網絡配置。也就是說,這個Docker容器除了network namespace自帶的loopback網卡名,沒有其他任何網卡、IP、路由等信息,需要用戶為Docker容器添加網卡、配置IP等。

這種模式如果不進行特定的配置是無法正常使用的,但是優點也非常明顯,它給了用戶最大的自由度來自定義容器的網絡環境。

overlay driver

此驅動採用IETE標準的VXLAN方式,並且是VXLAN中被普遍認為最適合大規模的雲計算虛擬化環境的SDN controller模式。在使用過程中,需要一個額外的配置存儲服務,例如Consul、etcd和zookeeper。還需要在啟動Docker daemon的時候額外添加參數來指定所使用的配置存儲服務地址。

remote Driver

這個驅動實際上並未做真正的網絡服務實現,而是調用了用戶自行實現的網絡驅動插件,使libnetwork實現了驅動的可插件化,更好地滿足了用戶的多種需求。用戶只需要根據libnetwork提供的協議標準,實現其所要求的各個接口並向Docker daemon進行註冊。

什麼是 Docker CNM

Docker Libnetwork 中使用了 CNM 的容器網絡模式概念,CNM定義了構建容器虛擬化網絡的模型,此後容器網絡模式也被抽象變成了統一接口的驅動。

CNM 中主要有 sandbox、endpoint 和 network 3 種核心組件CNM 中核心組件的使用模型如下圖:

沙盒 (sandbox):一個沙盒包含了一個容器網絡棧的信息。沙盒可以對容器的接口(interface)、路由和 DNS 設置等進行管理,沙盒的實現可以是Linux netns、FreeBSD Jail 或者類似的機制,一個沙盒可以有多個端點和多個網絡。

端點 (endpoint):一個端點可以加入一個沙盒和一個網絡。端點的實現可以是 veth pair、ovs 內部埠或者相似的設備,一個端點只屬於一個網絡並且只屬於一個沙盒。

網絡 (network):一個網絡是一組可以直接互相聯通的端點。網絡的實現可以是 Linux bridge、VLAN等,一個網絡可以包含多個端點。

Libnetwork Remote driver

kuryr-libnetwork 是 Libnetwork 框架下的一種 remote driver 實現,現在已經成為Docker 官網推薦的一個 remote driver,kuryr-libnetwork 需要做的就是實現 Libnetwork remote driver 需要實現的接口.

常見的 remote driver 要實現的接口如下,格式:HTTP POST + JSON Body

/Plugin.Activate no payload -- Handshake/NetworkDriver.GetCapabilities -- Set capability

/NetworkDriver.DiscoverNew -- DiscoverNew Notification

/NetworkDriver.DiscoverDelete -- DiscoverDelete Notification

/NetworkDriver.AllocateNetwork -- Allocate network specific resources, only called in docker swarm mode

/NetworkDriver.FreeNetwork -- Free network specific resources, only called in docker swarm mode

/NetworkDriver.CreateNetwork -- Create network

/NetworkDriver.DeleteNetwork -- Delete network

/NetworkDriver.CreateEndpoint -- Create endpoint

/NetworkDriver.EndpointOperInfo -- Endpoint operational info

/NetworkDriver.DeleteEndpoint -- Delete endpoint

/NetworkDriver.Join -- Join an endpoint to a sandbox

/NetworkDriver.Leave -- Remove an endpoint from a sandbox

IPAM Driver

在 Libnetwork 中,CNM 模塊通過 IPAM Driver 管理 IP 地址的分配,Libnetwork 內含有一個默認的IPAM驅動,同時它也允許動態地增加第三方IPAM驅動。

在用戶創建網絡時可以指定 Libnetwork 使用的 IPAM 驅動, Kuryr 項目通過實現了 IPAM 的驅動接口,成為了Docker 的第三方 libnetwork IPAM driver。

常見的 IPAM driver 要實現的接口如下,格式:HTTP POST + JSON Body

/IpamDriver.GetCapabilities -- provides the IPAM driver capabilities. it's called during the registration of the IPAM driver.

/IpamDriver.GetDefaultAddressSpaces -- returns the default local and global address space names for this IPAM. it's called after the registration of the IPAM driver

/IpamDriver.RequestPool -- registering an address pool with the IPAM driver. multiple identical calls must return the same result.

/IpamDriver.RequestAddress -- allocates the IP address

/IpamDriver.ReleaseAddress -- deallocates the IP address

/IpamDriver.ReleasePool -- releasing a previously registered address pool

Docker 網絡的生命周期

Docker 用戶可以通過與 CNM 的 Object 以及 API 的交互來管理對應容器的網絡,下面是一個典型的容器網絡生命周期:

1、Driver要向NetworkController註冊。內置的Driver在Libnetwork內註冊,遠程的Driver則通過Plugin mechanism註冊。每一個Driver處理特定的networkType。

2、libnetwork.New():NetworkController通過libnetwork.New()創建,用於Network的創建以及通過一些特定的Options配置Driver。

3、controller.NewNetwork():Network通過給這個API提供name和networkType來創建,networkType參數用來選擇特定的Driver並且將創建的Network和該Driver相關聯。從此以後,對於Network的任何操作都由Driver處理。controller.NewNetwork() 還有一個可選的options參數,用於提供特定Driver的options和Labels。

4、network.CreateEndpoint():可以用於在給定的Network中創建一個新的Endpoint。同時該API還有一個可選的options參數供Driver使用。這個"options"既可以攜帶已知的labels,也可以攜帶和特定Driver相關的labels。之後調用相應的Driver的driver.CreateEndpoint,它可以為在一個Endpoint在Network中被創建時,為它們保留IP位址。Driver會通過driverapi中定義的InterfaceInfo進行這些地址的賦值。IP位址將和endpoint暴露的埠用來完善Endpoint作為Service的定義。事實上,Service endpoint不是其他什麼東西,僅僅只是一個網絡地址以及該應用的容器監聽的埠號。

5、endpoint.Join():用於將Endpoint與一個容器相連接。Join操作會先創建一個Sandbox如果對應的容器中還沒有的話。Driver可以使用Sandbox Key來識別連接到同一個容器的多個Endpoint。這個API同樣接受可選的options參數供Driver使用。

- 雖然這並不是Libnetwork直接的設計要求,但是我們鼓勵像Docker這樣的用戶在執行容器的Start()操作時,即在容器可以操作之前,調用endpoint.Join()。

- 另一個關於endpoint.join()這個API經常被提到的問題是,為什麼我們需要一個API創建Endpoint和另一個API來join endpoint。事實上Endpoint代表的是一個Service,它可能有,也可能並沒有容器支持。當一個Endpoint被創建的時候,會預留它所需的資源,因此任何容器都能連接該Endpoint並且獲得一個一致的網絡行為。

6、endpoint.Leave():會在容器停止的時候被調用。Driver可以清除它在調用Join()時獲取的狀態。Libnetwork會在最後一個Endpoint離開的時候刪除Sandbox。但是只要該Endpoint依舊存在,Libnetwork會依然保有IP位址並且在有新的容器加入的時候進行重用。這保證了容器的資源在停止並重啟的過程中能夠重用。

7、endpoint.Delete():用於從一個Network中刪除Endpoint。這將導致Endpoint的刪除以及清空緩存的sandbox.Info。

8、network.Delete():用於刪除Network。如果還有Endpoint連接到該網絡,Libnetwork是不允許對它進行刪除的。

Docker 網絡命名空間

docker 常常使用 linux netns 實現網絡資源隔離,但使用 ip netns 命令卻無法查看,這是因為 docker 默認把創建的網絡命名空間連結文件隱藏起來了,導致 ip netns 命令無法讀取,可以通過下面的方法復現 docker 的 ip netns 命名空間。

# 創建一個帶有橋接網絡的 docker 容器

$ docker run -it -d --rm --name mytest --network bridge cirros /bin/sh

c093857c756028b4d4f37b16262d017239236bde22a3545f8769fd17366f183a

$ docker ps | grep mytest

c093857c7560 cirros "/bin/sh" 6 seconds ago Up 2 seconds mytest

# 可以通過 inspect 命令查看該容器的 ip 地址和進程號

$ docker inspect mytest |egrep '"IPAddress"|"Pid"'

"Pid": 14908,

"IPAddress": "172.17.0.2",

# 通過進程號參考容器進程

$ ps -fp 14908

UID PID PPID C STIME CMD

root 14889 1676 0 11:42 containerd-shim -namespace moby \

-workdir

/var/lib/containerd/io.containerd.runtime.v1.linux/moby/c093857c756028b4d4f37b16262d017239236bde22a3545f8769fd17366f183a \

-address /run/containerd/containerd.sock \

-containerd-binary /usr/bin/containerd \

-runtime-root /var/run/docker/runtime-runc

# 通過 nsenter 進入容器網絡空間

$ nsenter --target 14908 --net ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN

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

54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP

link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0

inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0

valid_lft forever preferred_lft forever

# 通過軟連接容器命名空間實現在 ip netns 下顯示

$ ls /proc/14908/ns/net

lrwxrwxrwx 1 root root 0 Jul 25 11:42 /proc/14908/ns/net -> net:[4026532445]

$ ln -s /proc/14908/ns/net /var/run/netns/mytest

# 最後檢查一下

$ ip netns

mytest (id: 1)

$ ip netns exec mytest ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN

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

54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP

link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0

inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0

valid_lft forever preferred_lft forever

Docker 主機名與DNS

一個鏡像可以啟動多個容器,但是它們的主機名和網絡信息並不一樣,也即是說主機名和網絡信息並非是被寫入鏡像中的。實際上容器中/etc/目錄下有三個文件是容器啟動後被虛擬文件覆蓋的,分別是/etc/hostname、/etc/hosts、/etc/resolv.conf。對這三個文件的修改不會被docker commit保存,也就是不會保存在鏡像中,重啟容器也會導致修改失效。

$ docker exec -it mytest mount | grep etc

/dev/mapper/centos-root on /etc/resolv.conf type xfs (rw,relatime,attr2,inode64,noquota)

/dev/mapper/centos-root on /etc/hostname type xfs (rw,relatime,attr2,inode64,noquota)

/dev/mapper/centos-root on /etc/hosts type xfs (rw,relatime,attr2,inode64,noquota)

- 這樣能解決主機名的問題,同時也能讓DNS及時更新(改變resolv.conf)。

- 由於這些文件的維護方法隨著Docker版本演進而不斷變化,因此儘量不修改這些文件,而是通過Docker提供的參數進行相關設置。

參考資料

https://github.com/docker/libnetwork/blob/master/docs/design.md

http://dockone.io/article/1306

https://www.oreilly.com/library/view/learning-docker-networking/9781785280955/

https://feisky.gitbooks.io/sdn/container/cnm/

https://www.nuagenetworks.net/blog/container-networking-standards/

想要了解更多華雲數據的相關信息,歡迎撥打諮詢熱線:400-808-4000

相關焦點

  • Docker 容器的網絡
    使用默認網絡來運行一個容器Docker 能夠支持通過 network drivers來使用網絡的容器。在默認的情況下,Docker 為你提供了 2 個網絡驅動: bridge 和 overlay 驅動。
  • Docker 添加容器到一個網絡
    要創建一個安全並且能夠協同運行的 Web 應用程式,你需要創建一個網絡。通過網絡,在默認情況下為容器提供了完全獨立的環境。在你第一次運行一個容器的時候,你可以將容器添加到一個網絡中。例如,我們希望運行一個容器來運行 PostgreSQL 資料庫,並且傳遞 --net=my_bridge 標記來到你新網絡的連接中,可以運行下面的命令:$ docker run -d --net=my_bridge --name db training/postgres如果你檢查你的
  • docker鏡像及容器命令
    鏡像列表基本狀態解析各個選項說明:- **REPOSITORY:**表示鏡像的倉庫源- **TAG:**鏡像的標籤- **IMAGE ID:**鏡像ID- **CREATED:**鏡像創建時間運行容器docker run <容器名>3. 查看容器列表docker ps -a4.
  • docker容器的啟動方式
    容器(Container):  容器,從認識上來說,就是類創建的實例,就是依據鏡像這個模板創建出來的實體。容器的實質是進程,但與直接在宿主執行的進程不同,容器進程運行於屬於自己的獨立的命名空間。因此容器可以擁有自己的root 文件系統、自己的網絡配置、自己的進程空間,甚至自己的用戶ID 空間。
  • Docker系列教程02-操作Docker容器
    t分配一個tty偽終端,支持終端登錄2)啟動容器使用docker create命令新建的容器處理停止狀態,可使用docker start啟動它。[root@qll251 ~]# docker run -it ubuntu /bin/bashroot@8b18b6758bb6:/#docker run相當於執行了兩個步驟:將鏡像放入容器中(docker create),然後啟動容器(docker start)。
  • 【Docker】系列教程02-操作Docker容器
    ~]# docker run -it ubuntu /bin/bashroot@8b18b6758bb6:/#docker run相當於執行了兩個步驟:將鏡像放入容器中(docker create),然後啟動容器(docker start)。
  • 攻擊和審計Docker容器01
    Docker容器鏡像是一個輕量級,獨立的可執行軟體包,包含運行應用程式所需的一切:代碼,運行時,系統工具,系統庫和設置。容器鏡像在運行時成為容器,在Docker容器的情況下- 鏡像在Docker Engine上運行時成為容器。適用於基於Linux和Windows的應用程式,無論基礎架構如何,容器化軟體都將始終運行相同。容器將軟體與其環境隔離開來,並確保它可以統一工作,儘管開發和演示之間存在差異。
  • Docker入門知識|Docker資源容器 與 VM虛擬機的區別與聯繫
    每當伺服器啟動執行Hypervisor這個進程時,它會給每一臺虛擬機分配適量的內存、CPU、網絡和磁碟,並加載所有虛擬機的客戶作業系統。hypervisor仍然存在一些性能和資源使用效率方面的小問題,在大運算量有時候就成了一種瓶頸與制約,因此基於容器(Container)的新型虛擬化技術成為真實的新晉網紅。這個網紅代表就是Docker。
  • 如何開始docker - docker架構及創建容器
    docker的組成docker是採用C/S模式,使用遠程API來管理創建docker。容器的創建過程: 鏡像倉庫 ->pull 鏡像到本地 -> 本地鏡像 -> run容器 from 鏡像 -> 創建容器並執行程序->程序退出->容器退出1、dockerd服務端(守護進程),dockerd是docker的守護進程。
  • 智匯華雲 OpenStack路由器之拓撲解析
    本期智匯華雲,特別邀請到華雲數據網絡技術經理黃茂峰為大家帶來「OpenStack路由器之拓撲解析」。可以看到,VM 1 和VM 2 需要經過網絡節點上的 Legacy Router 進行互通,該互通的網絡流量也稱為東西向網絡流量;同時,VM 1 和 VM 2 如果想和外網進行通信,也需要經過網絡節點上的 Legacy Router 和外網交換機與外部網絡進行交互,這種交互的網絡流量也稱為南北向網絡流量。
  • Docker集群管理之Docker Compose
    使用Docker Compose你只需要在一個配置文件中定義多個Docker容器,然後使用一條命令將多個容器啟動,Docker Compose會通過解析容器件的依賴關係(link, 網絡容器 –net-from或數據容器 –volume-from)按先後順序啟動所定義的容器。
  • 容器到底是個啥?(附Docker學習資源匯總)
    容器中運行的就是一個或者多個應用程式,以及應用運行所需要的環境。容器直接運行在作業系統內核之上的用戶空間。容器技術可以讓多個獨立的用戶空間運行在同一臺宿主機上。容器既可以運行在物理機也可以運行在虛擬機上,當然也可以運行在公有雲主機上。
  • 雲計算核心技術Docker教程:Docker容器使用
    要退出客戶端程序,直接輸入 exit:查看所有的容器命令如下:$ docker ps -a使用 docker start 啟動一個已停止的容器:$ docker start <容器 ID>$ docker run -itd --name ubuntu-test ubuntu /bin/bash要停止一個容器運行使用如下命令:$ docker stop <容器 ID>要重啟一個容器運行使用如下命令:
  • 理解docker鏡像與容器
    docker有兩個重要的概念:鏡像,容器。首先在說一下docker鏡像的存儲位置/var/lib/docker/,在aufs/layer/下可以看到ubuntu15.04鏡像的分層,正是這四層組成了ubuntu15.04鏡像。
  • 通過容器化一個Python Web應用學習Docker容器技術
    Username: liuchunmingPassword:Login Succeeded在push到Docker Hub之前,需要先給鏡像指定一個版本號:$ docker tag helloworld liuchunming/helloworld:v1liuchunming是我在Docker Hub
  • 誰是容器中的「戰鬥機」?Docker與Chef、LXC等容器對比
    以下是我研究 docker 和 LXC 後總結的一些區別。 標準的配置方法 每個 LXC "容器" 之間或許不兼容,但是 docker 採用了一種標準的配置方法使得由不同 docker 創建出的 LXC 能夠完全兼容。 基於應用 LXC 的定位是作為一種虛擬機的替代方案。
  • Docker進階-容器監控cAdvisor+InfluxDB+Granfana
    概述前面文章介紹使用docker compose組合應用並利用scale快速對容器進行擴容。由於docker compose啟動的服務都在同一臺宿主機上,對於一個宿主機上運行多個容器應用時,容器的運行情況如:CPU使用率、內存使用率、網絡狀態、磁碟空間等一系列隨時間變化的時序數據信息,都是需要去了解,因此監控是必須的。
  • Docker 容器資源管理,你真的學會了嗎?
    所以,要想真正掌握 Docker 的核心知識,只靠網絡上零散的信息往往是不夠的,必須系統性地學習。容器,作為 Docker 的核心特性之一,是 Docker 使用者們無法迴避的重要知識點。要想了解容器的核心原理,甚至自己動手寫容器,不深入了解容器資源管理的相關的內容是絕對不行的。
  • CoreOS實踐指南(七):Docker容器管理服務
    操作 docker 的方式與 systemctl、etcdctl 類似,需要由一個二級命令共同組成一個完整的命令。通過 docker pull 命令可以指定的網絡地址拉取鏡像到本地(如果指定的是名稱而不是網絡地址,則會在docker官方的鏡像倉庫裡面搜索,比如下面的兩個例子)。
  • 用docker命令創建一個容器以及容器的暫停和恢復
    docker命令創建一個容器docker創建容器的命令是create,用法和run類似。例如:docker create --name newnginx nginx:latest,這句命令的意思是使用nginx的鏡像來創建一個名叫newnginx的容器。