1. 程式人生 > >Docker網路(一)---------原生支援

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網橋實現了跨主機的虛擬子網功能。