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 常常使用 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 UNKNOWNlink/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UPlink/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0valid_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 UNKNOWNlink/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UPlink/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0valid_lft forever preferred_lft forever
?