之前有另一篇文章介紹過盖腕,容器可以通過網(wǎng)絡(luò)命名空間來和主機(jī)之間進(jìn)行網(wǎng)絡(luò)的隔離疆前,這篇文章會(huì)繼續(xù)介紹一下容器的幾種組網(wǎng)模式。這個(gè)對(duì)于我們?cè)谑褂萌萜鬟M(jìn)行服務(wù)部署的時(shí)候來理解不同服務(wù)之間的通信尤為重要。
首先對(duì)于host系統(tǒng)上的物理網(wǎng)絡(luò)設(shè)備戴而,一個(gè)物理的網(wǎng)絡(luò)設(shè)備最多存在于一個(gè)Network namespace中砾肺,所以通常我們不會(huì)給容器分配一個(gè)物理的網(wǎng)絡(luò)設(shè)備挽霉,而是用虛擬網(wǎng)卡來替代,比如說使用veth pair進(jìn)行不同的Network namespace之間的通信变汪,
- 可以是veth pair的直連
- 也可以是借助bridge進(jìn)行連接通信
當(dāng)然容器也可以和主機(jī)不進(jìn)行網(wǎng)絡(luò)命名空間的隔離侠坎,使用host模式和主機(jī)共享網(wǎng)絡(luò)設(shè)備和配置。
1. 橋接模式(Bridge)
Docker創(chuàng)建新的容器的時(shí)候默認(rèn)的網(wǎng)絡(luò)模式是橋接裙盾,如圖所示
- docker0是虛擬網(wǎng)橋
- 一端通過veth pair和container相連实胸;
- 另一端通過SNAT/DNAT連接外部網(wǎng)絡(luò)
1.1. veth pair
另一篇文章中有介紹,通過如下命令可以手動(dòng)添加一個(gè)veth對(duì)到linux系統(tǒng)中番官,
$ ip link add veth0 type veth peer name veth01
如果像docker這樣庐完,已經(jīng)默認(rèn)構(gòu)建好了內(nèi)部網(wǎng)絡(luò),在host系統(tǒng)中我們只可以查看到veth01這個(gè)虛擬網(wǎng)口徘熔,
$ ip -d link show
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
....
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:4a:90:6c:62 brd ff:ff:ff:ff:ff:ff
...
7: vethf0f03d4@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
link/ether ae:58:d8:8e:46:72 brd ff:ff:ff:ff:ff:ff link-netnsid 0
依據(jù)打印出來的信息可以看出來门躯,
- index 5的網(wǎng)口就是docker默認(rèn)的虛擬網(wǎng)關(guān)
- index 7對(duì)應(yīng)的網(wǎng)口vethf0f03d4就是和container相連的虛擬網(wǎng)卡,后面還跟了
@if6
代表它和index 6的網(wǎng)卡互為veth pair酷师;
如果這個(gè)時(shí)候打開docker容器查看讶凉,可以看到容器中的網(wǎng)卡的index就是6。
除此之外還可以通過如下命令來查看Linux系統(tǒng)中的veth pair
# ethtool -S vethf0f03d4
NIC statistics:
peer_ifindex: 6
1.2. docker0
可以使用如下命令來查看現(xiàn)在系統(tǒng)中創(chuàng)建的bridge
# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02424a906c62 no veth8ef89b4
vetha95b32d
當(dāng)前這個(gè)例子中創(chuàng)建了兩個(gè)容器窒升,使用的都是默認(rèn)網(wǎng)絡(luò)缀遍;對(duì)應(yīng)的host系統(tǒng)上也會(huì)有兩個(gè)虛擬網(wǎng)卡veth8ef89b4和vetha95b32d
1.3. 路由表
- 首先查看一下容器中的ip路由表
# cat /proc/net/route
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth0 00000000 010011AC 0003 0 0 0 00000000 0 0 0
eth0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
- 第一條是默認(rèn)路由,會(huì)通過eth0這個(gè)網(wǎng)口發(fā)往默認(rèn)的網(wǎng)關(guān)饱须,這里的網(wǎng)關(guān)是010011AC(172.17.0.1域醇,大端終結(jié))
- 第二條是所有發(fā)往172.17.0.0(掩碼是255.255.0.0)網(wǎng)段的數(shù)據(jù)包都由本機(jī)處理(網(wǎng)關(guān)是0.0.0.0);
- 然后我們看一下host的路由表
$ ip route show
default via 10.0.2.2 dev enp0s3 proto static metric 100
...
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
- 默認(rèn)路由是所有找不到路由信息的數(shù)據(jù)蓉媳,都通過ip地址10.0.2.2以及網(wǎng)絡(luò)接口enp0s3發(fā)送出去譬挚;
- docker0這條路由的意思是,所有docker0接口上收到的發(fā)往172.17.0.0/16網(wǎng)段的數(shù)據(jù)包都由本機(jī)處理酪呻;
1.4. iptables
1.4.1. 首先在host上查看filter表的鏈
iptables -L -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:53
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:67
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER-ISOLATION all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain DOCKER (1 references)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:443
ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:80
ACCEPT tcp -- 0.0.0.0/0 172.17.0.4 tcp dpt:80
Chain DOCKER-ISOLATION (1 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
這里我們可以看到有兩個(gè)和docker有關(guān)的鏈
- Docker-isolation chain:包含限制不同容器網(wǎng)絡(luò)之間的訪問規(guī)則;
- Docker chain:包含容器的訪問規(guī)則减宣,例如這里就允許HTTP(port:80)和HTTPs(port: 443)兩種訪問;
Chain FORWAR中配置了轉(zhuǎn)發(fā)策略,所有的數(shù)據(jù)包都會(huì)轉(zhuǎn)發(fā)到DOCKER這個(gè)chain上玩荠;然后DOCKER這個(gè)CHAIN上會(huì)設(shè)置過濾規(guī)則漆腌。
1.4.2 然后查看nat表鏈
# iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE all -- 10.0.3.0/24 !10.0.3.0/24
MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:443
MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:80
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 to:172.17.0.2:443
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.4:80
NAT允許主機(jī)修改數(shù)據(jù)包的IP地址或者端口贼邓,
- SNAT
也就是這里Chain POSTROUTING中設(shè)置的MASQUERADE規(guī)則,看第一條闷尿,所有的源地址是172.17.0.0/16網(wǎng)段的數(shù)據(jù)包的源IP都會(huì)修改成這個(gè)數(shù)據(jù)要發(fā)送的網(wǎng)絡(luò)接口上的IP地址塑径;比如說之前route中發(fā)現(xiàn)這個(gè)數(shù)據(jù)包適用默認(rèn)路由,要經(jīng)過enp0s3 (IP:10.0.2.2)發(fā)送到默認(rèn)網(wǎng)關(guān)填具,這個(gè)時(shí)候源IP地址就會(huì)修改成10.0.2.2统舀;
Note: SNAT和MASQUERAGE的區(qū)別,SNAT需要指明目的地址要修改成具體的哪個(gè)或者哪幾個(gè)IP地址劳景;MASQUERAGE只要指定輸出的網(wǎng)絡(luò)口是哪個(gè)誉简,系統(tǒng)會(huì)自動(dòng)獲取這個(gè)網(wǎng)絡(luò)口的IP地址并進(jìn)行偽裝。后者對(duì)于動(dòng)態(tài)分配IP的場景很有效盟广。
- DNAT
Chain DOCKER設(shè)置了DNAT規(guī)則闷串。例如第一條表示,所有的目的端口為443的數(shù)據(jù)包的目的地址都會(huì)修改成172.17.0.2:443衡蚂,然后數(shù)據(jù)包才會(huì)去匹配路由規(guī)則窿克,看下一跳要發(fā)送到哪里。
NOTE:所以這里可以看出來毛甲,如果你通過端口映射8080:80把主機(jī)的端口8080暴露給外網(wǎng)了年叮,也就相當(dāng)于主機(jī)上所有網(wǎng)絡(luò)接口的8080端口都暴露給外網(wǎng)訪問了,這個(gè)會(huì)破壞原有的iptables防火墻規(guī)則玻募,可能會(huì)帶來危險(xiǎn)只损。
1.5. 連起來就是如下這么一個(gè)數(shù)據(jù)收發(fā)流程
2. 主機(jī)模式(Host)
使用主機(jī)的network namespace以及網(wǎng)絡(luò)配置。
缺點(diǎn):容器可以修改主機(jī)的網(wǎng)絡(luò)配置七咧,沒有做網(wǎng)絡(luò)的隔離跃惫。
還是來看個(gè)實(shí)際的例子,啟動(dòng)一個(gè)nginx的容器艾栋,
docker run --rm -dit --network host --name my_nginx nginx
- 查看一下當(dāng)前host上的網(wǎng)絡(luò)接口
ip addr show
可以看到這個(gè)時(shí)候沒有添加新的veth口爆存,這個(gè)和bridge的模式不一樣。
- 再來看看端口80綁定到哪個(gè)進(jìn)程了
$ sudo netstat -tulpn | grep :80
這里必須要使用sudo蝗砾,因?yàn)閚ginx進(jìn)程是屬于Docker daemon用戶的先较,否則沒法兒獲取進(jìn)程名和PID。
可以看到這個(gè)例子里會(huì)輸出如下信息悼粮,80端口直接綁定到nginx這個(gè)進(jìn)程上了闲勺,
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 9815/nginx: master
如果配置成了橋接模式,會(huì)打印這樣的信息扣猫,80端口沒有直接綁定到nginx菜循,而是綁定到了docker-proxy。
tcp6 0 0 :::80 :::* LISTEN 2972/docker-proxy
3. Overlay網(wǎng)絡(luò)模式
overlay這種網(wǎng)絡(luò)模式下申尤,會(huì)在多個(gè)Docker Daemon主機(jī)的基礎(chǔ)上創(chuàng)建一個(gè)分布式網(wǎng)絡(luò)癌幕,這個(gè)分布式網(wǎng)絡(luò)允許容器連接上去并且互相通信衙耕。這種部署方式相對(duì)復(fù)雜,后面單獨(dú)找個(gè)時(shí)間去研究一下勺远。
4. Macvlan網(wǎng)絡(luò)模式
一些應(yīng)用程序臭杰,尤其是一些遺留的程序或者是那些監(jiān)控網(wǎng)絡(luò)流量的程序,需要直接連接到物理網(wǎng)絡(luò)上谚中。這種情況下,你可以使用macvlan網(wǎng)絡(luò)驅(qū)動(dòng)來給每個(gè)容器的虛擬網(wǎng)絡(luò)接口都分配一個(gè)MAC地址寥枝,讓這些網(wǎng)絡(luò)接口看上去像是一個(gè)物理網(wǎng)卡宪塔,并且能直接連到物理網(wǎng)絡(luò)上。這種情況下你需要指定host上的一個(gè)物理網(wǎng)卡用于Macvlan囊拜,以及子網(wǎng)某筐,和Macvlan的網(wǎng)關(guān)。你甚至可以通過使用不同的物理網(wǎng)卡來隔離Macvlan冠跷。注意以下幾點(diǎn)南誊,
- 由于IP地址的耗盡或者由于VLAN spread而很容易導(dǎo)致對(duì)網(wǎng)絡(luò)的損害,通常出現(xiàn)在你的網(wǎng)絡(luò)中有著大量的獨(dú)立的MAC地址的情況下
- 你的網(wǎng)絡(luò)設(shè)備需要可以處理混雜模式蜜托,這種情況下一個(gè)物理接口可以分配多個(gè)MAC地址
- 應(yīng)用程序依然可以使用橋接(單一的Docker主機(jī))或者overlay模式(跨多個(gè)docker主機(jī)間通信)
4.1. 創(chuàng)建一個(gè)macvlan網(wǎng)絡(luò)
當(dāng)你創(chuàng)建一個(gè)macvlan網(wǎng)絡(luò)的時(shí)候抄囚,可以有兩種模式
- 橋接模式,這種模式下Macvlan的數(shù)據(jù)通過主機(jī)上的物理設(shè)備傳遞橄务;
- 802.1q分支橋接模式幔托,數(shù)據(jù)通過一個(gè)802.1q 子接口來傳輸,這種模式允許用戶控制路由和過濾規(guī)則
4.1.1 橋接模式
創(chuàng)建一個(gè)Macvlan網(wǎng)絡(luò)
$ docker network create -d macvlan \
--subnet=172.16.86.0/24 \
--gateway=172.16.86.1 \
-o parent=eth0 pub_net
這里指定了子網(wǎng)和網(wǎng)關(guān)蜂挪,parent這個(gè)選項(xiàng)指定所有的數(shù)據(jù)會(huì)通過哪個(gè)主機(jī)上的物理網(wǎng)卡來傳輸重挑。
4.1.2 802.1q分支橋接模式
parent選項(xiàng)在指定網(wǎng)口的時(shí)候,如果后面加了小數(shù)點(diǎn)棠涮,就會(huì)默認(rèn)為這是一個(gè)eth0的子網(wǎng)口谬哀,并且會(huì)自動(dòng)創(chuàng)建這個(gè)子網(wǎng)口。
$ docker network create -d macvlan \
--subnet=192.168.50.0/24 \
--gateway=192.168.50.1 \
-o parent=eth0.50 macvlan50
4.1.3 使用ipvlan來替代macvlan
上述的例子里面依舊是個(gè)L3橋接(層三交換严肪?)史煎,我們可以使用ipvlan來進(jìn)行替換,并且指定ipvlan的模式是l2
$ docker network create -d ipvlan \
--subnet=192.168.210.0/24 \
--subnet=192.168.212.0/24 \
--gateway=192.168.210.254 \
--gateway=192.168.212.254 \
-o ipvlan_mode=l2 ipvlan210
4.2. 實(shí)例
4.2.1. 橋接的例子
這個(gè)例子里面诬垂,容器所有的流量都經(jīng)過eth0發(fā)出去劲室,Docker利用容器的MAC地址來路由消息到容器;從網(wǎng)絡(luò)上來看结窘,docker容器的網(wǎng)絡(luò)接口就像是直接連在網(wǎng)絡(luò)上的物理網(wǎng)卡
- 這個(gè)例子里面有兩個(gè)虛擬主機(jī)很洋,主機(jī)用于實(shí)驗(yàn)的網(wǎng)卡都必須開啟混雜模式,否則一臺(tái)主機(jī)上的容器無法訪問另一臺(tái)主機(jī)的容器
$ ip link set enp0s8 promisc on
這里enp0s8是主機(jī)上的網(wǎng)卡隧枫,IP地址是192.168.56.101喉磁,網(wǎng)關(guān)是192.168.56.1谓苟;
- 創(chuàng)建一個(gè)名為my-macvlan-net的網(wǎng)絡(luò)
$ docker network create -d macvlan \
--subnet=192.168.56.0/24 \
--gateway=192.168.56.1 \
-o parent=enp0s8 \
my-macvlan-net
- 啟動(dòng)一個(gè)容器加入到這個(gè)網(wǎng)絡(luò)中
$ docker run --rm -itd \
--network my-macvlan-net \
--ip 192.168.56.220 \
--name c1 \
alpine:latest \
ash
- 這個(gè)時(shí)候可以查看這個(gè)container的MAC地址了
$ docker container inspect my-macvlan-alpine
...
"Networks": {
"my-macvlan-net": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"6dd64f910242"
],
"NetworkID": "59d92c55e8b4f56535f8097881e9e35d5a2f363f9c53703ab228c64007afa256",
"EndpointID": "5bb8843f2990d7c0b80b068f8413a8f4170c90e86436a82b98eefa62e3ad1eb5",
"Gateway": "192.168.56.1",
"IPAddress": "192.168.56.220",
"IPPrefixLen": 24,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:10:56:02"
}
- 查看一下container中的網(wǎng)絡(luò)端口配置,
$ docker exec my-macvlan-alpine ip addr show
9: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:10:56:02 brd ff:ff:ff:ff:ff:ff
inet 192.168.56.220/24 scope global eth0
valid_lft forever preferred_lft forever
可以看到eth0配置了ip為192.168.56.220/24协怒,同時(shí)eth0和if3互為veth對(duì)涝焙,回到host上查看,if3就是host上的物理網(wǎng)卡孕暇;
- 重復(fù)上述的步驟仑撞,在一個(gè)虛擬機(jī)中創(chuàng)建兩個(gè)容器c1/c2,另一個(gè)虛擬機(jī)中創(chuàng)建容器c3/c4
拓?fù)浣Y(jié)構(gòu)如下
這個(gè)時(shí)候可以嘗試一下ping不同的容器
- c1 ping c2(同一主機(jī)內(nèi)的不同容器間) -> ok;
- c1 ping c3(不同主機(jī)的容器間) ->ok;
- master ping c1(主機(jī)到本機(jī)的容器) ->nok;
- master ping c3(主機(jī)到另一主機(jī)的容器) -> ok
Note:
這里的交換都是二層基于MAC地址的交換妖滔。
每個(gè)容器內(nèi)的eth0都有一個(gè)虛擬的mac地址隧哮,對(duì)于網(wǎng)關(guān)來說,都像是一個(gè)主機(jī)上的實(shí)際網(wǎng)卡座舍;這里設(shè)置不同容器內(nèi)的網(wǎng)卡的時(shí)候沮翔,要相應(yīng)設(shè)置對(duì)應(yīng)的網(wǎng)關(guān)和交換機(jī)。
4.2.2. 802.1q分支橋接例子
用兩張圖來描述和普通橋接例子的區(qū)別
5. none模式
6. 容器模式
綜合各種網(wǎng)絡(luò)模式的優(yōu)劣
參考
Docker網(wǎng)絡(luò)深度解讀
最新實(shí)踐 | 將Docker網(wǎng)絡(luò)方案進(jìn)行到底
通過MacVLAN實(shí)現(xiàn)Docker跨宿主機(jī)互聯(lián)
Macvlan and IPvlan basics