1. 程式人生 > >Docker實戰(三)Docker 跨主機網路overlay、macvlan和flannel

Docker實戰(三)Docker 跨主機網路overlay、macvlan和flannel

跨主機網路概述

docker跨主機網路方案包括:

  • docker 原生的 overlaymacvlan
  • 第三方方案:常用的包括 flannel、weave 和 calico。

docker通過libnetwork 以及 CNM 將眾多方案整合在一起。

libnetwork 是 docker 容器網路庫,最核心的內容是其定義的 Container Network Model (CNM),這個模型對容器網路進行了抽象,由以下三類元件組成:

  • Sandbox:Sandbox 是容器的網路棧,包含容器的 interface、路由表和 DNS 設定。 Linux Network Namespace 是 Sandbox 的標準實現。Sandbox 可以包含來自不同 Network 的 Endpoint。
  • Endpoint:Endpoint 的作用是將 Sandbox 接入 Network。Endpoint 的典型實現是 veth pair。一個 Endpoint 只能屬於一個網路,也只能屬於一個 Sandbox。
  • Network:Network 包含一組 Endpoint,同一 Network 的 Endpoint 可以直接通訊。Network的實現可以是 Linux Bridge、VLAN 等。

CNM示例:
這裡寫圖片描述

libnetwork CNM 定義了 docker 容器的網路模型,按照該模型開發出的 driver 就能與 docker daemon 協同工作,實現容器網路。docker 原生的 driver 包括 none、bridge、overlay 和 macvlan;第三方 driver 包括 flannel、weave、calico 等。

接下來直接使用docker-machine建立的實驗環境。在docker 主機 host1(192.168.20.201)和 host2(192.168.20.202)上實踐各種跨主機網路方案,在 192.168.20.203上部署支援的元件。

overlay

Docker 跨節點容器網路互聯,最通用的是使用 overlay 網路。
一代 Swarm 已經不再使用,它要求使用 overlay 網路前先準備好分散式鍵值庫,比如 etcd, consulzookeeper。然後在每個節點的 Docker 引擎中,配置 --cluster-store--cluster-advertise

引數。這樣才可以互連。

在測試一代swarm網路時,配置各節點的Docker引擎時,加入 --cluster-store--cluster-advertise 引數後,重啟docker服務啟動失敗。

現在都在使用二代 Swarm,也就是 Docker Swarm Mode,非常簡單,只要 docker swarm init 建立叢集,其它節點 docker swarm join 加入集群后,叢集內的服務就自動建立了 overlay 網路互聯能力。

需要注意的是,如果是多網絡卡環境,無論是 docker swarm init 還是 docker swarm join,都不要忘記使用引數 --advertise-addr 指定宣告地址,否則自動選擇的地址很可能不是你期望的,從而導致叢集互聯失敗。格式為 --advertise-addr <地址>:<埠>,地址可以是 IP 地址,也可以是網絡卡介面,比如 eth0。埠預設為 2377,如果不改動可以忽略。

此外,這是供服務使用的 overlay,因此所有 docker service create 的服務容器可以使用該網路,而 docker run 不可以使用該網路,除非明確該網路為 --attachable

關於 overlay 網路的進一步資訊,可以參考官網文件
參考連結

macvlan

macvlan 本身是 linxu kernel 模組,其功能是允許在同一個物理網絡卡上配置多個 MAC 地址,即多個 interface,每個 interface 可以配置自己的 IP。macvlan 本質上是一種網絡卡虛擬化技術
macvlan 的最大優點是效能極好,相比其他實現,macvlan 不需要建立 Linux bridge,而是直接通過以太 interface 連線到物理網路。

1.準備實驗環境:

使用 host1 和 host2 上單獨的網絡卡 eth1 建立 macvlan。為保證多個 MAC 地址的網路包都可以從 eth1 通過,我們需要開啟網絡卡的混雜模式(promisc)。
參考連結
設定支援promisc:

ip link set eth1 promisc on  |  
ifconfig eth1 promisc 

設定不支援promisc:

ifconfig eth0 -promisc

檢視:

# ip link show eth1
6: eth1: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:79:9a:8e brd ff:ff:ff:ff:ff:ff          

2.建立machine網路:

(1)在 host1 和 host2 中建立 macvlan 網路 mac_net1:

[[email protected] ~]# docker network create -d macvlan --subnet=192.168.100.0/24 --gateway=192.168.100.1 -o parent=eth1 mac_net1
690bfd5aee8cb279ca78617343d9227d40d5583703ade6398e96d99556dca78e

[[email protected] ~]# docker network create -d macvlan --subnet=192.168.100.0/24 --gateway=192.168.100.1 -o parent=eth1 mac_net1 
02eca5ba535994564dfeb746c5f2d03af7a2a0e3870398b897bc3770aaa725c5
  • -d macvlan 指定 driver 為 macvlan。
  • macvlan 網路是 local 網路,為了保證跨主機能夠通訊,使用者需要自己管理 IP subnet。
  • 與其他網路不同,docker 不會為 macvlan 建立閘道器,這裡的閘道器應該是真實存在的,否則容器無法路由。
  • -o parent 指定使用的網路 interface。

(2)在 host1 中執行容器 bbox1 並連線到 mac_net1。

[[email protected] ~]# docker run -itd --name bbox1 --ip=192.168.100.10 --network mac_net1 busybox
1c3fd10d23edc32d5b69c5ddd2877e624e312a4310b83e801a0a3f9746d5099b    

由於 host1 中的 mac_net1 與 host2 中的 mac_net1 本質上是獨立的,為了避免自動分配造成 IP 衝突,我們最好通過 --ip 指定 bbox1 地址為192.168.100.10。

WARNING: IPv4 forwarding is disabled. Networking will not work.
1.臨時開啟,(寫入記憶體,在記憶體中開啟)
echo "1" > /proc/sys/net/ipv4/ip_forward
2.永久開啟,(寫入核心)
在 vim /sysctl.conf 下
加入此行 net.ipv4.ip_forward = 1
sysctl -p —-載入一下
[[email protected] ~]# sysctl -a |grep "ip_forward" —-檢視一下

(3)在 host2 中執行容器 bbox2,指定 IP 192.168.100.11。

[[email protected] ~]# docker run -itd --name bbox2 --ip=192.168.100.11 --network mac_net1 busybox 
bbb62a6a58e454ea166b484e477f31be90d96dfa3cb883af853a93298bcef197

網路結構如下所示:
這裡寫圖片描述
(4)驗證 bbox1 和 bbox1 的連通性

[[email protected] ~]# docker exec bbox2 ping -c 2 192.168.100.10
PING 192.168.100.10 (192.168.100.10): 56 data bytes
64 bytes from 192.168.100.10: seq=0 ttl=64 time=2.457 ms
64 bytes from 192.168.100.10: seq=1 ttl=64 time=0.456 ms    

注意:bbox2 能夠 ping 到 bbox1 的 IP地址,但是無法解析”bbox1”的主機名。docker 沒有為 macvlan 提供 DNS 服務,這點與 overlay 網路是不同的

3.macvlan 網路結構分析:

這裡寫圖片描述
macvlan 不依賴 Linux bridge,除了 lo,容器只有一個 eth0,請注意 eth0 後面的 @if5,這表明該 interface 有一個對應的 interface,其全域性的編號為 5。通過ip link show eth1可以發現容器的eth0就是eth1通過 macvlan 虛擬出來的 interface。容器的 interface 直接與主機的網絡卡連線,這種方案使得容器無需通過 NAT 和埠對映就能與外網直接通訊(只要有閘道器),在網路上與其他獨立主機沒有區別。

4.用 sub-interface 實現多 macvlan 網路:

macvlan 會獨佔主機的網絡卡,也就是說一個網絡卡只能建立一個 macvlan 網路,否則會報錯:

Error response from daemon: network dm-690bfd5aee8c is already using parent interface eth1

但主機的網絡卡數量是有限的,如何支援更多的 macvlan 網路呢?
macvlan 不僅可以連線到 interface(如 eth1),也可以連線到 sub-interface(如 eth1.xxx)。

VLAN 是現代網路常用的網路虛擬化技術,它可以將物理的二層網路劃分成多達 4094 個邏輯網路,這些邏輯網路在二層上是隔離的,每個邏輯網路(即 VLAN)由 VLAN ID 區分,VLAN ID 的取值為 1-4094。

Linux 的網絡卡也能支援 VLAN(ubuntu:apt-get install vlan),同一個 interface 可以收發多個 VLAN 的資料包,不過前提是要建立 VLAN 的 sub-interface。

比如希望 eth1 同時支援 VLAN10 和 VLAN20,則需建立 sub-interface eth1.10 和 eth1.20。

在交換機上,如果某個 port 只能收發單個 VLAN 的資料,該 port 為 Access 模式,如果支援多 VLAN,則為 Trunk 模式,如果需要不同vlan下的容器互通,則eth1要接在交換機的 trunk 口上(VirtualBox 虛擬機器不需要額外的配置)

centos7下的用法:
參考連結

建立 VLAN 裝置:
    # ip link add link eth1 name eth1.10 type vlan id 10 
    # ip link add link eth1 name eth1.20 type vlan id 20 
    執行 ip link 命令確認 VLAN 已建立。

新增 IP:
    # ip addr add 192.168.100.1/24 brd 192.168.100.255 dev eth1.10

啟用裝置:
    # ip link set dev eth1.10 up

關閉裝置:
    # ip link set dev eth1.10 down

移除裝置:
    # ip link delete eth1.10

(1)首先配置host1和host2的sub-interface:

# ip link add link eth1 name eth1.10 type vlan id 10 
# ip link add link eth1 name eth1.20 type vlan id 20 
# ip link set dev eth1.10 up
# ip link set dev eth1.20 up

(2)在host1和host2主機上建立 macvlan 網路:

# docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=eth1.10 mac_net10
# docker network create -d macvlan --subnet=172.16.20.0/24 --gateway=172.16.20.1 -o parent=eth1.20 mac_net20

(3)在host1中執行容器:

# docker run -itd --name bbox1 --ip=172.16.10.10 --network mac_net10 busybox
# docker run -itd --name bbox2 --ip=172.16.20.10 --network mac_net20 busybox

(4)在host2中執行容器:

# docker run -itd --name bbox3 --ip=172.16.10.11 --network mac_net10 busybox
# docker run -itd --name bbox4 --ip=172.16.20.11 --network mac_net20 busybox

網路結構圖如下所示:
這裡寫圖片描述

(5)驗證 macvlan 之間的連通性:

注意:由於我的是在esxi的虛擬機器上面做的實驗,需要在esxi中設定eth1為trunk口,允許所有vlan通過後試驗才會生效
(沒有設定esxi,主機間互不連通,後續補充吧,歡迎大神留言指教)

flannel

flannel 是 CoreOS 開發的容器網路解決方案。flannel 為每個 host 分配一個 subnet,容器從此 subnet 中分配 IP,這些 IP 可以在 host 間路由,容器間無需 NAT 和 port mapping 就可以跨主機通訊。

每個 subnet 都是從一個更大的 IP 池中劃分的,flannel 會在每個主機上執行一個叫 flanneld 的 agent,其職責就是從池子中分配 subnet。為了在各個主機間共享資訊,flannel 用 etcd(與 consul 類似的 key-value 分散式資料庫)存放網路配置、已分配的 subnet、host 的 IP 等資訊。

資料包如何在主機間轉發是由 backend 實現的。flannel 提供了多種 backend,最常用的有 vxlan 和 host-gw。其他 backend 請參考 https://github.com/coreos/flannel
flannel支援多種的backend:目前已經支援UDP、VxLAN、AWS VPC和GCE路由等資料轉發方式,預設使用的是udp的方式

實驗環境:
繼續使用docker-machine的實驗環境,etcd部署在192.168.20.203上,host1和host2上執行flanneld。

安裝配置etcd:

GitHub地址
可以根據官網提供的二進位制方式包或通過docker的方式搭建,也可以直接yum安裝。

1.安裝etcd:

yum install -y etcd

2.修改配置檔案:/etc/etcd/etcd.conf

# grep ^[A-Z] /etc/etcd/etcd.conf  
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://192.168.20.203:2379"
ETCD_NAME="default"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.20.203:2379" 
  • --name :方便理解的節點名稱,預設為 default ,在叢集中應該保持唯一,可以使用 hostname
  • --data-dir :服務執行資料儲存的路徑,預設為 ${name}.etcd
  • --advertise-client-urls :對外公告的該節點客戶端監聽地址,這個值會告訴叢集中其他節點

詳細引數和用法可以參考:etcd使用簡介

3.編輯一個檔案:etcd.sh,設定host主機的subnet:

{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}
  • Network:定義host主機的IP地址池為10.2.0.0/16,注:由於etcd並不是動態儲存host上flannel網路的,比如:當有節點被刪除後,etcd中的關於這個節點的subnet網路並不會被刪除,所以使用10.X.X.X的網路,保證有足夠的網路可用
  • SubnetLen:指定每個主機分配的subnet大小為24位,即10.2.x.0/24
  • Backend為vxlan,即主機之間通過vxlan通訊,我們主要討論vxlan和host-gw這兩種方式

4.將配置儲存到etcd

etcdctl --endpoints=http://192.168.20.203:2379 set /usr/local/bin/network/config </root/etcd.sh
#--endpoints=http://192.168.7.222:2379 指定etcd的url
#/usr/local/bin/network/config  儲存key的地方,flanneld會讀取這個配置,保證自己能獲取到subnet,這個key可以任意指定,當host主機分配到subnet後,key會修改docker的啟動引數

檢視:

# etcdctl --endpoints=http://192.168.20.203:2379 get /usr/local/bin/network/config 
{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}

安裝配置fannel:

yum install -y flannel

2.修改配置檔案:/etc/sysconfig/flanneld

FLANNEL_ETCD_ENDPOINTS="http://192.168.20.203:2379"   #  連線etcd的地址
FLANNEL_ETCD_PREFIX="/usr/local/bin/network"      # key的地址

3.啟動flanneld:

systemctl start flanneld

host1和host2上都安裝啟動完flannel,就可以看見每個主機上起了一個flanneld1.1的網路
這裡寫圖片描述
兩個host都會出現一個flannel1.1的路由,路由都是10.2.0.0的

10.2.0.0        0.0.0.0         255.255.0.0     U     0      0        0 flannel.1

配置docker連線flannel:

編輯docker的配置檔案:/etc/systemd/system/docker.service.d/10-machine.conf 新增--bip= --mtu=

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock  --storage-driver devicemapper --tlsverify --tlscacert /etc/docker/ca.pem --tlscert /etc/docker/server.pe --tlskey /etc/docker/server-key.pem --label provider=generic --bip=10.2.54.1/24 --mtu=1450
Environment=        

這兩個引數要與/run/flannel/subnet.env中保持一致

# cat /run/flannel/subnet.env 
FLANNEL_NETWORK=10.2.0.0/16
FLANNEL_SUBNET=10.2.54.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false

重啟docker服務

systemctl daemon-reload
systemctl restart docker

檢視docker0的變化
這裡寫圖片描述
Docker 會將 10.2.54.1 配置到 Linux bridge docker0 上,並新增 10.2.54.0/24 的路由。
這裡寫圖片描述

在host2上,做相同的操作,修改配置檔案並重啟

驗證容器的連通性:

#host1
# docker run -dit --name b1 busybox

# docker exec b1 ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:02:36:02  
          inet addr:10.2.54.2  Bcast:10.2.54.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:648 (648.0 B)  TX bytes:0 (0.0 B)

#host2
# docker run -dit --name b2 busybox

# docker exec b2 ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:02:1D:02  
          inet addr:10.2.29.2  Bcast:10.2.29.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:648 (648.0 B)  TX bytes:0 (0.0 B)

測試b1到b2的連通性:

# docker exec b1 ping -c 2 10.2.29.2
PING 10.2.29.2 (10.2.29.2): 56 data bytes
64 bytes from 10.2.29.2: seq=0 ttl=62 time=3.008 ms
64 bytes from 10.2.29.2: seq=1 ttl=62 time=0.670 ms 

b1能夠 ping 到位於不同 subnet 的 b2,通過 traceroute 分析一下 b1 到 b2 的路徑。

# docker exec b1 traceroute 10.2.29.2           
traceroute to 10.2.29.2 (10.2.29.2), 30 hops max, 46 byte packets
 1  10.2.54.1 (10.2.54.1)  0.027 ms  0.024 ms  0.058 ms         
 2  10.2.29.0 (10.2.29.0)  0.410 ms  0.470 ms  0.275 ms         
 3  10.2.29.2 (10.2.29.2)  0.205 ms  0.720 ms  0.371 ms          
  1. b1與b2不是一個subnet,資料包傳送給預設閘道器10.2.54.1(docker0)
  2. 根據 host1 的路由表,資料包會發給 flannel.1。(10.2.0.0/16 dev flannel.1)
  3. flannel.1 將資料包封裝成 VxLAN,通過 eth0傳送給 host2。
  4. host2 收到包解封裝,發現數據包目的地址為 10.2.29.2,根據路由表將資料包傳送給 flannel.1,並通過 docker0
    到達 b2。(如下所示)
#host2路由表
10.2.0.0/16 dev flannel.1 
10.2.29.0/24 dev docker0 proto kernel scope link src 10.2.29.1 

flannel網路隔離:

flannel為每個主機分配了獨立的subnet,但是flannel1.1將這些subnet連結額起來,相互之間可以路由。本質上,flannel將個主機上相互獨立的docker0容器網路組成了一個互通的大網路,實現了容器跨主機通訊。flannel沒有實現隔離

flannel與外網連通性:
因為flannel網路是將flannel1.1橋接到docker0上,所以容器與外部通訊的方式與docker0預設的bridge網路一樣,即:

  • 容器通過NAT連線外網
  • 外網通過埠對映的方式訪問容器

使用 flannel host-gw

與vxlan不同,host-gw不會封裝資料包,而是在主機的路由表中建立到其他主機的subnet的路由條目,從而實現容器跨主機通訊

1.要使用host-gw首先修改etcd key:

前邊我們定義過vclan的方式:

{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}

現在修改backend的type為host-wg:

{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"host-gw"}}

更新 etcd 資料庫:

# etcdctl --endpoints=http://192.168.20.203:2379 set /usr/local/bin/network/config </root/etcd.sh 
{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"host-gw"}}

# etcdctl --endpoints=http://192.168.20.203:2379 get /usr/local/bin/network/config  
{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"host-gw"}}

2.重啟host1和host2上的flanneld服務

systemctl restart flanneld

與之前 vxlan backend 啟動時有幾點不同:
① flanneld 檢查到原先已分配的 subnet 10.2.54.0/24,重用之。
② flanneld 從 etcd 資料庫中檢索到 host2 的 subnet 10.2.29.0/24,但因為其 type=vxlan,立即忽略。
③ 稍後,再次發現 subnet 10.2.29.0/24,將其加到路由表中。這次沒有忽略 subnet 的原因是此時我們在 host2 上重啟了 flanneld,根據當前 etcd 的配置使用 host-gw backend。

檢視host1、host2的路由表可以發現,host1路由表增加了一條到10.2.29.0/24的路由,閘道器為host2的IP 192.168.20.202,類似的host2啟動 flanneld 時會重用 subnet 10.2.29.0/24,並將 host1 的 subnet 10.2.54.0/24 新增到路由表中,閘道器為 host1 IP 192.168.20.201。

#host1
10.2.29.0/24 via 192.168.20.202 dev eth0        
#host2
10.2.54.0/24 via 192.168.20.201 dev eth0

3.修改docker的配置
根據/run/flanneld/subnet.env,修改docker配置,重啟服務

FLANNEL_NETWORK=10.2.0.0/16
FLANNEL_SUBNET=10.2.29.1/24
FLANNEL_MTU=1500
FLANNEL_IPMASQ=false

subnet會重用,所以只需修改docker啟動引數–mtu=1450為–mtu=1500即可。

traceroute 檢視一下:

[[email protected] ~]# docker exec b1 traceroute 10.2.29.2
traceroute to 10.2.29.2 (10.2.29.2), 30 hops max, 46 byte packets
 1  10.2.54.1 (10.2.54.1)  0.027 ms  0.024 ms  0.058 ms
 2  192.168.20.202 (192.168.20.202)  0.831 ms  0.470 ms  0.275 ms
 3  10.2.29.2 (10.2.29.2)  0.205 ms  0.720 ms  0.371 ms

vxlan和 host-gw的區別

  • host-gw把每個主機都配置成閘道器,主機知道其他主機的subnet和轉發地址。vxlan則是在主機之間建立隧道,不同的主機的容器都在一個大的網段內,比如:10.2.0.0/16
  • 雖然vxlan與host-gw使用不同的機制,但對於容器之間還是可以相互通訊的
  • 由於vxlan需要對資料進行打包和拆包,效能可能稍遜於host-gw

參考連結

參考連結: