本系列文章將介紹 Docker的相關知識:html
(1)Docker 安裝及基本用法python
(2)Docker 鏡像linux
(3)Docker 容器的隔離性 - 使用 Linux namespace 隔離容器的運行環境git
(4)Docker 容器的隔離性 - 使用 cgroups 限制容器使用的資源github
(5)Docker 網絡web
用一張圖來講明 Docker 網絡的基本概況:docker
Docker 容器默認使用 bridge 模式的網絡。其特色以下:數據庫
iptables 的 SNTA 規則,使得從容器離開去外界的網絡包的源 IP 地址被轉換爲 Docker 主機的IP地址:json
Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 MASQUERADE all -- 172.18.0.0/16 0.0.0.0/0
效果是這樣的:網絡
(圖片來源)
示意圖:
定義:
Host 模式並無爲容器建立一個隔離的網絡環境。而之因此稱之爲host模式,是由於該模式下的 Docker 容器會和 host 宿主機共享同一個網絡 namespace,故 Docker Container能夠和宿主機同樣,使用宿主機的eth0,實現和外界的通訊。換言之,Docker Container的 IP 地址即爲宿主機 eth0 的 IP 地址。其特色包括:
實驗:
(1)啓動一個 host 網絡模式的容器
docker run -d --name hostc1 --network host -p 5001:5001 training/webapp python app.py
(2)檢查其 network namespace,其中能夠看到主機上的全部網絡設備
root@docker2:/home/sammy# ln -s /proc/28353/ns/net /var/run/netns/hostc1 root@docker2:/home/sammy# ip netns hostc1 root@docker2:/home/sammy# ip netns exec hostc1 No command specified root@docker2:/home/sammy# ip netns exec hostc1 ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 08:00:27:d4:66:75 brd ff:ff:ff:ff:ff:ff inet 192.168.1.20/24 brd 192.168.1.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fed4:6675/64 scope link valid_lft forever preferred_lft forever
......
示意圖:
定義:
Container 網絡模式是 Docker 中一種較爲特別的網絡的模式。處於這個模式下的 Docker 容器會共享其餘容器的網絡環境,所以,至少這兩個容器之間不存在網絡隔離,而這兩個容器又與宿主機以及除此以外其餘的容器存在網絡隔離。
實驗:
(1)啓動一個容器:
docker run -d --name hostcs1 -p 5001:5001 training/webapp python app.py
(2)啓動另外一個容器,並使用第一個容器的 network namespace
docker run -d --name hostcs2 --network container:hostcs1 training/webapp python app.py
注意:由於此時兩個容器要共享一個 network namespace,所以須要注意端口衝突狀況,不然第二個容器將沒法被啓動。
示意圖:
定義:
網絡模式爲 none,即不爲 Docker 容器構造任何網絡環境。一旦Docker 容器採用了none 網絡模式,那麼容器內部就只能使用loopback網絡設備,不會再有其餘的網絡資源。Docker Container的none網絡模式意味着不給該容器建立任何網絡環境,容器只能使用127.0.0.1的本機網絡。
實驗:
(1)建立並啓動一個容器: docker run -d --name hostn1 --network none training/webapp python app.py
(2)檢查其網絡設備,除了 loopback 設備外沒有其它設備
root@docker2:/home/sammy# ip netns exec hostn1 ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 inet6 ::1/128 scope host valid_lft forever preferred_lft forever
Docker 多節點網絡模式能夠分爲兩類,一類是 Docker 在 1.19 版本中引入的基於 VxLAN 的對跨節點網絡的原生支持;另外一種是經過插件(plugin)方式引入的第三方實現方案,好比 Flannel,Calico 等等。
Docker 1.19 版本中增長了對 overlay 網絡的原生支持。Docker 支持 Consul, Etcd, 和 ZooKeeper 三種分佈式key-value 存儲。其中,etcd 是一個高可用的分佈式 k/v存儲系統,使用etcd的場景默認處理的數據都是控制數據,對於應用數據,只推薦數據量很小,可是更新訪問頻繁的狀況。
準備三個節點:
在 devstack 上使用Docker 啓動 etcd 容器:
export HostIP="192.168.1.18" docker run -d -v /usr/share/ca-certificates/:/etc/ssl/certs -p 4001:4001 -p 2380:2380 -p 2379:2379 \ --name etcd quay.io/coreos/etcd \
/usr/local/bin/etcd \ -name etcd0 \ -advertise-client-urls http://${HostIP}:2379,http://${HostIP}:4001 \ -listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 \ -initial-advertise-peer-urls http://${HostIP}:2380 \ -listen-peer-urls http://0.0.0.0:2380 \ -initial-cluster-token etcd-cluster-1 \ -initial-cluster etcd0=http://${HostIP}:2380 \ -initial-cluster-state new
使用 Docker 啓動 etcd 請參考 https://coreos.com/etcd/docs/latest/docker_guide.html。不過,應該是由於製造鏡像所使用的Dockerfile 緣由,官網上的命令由於少了上面紅色字體部分而會形成啓動失敗:
b847195507addf4fb5a01751eb9c4101416a13db4a8a835e1c2fa1db1e6f364e docker: Error response from daemon: oci runtime error: exec: "-name": executable file not found in $PATH.
添加紅色部分後,容器能夠被正確建立:
root@devstack:/# docker exec -it 179cd52b494d /usr/local/bin/etcdctl cluster-health member 5d72823aca0e00be is healthy: got healthy result from http://:2379 cluster is healthy
root@devstack:/home/sammy# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 179cd52b494d quay.io/coreos/etcd "/usr/local/bin/etcd " 8 seconds ago Up 8 seconds 0.0.0.0:2379-2380->2379-2380/tcp, 0.0.0.0:4001->4001/tcp etcd root@devstack:/home/sammy# netstat -nltp | grep 2380 tcp6 0 0 :::2380 :::* LISTEN 4072/docker-proxy root@devstack:/home/sammy# netstat -nltp | grep 4001 tcp6 0 0 :::4001 :::* LISTEN 4047/docker-proxy
在docker1 和 docker2 節點上修改 /etc/default/docker,添加:
DOCKER_OPTS="--cluster-store=etcd://192.168.1.18:2379 --cluster-advertise=192.168.1.20:2379"
而後分別重啓 docker deamon。注意,要使用IP地址;要是使用 hostname 的話,docker 服務將啓動失敗:
root@docker2:/home/sammy# docker ps An error occurred trying to connect: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json: read unix @->/var/run/docker.sock: read: connection reset by peer
(1)在docker1上運行下面的命令建立一個 overlay 網絡:
root@docker1:/home/sammy# docker network create -d overlay overlaynet1 1de982804f632169380609b9be7c1466b0064dce661a8f4c9e30d781e79fc45a root@docker1:/home/sammy# docker network inspect overlaynet1 [ { "Name": "overlaynet1", "Id": "1de982804f632169380609b9be7c1466b0064dce661a8f4c9e30d781e79fc45a", "Scope": "global", "Driver": "overlay", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "10.0.0.0/24", "Gateway": "10.0.0.1/24" } ] }, "Internal": false, "Containers": {}, "Options": {}, "Labels": {} } ]
在 docker2 上你也會看到這個網絡,說明經過 etcd,網絡數據是分佈式而不是本地的了。
(2)在網絡中建立容器
在 docker2 上,運行 docker run -d --name over2 --network overlaynet1 training/webapp python app.py
在 docker1 上,運行 docker run -d --name over1 --network overlaynet1 training/webapp python app.py
進入容器 over2,發現它有兩塊網卡:
root@docker2:/home/sammy# ln -s /proc/23576/ns/net /var/run/netns/over2 root@docker2:/home/sammy# ip netns over2 root@docker2:/home/sammy# ip netns exec over2 ip a 22: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link/ether 02:42:0a:00:00:02 brd ff:ff:ff:ff:ff:ff inet 10.0.0.2/24 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::42:aff:fe00:2/64 scope link valid_lft forever preferred_lft forever 24: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff inet 172.19.0.2/16 scope global eth1 valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe13:2/64 scope link valid_lft forever preferred_lft forever
其中 eth1 的網絡是一個內部的網段,其實它走的仍是普通的 NAT 模式;而 eth0 是 overlay 網段上分配的IP地址,也就是它走的是 overlay 網絡,它的 MTU 是 1450 而不是 1500.
進一步查看它的路由表,你會發現只有同一個 overlay 網絡中的容器之間的通訊纔會經過 eth0,其它全部通訊仍是走 eth1.
root@docker2:/home/sammy# ip netns exec over2 route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.19.0.1 0.0.0.0 UG 0 0 0 eth1 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 172.19.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth1
先看此時的網絡拓撲圖:
可見:
ov-000100-1de98 的初始情形:
root@docker1:/home/sammy# ip -d link show dev vx-000100-1de98 8: vx-000100-1de98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master ov-000100-1de98 state UNKNOWN mode DEFAULT group default link/ether 22:3c:3f:8f:94:f6 brd ff:ff:ff:ff:ff:ff promiscuity 1 vxlan id 256 port 32768 61000 proxy l2miss l3miss ageing 300 root@docker1:/home/sammy# bridge fdb show dev vx-000100-1de98 22:3c:3f:8f:94:f6 vlan 0 permanent
這裏很明顯的一個問題是,vxlan dev vx-000100-1de98 的 fdb 表內容不全,致使從容器1 ping 容器2 不通。待選的解決方式不外乎下面幾種:
Docker 從某種程度上利用了第一種和第三種方式的組合,首先Docker 利用 consul 以及 etcd 這樣的分佈式 key/value 存儲來保存IP地址映射關係,另外一方面個Docker 節點也經過某種協議來直接廣告映射關係。
爲了測試,中間重啓了 docker1 節點,發現 over1 容器沒法啓動,報錯以下:
docker: Error response from daemon: network sandbox join failed: could not get network sandbox (oper true): failed get network namespace "": no such file or directory.
根據 https://github.com/docker/docker/issues/25215,這是 Docker 的一個bug,fix 剛剛推出。一個 workaround 是從新建立 overlay network。
回到容器之間沒法ping通對方的問題,尚不知道根本緣由是什麼(想吐槽Docker目前的問題真很多)。要使得互相 ping 能工做,至少必須具有下面的條件:
在 docker1 上,
在 docker 2 上,
使用 iperf 工具檢查測試了一下性能並作對比:
類型 | TCP | UDP |
Overlay 網絡中的兩個容器之間 (A) | 913 Mbits/sec | 1.05 Mbits/sec |
Bridge/NAT 網絡中的兩個容器之間 (B) | 1.73 Gbits/sec | |
主機間 (C) | 2.06 Gbits/sec | 1.05 Mbits/sec |
主機到另外一個主機上的 bridge 網絡模式的容器 (D) | 1.88 Gbits/sec | |
主機到本主機上的容器 (E) | 20.5 Gbits/sec | |
主機到另外一個主機上的 host 網絡模式的容器 (F) | 2.02 Gbits/sec | 1.05 Mbits/sec |
容器 Overlay 效率 (A/C) | 44% | 100% ? |
單個 NAT 效率 (D/C) | 91% | |
兩個 NAT 效率 (B/C) | 83% | |
Host 網絡模式效率 (F/C) | 98% | 100% |
兩臺主機是同一個物理機上的兩個虛機,所以,結果的絕對值其實沒多少意義,相對值有必定的參考性。
文章 Testing Docker multi-host network performance 對比了多種網絡模式下的性能,結果以下:
看起來這個表裏面的數據和個人表裏面的數據差不了太多。
參考連接: