docker 實踐十一:docker 跨主機通信

在上一篇瞭解了關於 docker 的網絡模型後,本篇就基於上一篇的基礎來實現 docker 的跨主機通訊。docker

注:環境爲 CentOS7,docker 19.03。json

本篇會嘗試使用幾種不一樣的方式來實現跨主機方式 環境準備 準備兩臺或以上的主機或者虛擬機,相關環境以下:vim

  • 主機1:配置兩張網卡 br0 192.168.10.10,ens33橋接br0,ens37(不須要IP),docker環境
  • 主機2:配置兩張網卡 br0 192.168.10.11,ens33橋接br0,ens37(不須要IP),docker環境

橋接方式

網絡拓撲圖如圖 網絡

ens33 做爲外部網卡,使 docker 容器能夠和外部通訊,ens37 做爲內部網卡,和 docker0 橋接(因此不須要IP)讓不一樣的主機間的容器能夠相互通訊。配置以下:tcp

主機1上的配置

1.修改 docker 的啓動參數文件 /etc/docker/daemon.json 並重啓 docker daemonide

# vim /etc/docker/daemon.json 
{
  "bip": "172.17.0.1/16",
  "fixed-cidr": "172.17.18.1/24"
}
# systemctl restart docker

2.將 ehs33 網卡接入到 docker0 網橋中學習

# brctl addif docker0 ens37

3.添加容器con1spa

# docker run -it --rm --name con1 buysbox sh

主機2上的配置

1.修改 docker 的啓動參數文件 /etc/docker/daemon.json 並重啓 docker daemon3d

# vim /etc/docker/daemon.json 
{
  "bip": "172.17.0.2/16",
  "fixed-cidr": "172.17.19.1/24"
}
# systemctl restart docker

2.將 ehs33 網卡接入到 docker0 網橋中unix

# brctl addif docker0 ens37

3.添加容器con2

# docker run -it --rm --name con1 busybox sh

這時容器 con1 和 con2 既能彼此通訊,也能夠訪問外部IP。 容器con1向容器con2發送數據的過程是這樣的:首先,經過查看自己的路由表發現目的地址和本身處於同一網段,那麼就不須要將數據發往網關,能夠直接發給con2,con1經過ARP廣播獲取到con2的MAC地址;而後,構造以太網幀發往con2便可。此過程數據中docker0網橋充當普通的交換機轉發數據幀。

直接路由

直接路由方式是經過在主機中添加靜態路由實現。例若有兩臺主機host1和host2,兩主機上的Docker容器是獨立的二層網絡,將con1發往con2的數據流先轉發到主機host2上,再由host2轉發到其上的Docker容器中,反之亦然。

注:這個實現失敗!!!

直接路由的網絡拓撲圖以下:

host1上

1.配置 docker0 ip

# vim /etc/docker/daemon.json 
{
  "registry-mirrors": ["https://dhq9bx4f.mirror.aliyuncs.com"],
  "bip": "172.17.1.254/24"
}
# systemctl restart docker

2.添加路由,將目的地址爲172.17.2.0/24的包轉發到host2

# route add -net 172.17.2.0 netmask 255.255.255.0 gw 192.168.10.11

3.配置iptables規則

# iptables -t nat -F POSTROUTING
# iptables -t nat -A POSTROUTING s 172.17.1.0/24 ! -d 172.17.0.0/16 -j MASQUERADE

4.打開端口轉發

# echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

5.啓動容器con1

# docker run -it --name --rm --name con1 busybox sh

host2上

# vim /etc/docker/daemon.json 
{
  "registry-mirrors": ["https://dhq9bx4f.mirror.aliyuncs.com"],
  "bip": "172.17.2.254/24"
}
# systemctl restart docker

2.添加路由,將目的地址爲172.17.1.0/24的包轉發到host1

# route add -net 172.17.1.0 netmask 255.255.255.0 gw 192.168.10.10

3.配置iptables規則

# iptables -t nat -F POSTROUTING
# iptables -t nat -A POSTROUTING s 172.17.2.0/24 ! -d 172.17.0.0/16 -j MASQUERADE

4.打開端口轉發

# echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

5.啓動容器con2

# docker run -it --name --rm --name con2 busybox sh

以上介紹的兩種跨主機通訊方式簡單有效,但都要求主機都在同一個局域網中。

OVS 劃分 VLAN

VLAN(Virtual Local Area Network)即虛擬局域網,按照功能、部門等因素將網絡中的機器進行劃分,使之屬於不一樣的部分,每個部分造成一個虛擬的局域網絡,共享一個單獨的廣播域,從而避免因一個網絡內的主機過多而產生的廣播風暴問題。

VLAN如何在一個二層網絡中區分不一樣流量呢?IEEE 的802.1q協議規定了VLAN的實現方法,即在傳統的以太網幀中在添加一個VLAN tag字段,用於標識不一樣的VLAN。這樣,支持VLAN的交換機在轉發幀時,不只會關注MAC地址,還會考慮到VLAN tag字段。VLAN tag中包含TPID、PCP、CFI、VID,其中VID(VLAN ID)部分用來具體指出幀屬於哪一個VLAN。VID佔12爲,因此取值範圍爲0到4095。

交換機有兩種類型的端口access端口和trunk端口。圖中,Port一、Port二、Port五、Port六、Port8爲access端口,每一個access端口都會分配一個VLAN ID,標識它所鏈接的設備屬於哪個VLAN。當數據幀從外界經過access端口進入交換機時,數據幀本來是不帶tag的,access端口給數據幀打上tag(VLAN ID即爲access端口所分配的VLAN ID);當數據幀從交換機內部經過access端口發送時,數據幀的VLAN ID必須和access端口的VLAN ID一致,access端口才接收此幀,接着access端口將幀的tag信息去掉,再發送出去。Port3,Port4爲trunk端口,trunk端口不屬於某個特定的VLAN,而是交換機和交換機之間多個VLAN的通道。trunk端口聲明瞭一組VLAN ID,代表只容許帶有這些VLAN ID的數據幀經過,從trunk端口進入和出去的數據幀都是帶tag的(不考慮默認VLAN的狀況)。PC1和PC3屬於VLAN100,PC2和PC4屬於VLAN200,因此PC1和PC3處於同一個二層網絡中,PC2和PC4處於同一個二層網絡中。儘管PC1和PC2鏈接在同一臺交換機中,但它們之間的通訊時須要通過路由器的。

VLAN tag又是如何發揮做用的呢?當PC1向PC3發送數據時,PC1將IP包封裝在以太幀中,幀在目的MAC地址爲PC3的地址,此時幀並無tag信息。當幀到達Port1是,Port1給幀打上tag(VID=100),幀進入switch1,而後幀經過Port3,Port4到達Switch2(Port三、Port4容許VLAN ID爲100、200的幀經過)。在switch2中,Port5所標記的VID幀相同,MAC地址也匹配,幀就發送到Port5中,Port5將幀的tag信息去掉,而後發給PC3。因爲PC二、PC4於PC1的VLAN不一樣,所以收不到PC1發出的幀。

單主機docker容器的VLAN劃分

docker默認網絡模式下,全部容器都連在docker0網橋上。docker0網橋是普通的Linux網橋,不支持VLAN功能,這裏就使用Open vSwitch代替。

接下來咱們就來嘗試在主機1上實現圖中的效果:

1.在主機上建立4個 docker 容器 con一、con二、con三、con4。

# docker run -itd --name con1 busybox sh
# docker run -itd --name con2 busybox sh
# docker run -itd --name con3 busybox sh
# docker run -itd --name con4 busybox sh

2.下載open vswitch,並啓動

# yum install -y openvswitch
# systemctl start openvswitch

3.使用pipework將con一、con2劃分到一個VLAN中

# pipework ovs0 con1 192.168.0.1/24 @100
# pipework ovs0 con2 192.168.0.2/24 @100

4.使用pipework將con三、con4劃分到一個VLAN中

# pipework ovs0 con3 192.168.0.3/24 @200
# pipework ovs0 con4 192.168.0.4/24 @200

這樣一來con1和con2,con3和con4就也是互相通訊,但con1和con3是互相隔離的。 使用Open vSwitch配置VLAN,建立access端口和trunk端口的命令以下:

# ovs-vsctl add port ovs0 port1 tag=100
# ovs-vsctl add port ovs0 port2 trunk=100,200

多主機docker容器的VLAN劃分

# host1上
# docker run -itd --net=none --name con1 busybox sh
# docker run -itd --net=none --name con2 busybox sh
# pipework ovs0 con1 192.168.0.1/24 @100
# pipework ovs0 con2 192.168.0.2/24 @200
# ovs-vsctl add-port ovs0 eth1 

# host2上
# docker run -itd --net=none --name con3 busybox sh
# docker run -itd --net=none --name con4 busybox sh
# pipework ovs0 con3 192.168.0.3/24 @100
# pipework ovs0 con4 192.168.0.4/24 @200
# ovs-vsctl add-port ovs0 eth1

OVS隧道模式

使用VLAN來隔離docker容器網絡有多方面的限制:

  • VLAN是在二層幀上,要求主機在同個網絡中
  • VLAN ID只有12比特單位,可用的數量只有4000多個
  • VLAN配置較繁瑣。

所以如今比較廣泛的解決方式爲Overlay虛擬化網絡技術。

Overlay技術模型

Overlay網絡也就是隧道技術,它將一種網絡協議包裝在另外一種協議中傳輸。隧道被普遍應用於鏈接因使用不一樣網絡而被隔離的主機和網絡,使用隧道技術搭建的網絡就是所謂的Overlay網絡,它能有效地覆蓋在基礎網絡上。在傳輸過程當中,將以太幀封裝在IP包中,經過中間地因特網,最後傳輸到目的網絡中再解封裝,這樣來保證二層幀頭再傳輸過程當中不改變。在多租戶狀況下,就採用不一樣租戶不一樣隧道地方式進行隔離。

目前Overlay技術有VXLAN(Virtual Extensible LAN)和NVGRE(Network Virtualization using Generic Routing Encapsulation)。VXLAN是將以太網報文封裝在UDP傳輸層上地一種隧道轉發模式,它採用24位比特標識二層網絡分段,稱爲VNI(VXLAN Network Idenfier),相似於VLAN ID地做用。NVGRE同VXLAN相似,它使用GRE方法來打通二層與三層之間的通路,採用24位比特的GRE key來做爲網絡標識(TNI)。

GRE簡介

NVGRE使用GRE協議來封裝須要傳送的數據。GRE協議能夠用來封裝其餘網絡層的協議。

如圖的網絡如何通訊呢?經過在雙方路由器上配置GRE隧道實現。從IP地址爲192.168.1.1/24的主機A ping IP地址爲192.168.2.1/24的主機B中,主機A構造好IP包後,經過查看路由表發現目的地址和自己不在同一個子網中,要將其轉發到默認網關192.168.1.254上。主機A將IP包封裝在以太網幀中,源MAC地址爲自己網卡的MAC地址,目的MAC地址爲網關的MAC地址。

網關路由器收到數據幀後,去掉幀頭,將IP取出,匹配目的IP地址和自身的路由表,肯定包須要從GRE隧道設備發出,這就對這個IP包作GRE封裝,即加上GRE協議頭部。封裝完成後,該包是不能直接發往互聯網的,須要生成新的IP包做爲載體來運輸GRE數據包,新IP包的源地址爲1.1.1.1,目的地址爲2.2.2.2。並裝在新的廣域網二層幀中發出去。在傳輸過程當中,中間的節點僅能看到最外層的IP包。當IP包到達2.2.2.2的路由器後,路由器將外層IP頭部和GRE頭部去掉,獲得原始的IP數據包,再將其發往192.168.2.1。對於原始IP包來講,兩個路由器之間的傳輸過程就如同單鏈路上的同樣。

GRE實現 docker 容器跨網絡通訊(容器在同一子網中)

host1上

1.修改docker配置文件

# vim /etc/docker/daemon.json
{
  "registry-mirrors": ["https://dhq9bx4f.mirror.aliyuncs.com"],
  "bip": "172.17.42.1/16",
  "fixed-cidr": "172.17.1.1/24"
}
# systemctl restart docker

2.建立ovs0網橋,並將ovs0連在docker0上

# ovs-vsctl add-br ovs0
# brctl addif docker0 ovs0

3.在ovs0上建立GRE隧道

# ovs-vsctl add-port ovs0 gre0 -- set interface gre0 type=gre options:remote_ip=10.10.105.235

host2上

1.修改docker配置文件

# vim /etc/docker/daemon.json
{
  "registry-mirrors": ["https://dhq9bx4f.mirror.aliyuncs.com"],
  "bip": "172.17.42.1/16",
  "fixed-cidr": "172.17.1.1/24"
}
# systemctl restart docker

2.建立ovs0網橋,並將ovs0連在docker0上

# ovs-vsctl add-br ovs0
# brctl addif docker0 ovs0

3.在ovs0上建立GRE隧道

# ovs-vsctl add-port ovs0 gre0 -- set interface gre0 type=gre options:remote_ip=10.10.105.91

驗證

建立容器

# host1上
# docker run -it --rm --name con1 busybox sh
# ping 172.17.2.0

# host2上
# docker run -it --rm --name con2 busybox sh
# ping 172.17.1.0

con1向con2發送數據時,會發送ARP請求獲取con2的MAC地址。APR請求會被docker0網橋洪泛到全部端口,包括和ovs0網橋相連的ovs0端口。ARP請求達到ovs0網橋後,繼續洪泛,經過gre0隧道端口到達host2上的ovs0中,最後到達con2。host1和host2處在不一樣網絡中,該ARP請求如何跨越中間網絡到達host2呢?ARP請求通過gre0時,會首先加上一個GRE協議的頭部,而後再加上一個源地址爲10.10.105.9一、目的地址爲10.10.105.235的IP協議頭部,再發送給host2。這裏GRE協議封裝的是二層以太網幀,而非三層IP數據包。con1獲取到con2的MAC地址以後,就能夠向它發送數據,發送數據包的流程和發送ARP請求的流程相似。只不過docekr0和ovs0會學習到con2的MAC地址該從哪一個端口發送出去,而無需洪泛到全部端口。

GRE實現docker容器跨網絡通訊(容器在不一樣子網中)

host1上

1.修改docker配置文件

# vim /etc/docker/daemon.json
{
  "registry-mirrors": ["https://dhq9bx4f.mirror.aliyuncs.com"],
  "bip": "172.17.1.254/16",
  "fixed-cidr": "172.17.1.1/24"
}
# systemctl restart docker

2.建立ovs0網橋,並將ovs0連在docker0上

# ovs-vsctl add-br ovs0
# brctl addif docker0 ovs0

3.在ovs0上建立一個internal類型的端口rou0,並分配一個不引發衝突的私有IP

# ovs-vsctl add-port ovs0 rou0 -- set interface rou0 type=internal
# ifconfig rou0 192.168.1.1/24
# 將通往docker容器的流量路由到rou0
# route add -net 172.17.0.0/16 dev rou0

4.在ovs0上建立GRE隧道

# ovs-vsctl add-port ovs0 gre0 -- set interface gre0 type=gre options:remote_ip=10.10.105.235

5.刪除並建立iptables規則

# iptables -t nat -D POSTROUTING -s 172.17.0.0/24 ! -o ens33 -j MASQUERADE          
# iptables -t nat -A POSTROUTING -s 172.17.0.0/24 ! -o ens33 -j MASQUERADE

host2上

1.修改docker配置文件

# vim /etc/docker/daemon.json
{
  "registry-mirrors": ["https://dhq9bx4f.mirror.aliyuncs.com"],
  "bip": "172.17.2.254/16",
  "fixed-cidr": "172.17.2.1/24"
}
# systemctl restart docker

2.建立ovs0網橋,並將ovs0連在docker0上

# ovs-vsctl add-br ovs0
# brctl addif docker0 ovs0

3.在ovs0上建立一個internal類型的端口rou0,並分配一個不引發衝突的私有IP

# ovs-vsctl add-port ovs0 rou0 -- set interface rou0 type=internal
# ifconfig rou0 192.168.1.1/24
# 將通往docker容器的流量路由到rou0
# route add -net 172.17.0.0/16 dev rou0

4.在ovs0上建立GRE隧道

# ovs-vsctl add-port ovs0 gre0 -- set interface gre0 type=gre options:remote_ip=10.10.103.91

5.刪除並建立iptables規則

# iptables -t nat -D POSTROUTING -s 172.17.0.0/24 ! -o eth0 -j MASQUERADE          
# iptables -t nat -A POSTROUTING -s 172.17.0.0/24 ! -o eth0 -j MASQUERADE

overlay配合consul實現跨主機通訊

環境準備 準備兩臺或以上的主機或者虛擬機,相關環境以下:

  • 主機1:配置兩張網卡 ens33 192.168.10.10,docker環境
  • 主機2:配置兩張網卡 ens33 192.168.10.11,docker環境
  • 主機3:consul 環境 192.168.10.12:8500

單通道實現容器互通

在兩主機上配置docker啓動的配置文件

# vim /etc/docker/daemon.json 
{
  "registry-mirrors": ["https://dhq9bx4f.mirror.aliyuncs.com"],
  "cluster-store": "consul://192.168.10.12:8500",
  "cluster-advertise": "ens33:2376"
}
# systemctl restart docker

一樣生效的是啓動腳本:/etc/systemd/system/docker.service.d/10-machine.conf

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --storage-driver overlay2 --tlsverify --tlscacert /etc/docker/ca.pem --tlscert /etc/docker/server.pem --tlskey /etc/docker/server-key.pem --label provider=generic  --cluster-store=consul://192.168.10.12:8500 --cluster-advertise=ens33:2376
Environment=

這個時候,在192.168.10.12:8500的consul上就存儲了docker的註冊信息了

在主機1上建立網絡

# docker network create -d overlay ov-net1
# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
ce13051e6bff        bridge              bridge              local
ff8cf4a7552a        host                host                local
8514568883d2        none                null                local
adef0a0c4d39        ov-net1             overlay             global

這是咱們在主機2上也能看到新建的網絡ov-net1,由於ov-net1網絡已經存儲到consul上,兩個主機均可以看到該網絡

兩個主機上容器

# host1上
# docker run -itd --name con1 --network ov-net1 busybox

# host2上
# docker run -itd --name con2 --network ov-net1 busybox

容器上分別生成兩張網卡eth0 10.0.0.2/24,eth1 172.18.0.1/24,eth0鏈接外部網絡,走overlay模式;eth1鏈接內部網絡,鏈接docker-gwbridge。

overlay網絡隔離

那overlay這麼實現網絡的隔離呢?其實也很簡單,就是利用多個overlay網絡實現,不一樣overlay網絡之間就存在隔離

# docker network create -d overlay --subnet 10.22.1.0/24 ov-net2

--subnet 參數用來限制分配子網的範圍。

# docker run -itd --name con3 --network ov-net2 busybox 
# docker exec -it con3 ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes

建立容器con3並綁定網絡ov-net2,con3不能ping通con1。

相關文章
相關標籤/搜索