Docker實戰(三)Docker 跨主機網路overlay、macvlan和flannel
跨主機網路概述
docker跨主機網路方案包括:
- docker 原生的
overlay
和macvlan
。 - 第三方方案:常用的包括
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
, consul
或 zookeeper
。然後在每個節點的 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
- b1與b2不是一個subnet,資料包傳送給預設閘道器10.2.54.1(docker0)
- 根據 host1 的路由表,資料包會發給 flannel.1。(10.2.0.0/16 dev flannel.1)
- flannel.1 將資料包封裝成 VxLAN,通過 eth0傳送給 host2。
- 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