Docker網路(一)---------原生支援
在部署大規模Docker叢集時,網路成為了最大挑戰。
純粹的Docker原生網路功能無法滿足廣大雲端計算廠商的需要,於是一大批第三方的SDN解決方案如雨後春筍般湧現出來,如Pipework, Weave, Flannel, SocketPlane等。
2015年3月,Docker宣佈收購SocketPlane,一個新的Docker子專案"Libnetwork"開始醞釀。一個月後,Libnetwork在github上正式與開發者見面,預示著Docker開始在網路方面發力。
Libnetwork
libnetwork提出了新的容器網路模型(CNM, Container Network Model),定義了標準的API用於為容器配置網路,其底層可以適配各種網路驅動。如下圖所示:
CNM有三個重要的元件
沙盒:
沙盒是一個隔離的網路執行環境,儲存了容器網路棧的配置,包括了對網路介面,路由表和DNS配置的管理。在Linux上,是用Network Namespace實現的,在其他平臺上可能有不同的實現,比如FreeBSD Jail。一個沙盒可以包含來自多個網路的多個Endpoint。
Endpoint:
一個Endpoint用於將沙盒加入一個網路。Endpoint的實現可以是veth pair或者OVS內部埠。當前libnetwork的實現是veth pair.一個Endpoint只能屬於一個沙盒及一個網路。通過給沙盒增加多個Endpoint可以將一個沙盒加入多個網路。
網路:
網路由一組能夠相互通訊的Endpoint組成。網路的實現可以是linux bridge,vlan等。
Libnetwork的出現使得Docker具備了跨主機多子網的能力,同一個子網內的不同容器可以執行在不同的主機上。
Libnetwork目前已經實現了5種驅動:
bridge:
Docker預設的容器網路驅動。Container通過一對veth pair連線到docker0網橋上,由Docker為容器動態分配IP及配置路由,防火牆規則等。
host:
容器與主機共享同一Network Namespace,共享同一套網路協議棧,路由表及iptables規則等。容器與主機看到的是相同的網路檢視。
null:
容器內網路配置為空,需要使用者手動為容器配置網路介面及路由。
remote:
Docker網路外掛的實現。Remote driver使得Libnetwork可以通過HTTP RESTFUL API對接第三方的網路方案,類似SocketPlane的SDN方案,只要實現了約定的HTTP URL處理函式及底層的網路介面配置方法,就可以替換Docker原生的網路實現。
overlay:
Docker原生的跨主機多子網網路方案。主要通過使用Linux bridge和vxlan隧道實現,底層通過類似於etcd和consul的KV儲存系統實現多機的資訊同步。
基本網路配置
Docker網路初探
- none:不為容器配置任何網路
$ docker run --net=none -it --name d1 debian ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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
[email protected]:~$
可以看到容器僅有一個lo環回介面,使用--net=none啟動容器之後,仍然可以手動為容器配置網路。
- container:與另一個執行中的容器共享Network namespace,共享相同的網路檢視。
首先以預設網路配置(birdge模式)啟動一個容器,設定hostname為dockeNet,dns為8.8.4.4
$ docker run -h dockerNet --dns 8.8.4.4 -itd debian bash
b8e47afe77af504ca8c46d52b1a7e709aa9abae8532df74ace8887d87887dd24
[email protected]:~$ docker exec -it b8e4 bash
[email protected]:/# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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
25: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:0c brd ff:ff:ff:ff:ff:ff
inet 172.17.0.12/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:c/64 scope link
valid_lft forever preferred_lft forever
[email protected]:/#
然後以--net=container:b8e4方式啟動另一個容器:
$ docker run --net=container:b8e4 -it debian bash
[email protected]:/# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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
25: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:0c brd ff:ff:ff:ff:ff:ff
inet 172.17.0.12/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:c/64 scope link
valid_lft forever preferred_lft forever
[email protected]:/#
可以看到,使用--net=container:b8e4引數啟動容器,其IP地址,DNS,hostname都繼承了容器b8e4。實質上兩個容器是共享同一個Network Namespace的,自然網路配置也是完全相同的。
- host:與主機共享Root Network Namespace,容器有完整的許可權可以操縱主機的協議棧,路由表和防火牆等,所以是不安全的。
相應的,host模式啟動時需要指定--net=host引數。
$ docker run -it --net=host debian bash
[email protected]:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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: ens34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
link/ether 00:0c:29:30:e2:e6 brd ff:ff:ff:ff:ff:ff
inet 192.168.124.6/24 brd 192.168.124.255 scope global dynamic ens34
valid_lft 74081sec preferred_lft 74081sec
inet6 fe80::3b06:154f:8b60:76e5/64 scope link
valid_lft forever preferred_lft forever
3: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:30:e2:dc brd ff:ff:ff:ff:ff:ff
inet 192.168.17.140/24 brd 192.168.17.255 scope global dynamic ens33
valid_lft 1339sec preferred_lft 1339sec
inet6 fe80::d1e2:6e4d:a046:a2ed/64 scope link
valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:4d:eb:09:d0 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:4dff:feeb:9d0/64 scope link
valid_lft forever preferred_lft forever
[email protected]:/#
host模式下,容器可以操縱主機的網路配置,這是很危險的,除非萬不得已,應該儘可能避免使用host模式。
- bridge:Docker設計的NAT網路模型。
Docker daemon啟動時會在主機建立一個Linux網橋(預設為docker0,可通過-b引數手動指定)。容器啟動時,Docker會建立一對veth pair裝置,veth裝置的特點是成對存在,從一端進入的資料會同時出現在另一端。Docker會將一端掛載到docker0網橋上,另一端放入容器的Network Namespace內,從而實現容器與主機通訊的目的。
bridge模式下的風格拓撲如下圖所示:
在橋接模式下,Docker容器與Internet的通訊,以及不同容器之間的通訊,都是通過iptables控制的。
Docker網路的初始化動作包括:建立docker0網橋,為docker0網橋新建子網及路由,建立相應的iptables規則等。
我們可以檢視主機的路由表:
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.17.2 0.0.0.0 UG 100 0 0 ens33
0.0.0.0 192.168.124.1 0.0.0.0 UG 101 0 0 ens34
169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 ens33
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
檢視iptables:
# iptables -vnL
Chain INPUT (policy ACCEPT 93127 packets, 32M bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
678 109K DOCKER-ISOLATION all -- * * 0.0.0.0/0 0.0.0.0/0
326 76320 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
326 76320 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
352 32742 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 58576 packets, 27M bytes)
pkts bytes target prot opt in out source destination
Chain DOCKER (1 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.2 tcp dpt:12384
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.2 tcp dpt:12383
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.4 tcp dpt:12382
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.9 tcp dpt:12380
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.9 tcp dpt:2379
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.11 tcp dpt:8080
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.5 tcp dpt:12381
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.6 tcp dpt:4443
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.7 tcp dpt:2375
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.3 tcp dpt:2376
0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.8 tcp dpt:4443
Chain DOCKER-ISOLATION (1 references)
pkts bytes target prot opt in out source destination
678 109K RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
[email protected]:~#
檢視docker0網橋:
# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02424deb09d0 no veth0f652b1
veth11e2546
veth3d102a8
veth4c5b172
veth9b3638d
vethab542f0
vethae532c8
vethcd8f58a
vethd23a8f0
vethf32305b
vethf8df181
每個bridge模式啟動的容器都會在docker0上建立一個veth.
- overlay:Docker原生的跨主機多子網模型
overlay網路模型比較複雜,底層需要類似consul或etcd的KV儲存系統進行訊息同步,核心是通過linux bridge與vxlan隧道實現跨主機劃分子網。
如下圖所示:每建立一個網路,Docker會在主機上建立一個單獨的沙盒,沙盒的實現實質上是一個Network Namespace。在沙盒中,Docker會建立名為br0的網橋,並在網橋上增加一個vxlan介面,每個網路佔用一個vxlan ID,當前Docker建立vlxan隧道的ID範圍為256~1000,因而最多可以建立745個網路。當新增一個容器到某一個網路上時,Docker會建立一對veth網絡卡裝置,一端連線到此網路相關沙盒內的br0網橋上,別一端放入容器沙盒內,並設定br0的IP地址作為容器內路由預設的閘道器地址,從而實現容器加入網路的目的。
容器1和容器4屬於一個網路,容器1需要通過256號vxlan隧道訪問另一臺主機的容器4.Docker通過vxlan和linux網橋實現了跨主機的虛擬子網功能。