知識,學(xué)過了之后,把它總結(jié)、分享出來,能讓自己對它的理解更加的深入。
因此,把以前學(xué)的Docker容器網(wǎng)絡(luò)模型歸納總結(jié)、并進行實驗。
后續(xù)再繼續(xù)對Kubernetes、CNI進行總結(jié)實驗。
Docker對網(wǎng)絡(luò)的支持,可以用如下的思維導(dǎo)圖來表示:

包括了None、Host、Bridge、Container、Overlay、Macvlan、IPvlan七種模型。
下面,針對每種網(wǎng)絡(luò)模型進行介紹與實驗。
一. 拔網(wǎng)線 - None模型
None,啥都沒有,類似于把網(wǎng)線給拔掉了。所以,這種模式下的容器,是一個封閉的環(huán)境。
適用于安全性高、又不需要網(wǎng)絡(luò)訪問的情景。
運行容器時,指定:--network=none即可。
$ docker run -it --rm --name=bbox --network=none busybox sh
運行一個BusyBox的容器,然后在容器內(nèi)可以看到:
/ # ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
該容器除了一個localhost的網(wǎng)卡,并沒有對外進行網(wǎng)絡(luò)通信的設(shè)備。
二. 寄生 - Host模型
使用該模式的容器,共享Host宿主機(運行Docker的機器)的網(wǎng)絡(luò)棧、網(wǎng)卡設(shè)備。
這種情況下,容器的網(wǎng)絡(luò)性能很好。但是不靈活,容器的端口容易與Host的端口沖突。Host A上能正常運行,換到了Host B未必就能正常運行。根據(jù)我的經(jīng)驗,這種模式很少有實際應(yīng)用。
運行容器時,指定:--network=host即可。
$ docker run -it --rm --name=bbox --network=host busybox sh
在容器內(nèi)看到的網(wǎng)卡信息:
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel qlen 1000
link/ether 00:0c:29:49:39:91 brd ff:ff:ff:ff:ff:ff
inet **192.168.111.128**/24 brd 192.168.111.255 scope global dynamic noprefixroute ens33
valid_lft 1242sec preferred_lft 1242sec
inet6 fe80::72bf:3960:42cd:13cb/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
link/ether 02:42:65:3a:0c:37 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:65ff:fe3a:c37/64 scope link
valid_lft forever preferred_lft forever
與在Host宿主機看到的信息是一致的:
$ 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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:49:39:91 brd ff:ff:ff:ff:ff:ff
altname enp2s1
inet **192.168.111.128**/24 brd 192.168.111.255 scope global dynamic noprefixroute ens33
valid_lft 1148sec preferred_lft 1148sec
inet6 fe80::72bf:3960:42cd:13cb/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:65:3a:0c:37 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:65ff:fe3a:c37/64 scope link
valid_lft forever preferred_lft forever
從容器內(nèi)訪問網(wǎng)絡(luò),一切正常:
/ # ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=53 time=34.735 ms
64 bytes from 8.8.8.8: seq=1 ttl=53 time=35.659 ms
64 bytes from 8.8.8.8: seq=2 ttl=53 time=35.603 ms
64 bytes from 8.8.8.8: seq=3 ttl=53 time=35.723 ms
三. 搭橋 - Bridge模型
這是Docker在運行容器時,默認的網(wǎng)絡(luò)模型。
Docker在安裝時,會自動在系統(tǒng)里面創(chuàng)建一個叫做docker0的網(wǎng)橋:
$ ip addr
......
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:65:3a:0c:37 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:65ff:fe3a:c37/64 scope link
valid_lft forever preferred_lft forever
繼續(xù)查看該網(wǎng)橋的詳細信息:
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "d92abe90e2bc79d8a4cd5ae73138d8da7aa0684a6a170fe7fc0ade4518057440",
"Created": "2023-04-18T11:02:11.199720084+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
**"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"**
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
根據(jù)上面的信息,Docker在運行容器時,會在172.17.0.0/16網(wǎng)段,為容器分配IP地址,并把Gateway指向172.17.0.1,即docker0這個虛擬設(shè)備。
而且,Docker會為運行的容器創(chuàng)建一對veth。該veth pair,一端接在容器內(nèi)部,另一端接在docker0上。使得容器可以通過docker0與外界通信。
運行一個容器,可以看到容器里面的網(wǎng)絡(luò)設(shè)備:
$ docker run -it --rm --name=bbox busybox sh
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
容器內(nèi)部有eth0網(wǎng)卡,它是veth的一端。在Host上,可以看到veth的另一端:
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242653a0c37 no vethe22e54f
網(wǎng)絡(luò)拓撲參考官方原圖:

1. 相同Host上的容器間網(wǎng)絡(luò)通信
在這種模式下,同一個Host上的不同容器,可以通過docker0直接通信。比如運行一個Nginx的容器:
$ docker run -it --rm --name=web nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2023/04/18 03:50:39 [notice] 1#1: using the "epoll" event method
2023/04/18 03:50:39 [notice] 1#1: nginx/1.23.4
2023/04/18 03:50:39 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2023/04/18 03:50:39 [notice] 1#1: OS: Linux 5.19.0-38-generic
2023/04/18 03:50:39 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2023/04/18 03:50:39 [notice] 1#1: start worker processes
2023/04/18 03:50:39 [notice] 1#1: start worker process 28
2023/04/18 03:50:39 [notice] 1#1: start worker process 29
在另一個shell窗口里,先查看該容器的IP地址,確定為172.17.0.2:
$ docker network inspect bridge
[
{
"Name": "bridge",
......
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
......
"Containers": {
"d3025a31c47c80bdcf711f329ff3c70677c28bfaed08d700743d91ad1bc33f15": {
"Name": "web",
"EndpointID": "d6f319d19fac52189c70fddb3a732f5b4081ff16fbe21e859c647ff8ff8ae7e6",
"MacAddress": "02:42:ac:11:00:02",
**"IPv4Address": "172.17.0.2/16",**
"IPv6Address": ""
}
},
......
}
]
然后,運行一個BusyBox的容器:
$ docker run -it --rm --name=bbox busybox sh
/ # wget 172.17.0.2:80
Connecting to 172.17.0.2:80 (172.17.0.2:80)
saving to 'index.html'
index.html 100% |********************************************************************************************************************************************************************************| 615 0:00:00 ETA
'index.html' saved
可以看到,BusyBox容器成功的訪問了Nginx容器。
2. 容器與外部網(wǎng)絡(luò)通信
2.1. 從內(nèi)到外:
Bridge模式下的容器,默認就可以訪問外部網(wǎng)絡(luò)。它依靠Host上的iptables,做了NAT地址轉(zhuǎn)換。
啟動一個BusyBox的容器,得到的IP是172.17.0.2。它的Host IP是:192.168.111.128。在容器內(nèi)可以直接訪問外部的另一臺機器:192.168.111.129。
$ docker run -it --rm --name=bbox busybox sh
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
/ # ping 192.168.111.129
PING 192.168.111.129 (192.168.111.129): 56 data bytes
64 bytes from 192.168.111.129: seq=0 ttl=63 time=0.521 ms
64 bytes from 192.168.111.129: seq=1 ttl=63 time=0.465 ms
2.2. 從外到內(nèi):
如果需要外部網(wǎng)絡(luò)訪問Bridge模式下的容器,可以通過端口映射功能。在運行容器時,指定Host端口A與容器端口B的映射。然后,通過訪問:Host-IP:Host端口A,即可映射到:容器:容器端口B。
我們做個試驗,把前面的Nginx容器和BusyBox容器全部退出。然后重新運行一個新的Nginx容器,并通過-p參數(shù)指定端口映射:
$ docker run -it --rm --name=web -p 8080:80 nginx
從另外一臺機器發(fā)起訪問,192.168.111.128是Host的IP地址:
$ wget 192.168.111.128:8080
Connecting to 192.168.111.128:8080... connected.
HTTP request sent, awaiting response... 200 OK
Length: 615 [text/html]
Saving to: ‘index.html’
index.html 100%[===============================================================================================================================>] 615 --.-KB/s in 0s
‘index.html’ saved [615/615]
四. 如影隨形 - Container模型
這個模式我沒看到官方的名字,名字我瞎取的,但是在Kubernetes的Pod里面經(jīng)常用。
具體的做法,是在容器B運行時,指定:--network=container:容器A的名字或者ID。
這樣,容器A、B處于同一個網(wǎng)絡(luò)空間。它們的MAC地址、IP地址都一樣,共享網(wǎng)絡(luò)棧、網(wǎng)卡和配置信息。它們可以通過127.0.0.1直接通信。
在Kubernetes部署Pod的時候,就會用到這個模式。針對每個Pod,Kubernetes先啟動Pause容器,然后再啟動其它容器并使用Pause容器的網(wǎng)絡(luò)。這樣,同一個Pod之內(nèi)的容器,共享了同一個網(wǎng)絡(luò)空間,可以高效的通信。
試驗看看,先啟動一個Nginx容器:
$ docker run -it --rm --name=web nginx
看看Docker網(wǎng)絡(luò)情況:
$ docker network inspect bridge
[
{
"Name": "bridge",
......
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
......
"Containers": {
"da67b03b02c2e99fdaaa2fd75b7829c4005eba80a50f39404db4da8d8defa0e3": {
"Name": "web",
"EndpointID": "0e29b7473a8446100dc45a711536b0277ae0f911cb4c2decc7245511fd2dbb02",
**"MacAddress": "02:42:ac:11:00:02",**
**"IPv4Address": "172.17.0.2/16",**
"IPv6Address": ""
}
},
}
]
Nginx容器的IP地址是:172.17.0.2,MAC地址是:02:42:ac:11:00:02。
再啟動一個BusyBox容器,并使用Nginx的網(wǎng)絡(luò):
$ docker run -it --rm --name=bbox --network=container:web busybox sh
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
16: eth0@if17: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether **02:42:ac:11:00:02** brd ff:ff:ff:ff:ff:ff
inet **172.17.0.2**/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
可以看到,BusyBox的IP地址、MAC地址,和Nginx的一模一樣。
直接訪問127.0.0.1可以得到:
/ # wget 127.0.0.1
Connecting to 127.0.0.1 (127.0.0.1:80)
saving to 'index.html'
index.html 100% |********************************************************************************************************************************************************************************| 615 0:00:00 ETA
'index.html' saved
這種模式除了K8S的Pod之外,還可以用在簡易版的Web Server + App Server情景。
五. 套娃 - Overlay模型
Docker通過Overlay模式,實現(xiàn)了對VXLAN的支持。這個模式的環(huán)境搭建比別的模式稍顯復(fù)雜,主要是因為需要有一個地方來保存各個節(jié)點在overlay網(wǎng)絡(luò)中的配置信息。一般是在另一個機器安裝etcd或者Consul這種key-value數(shù)據(jù)庫。
偷懶起見,我直接使用了Docker自帶的Swarm來搭建環(huán)境。準(zhǔn)備了兩臺機器A、B。A身兼兩職,既保存數(shù)據(jù)庫,又運行容器。
-
首先,在機器A,初始化swarm:
ycwang@ycwang-ubuntu:~$ docker swarm init Swarm initialized: current node (qygp7ymrfh5g0lgky10teck4r) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-3rjaah******rowo 192.168.111.128:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions. -
換到機器B,Copy上面的join命令,加入集群:
ycwang@ycwang-ubuntu-slave:~$ docker swarm join --token SWMTKN-1-3rjaah******rowo 192.168.111.128:2377 This node joined a swarm as a worker. -
回到機器A,可以看到集群的情況:
ycwang@ycwang-ubuntu:~$ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION qygp7ymrfh5g0lgky10teck4r * ycwang-ubuntu Ready Active Leader 23.0.4 sr637j4g891bsxo56tesv55y8 ycwang-ubuntu-slave Ready Active 23.0.4 -
在機器A上,可以看到Docker為Overlay模式,創(chuàng)建了兩個新的網(wǎng)絡(luò),docker_gwbridge和ingress。后面運行的容器,會通過docker_gwbridge與外部網(wǎng)絡(luò)進行通信(南北向流量):
ycwang@ycwang-ubuntu:~$ docker network ls NETWORK ID NAME DRIVER SCOPE 51276c2e1741 bridge bridge local 596dbdd24c3a docker_gwbridge bridge local fc504698f255 host host local tmbwbg86eph4 ingress overlay swarm 4829db6948ad none null local -
在機器A上,為Docker創(chuàng)建Overlay網(wǎng)絡(luò):
ycwang@ycwang-ubuntu:~$ docker network create --driver=overlay vxlanA thya4qliq95dh81yndfqpimwn -
在機器A上,創(chuàng)建服務(wù),使用vxlanA這個網(wǎng)絡(luò),replicas 指定為 2:
ycwang@ycwang-ubuntu:~$ docker service create --network=vxlanA --name bboxes --replicas 2 busybox ping 8.8.8.8 q44lh7mwwpgbae7fleilgenk2 overall progress: 2 out of 2 tasks 1/2: running [==================================================>] 2/2: running [==================================================>] verify: Service converged注意,busybox后面的ping 8.8.8.8,并不是為了讓它去ping,目的只是讓這個容器不要馬上退出,否則Service會不停的重啟這兩個容器。別問我為什么知道的……
-
分別在兩個機器上查看容器的信息:
ycwang@ycwang-ubuntu:~$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a9f2b06f0f9e busybox:latest "ping 8.8.8.8" 16 minutes ago Up 16 minutes bboxes.2.m6qqi8k75rvr8ukk4ll6jfrnp ycwang@ycwang-ubuntu:~$ docker exec -it a9f2b06f0f9e sh / # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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 62: eth0@if63: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue link/ether 02:42:0a:00:01:21 brd ff:ff:ff:ff:ff:ff inet 10.0.1.33/24 brd 10.0.1.255 scope global eth0 valid_lft forever preferred_lft forever 64: eth1@if65: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:13:00:03 brd ff:ff:ff:ff:ff:ff inet 172.19.0.3/16 brd 172.19.255.255 scope global eth1 valid_lft forever preferred_lft foreverycwang@ycwang-ubuntu-slave:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 04aacff98016 busybox:latest "ping 8.8.8.8" 18 minutes ago Up 18 minutes bboxes.1.ky0fcmy5geudr2fothxxh05y3 ycwang@ycwang-ubuntu-slave:~$ docker exec -it 04aacff98016 sh / # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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 62: eth0@if63: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue link/ether 02:42:0a:00:01:20 brd ff:ff:ff:ff:ff:ff inet 10.0.1.32/24 brd 10.0.1.255 scope global eth0 valid_lft forever preferred_lft forever 64: eth1@if65: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:13:00:03 brd ff:ff:ff:ff:ff:ff inet 172.19.0.3/16 brd 172.19.255.255 scope global eth1 valid_lft forever preferred_lft forever可以發(fā)現(xiàn),每個容器會帶兩張網(wǎng)卡。
eth1 - 172.19.0.3接在前面的docker_gwbridge網(wǎng)橋上,負責(zé)與外部網(wǎng)絡(luò)的南北向流量。通過docker network inspect docker_gwbridge可以確認這個信息。
etho - 10.0.1.32/24,10.0.1.33/24,屬于vxlanA網(wǎng)絡(luò),負責(zé)VXLAN內(nèi)部的東西向流量。通過docker network inspect vxlanA可以確認這個信息。
-
從容器A ping 容器B:
/ # ping 10.0.1.32 PING 10.0.1.32 (10.0.1.32): 56 data bytes 64 bytes from 10.0.1.32: seq=0 ttl=64 time=0.735 ms 64 bytes from 10.0.1.32: seq=1 ttl=64 time=0.556 ms兩個容器之間是可以通信的。此時,用tcpdump在Host宿主機的網(wǎng)卡上抓包:
$ sudo tcpdump -i ens33 udp port 4789 -s 0 -X -nnn -vvv tcpdump: listening on ens33, link-type EN10MB (Ethernet), snapshot length 262144 bytes 15:50:57.566431 IP (tos 0x0, ttl 64, id 34769, offset 0, flags [none], proto UDP (17), length 134) 192.168.111.128.42627 > 192.168.111.129.4789: [bad udp cksum 0x60d6 -> 0xb9a9!] VXLAN, flags [I] (0x08), vni 4097 IP (tos 0x0, ttl 64, id 30207, offset 0, flags [DF], proto ICMP (1), length 84) 10.0.1.33 > 10.0.1.32: ICMP echo request, id 23, seq 0, length 64 0x0000: 4500 0086 87d1 0000 4011 9243 c0a8 6f80 E.......@..C..o. 0x0010: c0a8 6f81 a683 12b5 0072 60d6 0800 0000 ..o......r`..... 0x0020: 0010 0100 0242 0a00 0120 0242 0a00 0121 .....B.....B...! 0x0030: 0800 4500 0054 75ff 4000 4001 ae69 0a00 ..E..Tu.@.@..i.. 0x0040: 0121 0a00 0120 0800 ae96 0017 0000 bbd5 .!.............. 0x0050: 8d7c 0000 0000 0000 0000 0000 0000 0000 .|.............. 0x0060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x0070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x0080: 0000 0000 0000 ......可以清楚的看到從容器A - 10.0.1.33 到容器B - 10.0.1.32的ICMP數(shù)據(jù)包,被VNI為4097的VXLAN封裝。
封裝后,變成了從Host A - 192.168.111.128.42627到Host B - 192.168.111.129.4789的UDP數(shù)據(jù)包。
通過這個模型,實現(xiàn)了容器間的直連,虛擬的二層直連。Overlay模型也成了許多云廠商采用的實現(xiàn)方案。
六. 狡兔三窟 - Macvlan模型
Macvlan是一種網(wǎng)卡虛擬化技術(shù),將一張物理網(wǎng)卡(父接口)虛擬出多張網(wǎng)卡(子接口)。每個子接口有自己獨立的 MAC 地址和 IP 地址。
物理網(wǎng)卡(父接口)相當(dāng)于一個交換機,記錄著對應(yīng)的虛擬網(wǎng)卡(子接口)和 MAC 地址。當(dāng)物理網(wǎng)卡收到數(shù)據(jù)包后,會根據(jù)目的 MAC 地址判斷這個包屬于哪一個虛擬網(wǎng)卡,并轉(zhuǎn)發(fā)給它。
Macvlan技術(shù)有四種模式,Docker支持其中的bridge模式。
接下來,試驗看看。
-
首先,需要打開網(wǎng)卡的混雜模式,否則它拒絕接收MAC地址跟它不一樣的數(shù)據(jù)報文。ens33是Host機器的物理網(wǎng)卡:
$ sudo ip link set ens33 promisc on -
第二步,為Docker創(chuàng)建一個Macvlan網(wǎng)絡(luò)。子網(wǎng)是:192.168.111.0/24,跟Host一樣;指定父接口為ens33
$ docker network create --driver=macvlan --subnet=192.168.111.0/24 --gateway=192.168.111.1 -o parent=ens33 macvnet 0283990d6acdc9df87d5b34a999c05266e12a4423aa0041387373d8bc5ee042c -
第三步,運行容器,指定其IP地址為:192.168.111.10,并使用上一步創(chuàng)建的Macvlan網(wǎng)絡(luò):
$ docker run -it --rm --name=web --ip=192.168.111.10 --network=macvnet nginx
這樣,Nginx這個容器就運行在了192.168.110.10這個地址上,從外部機器可以直接訪問它:
$ wget 192.168.111.10
Connecting to 192.168.111.10:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 615 [text/html]
Saving to: ‘index.html’
index.html 100%[===============================================================================================================================>] 615 --.-KB/s in 0s
‘index.html’ saved [615/615]
可以看到,Macvlan 是一種將容器通過二層,連接到物理網(wǎng)絡(luò)不錯的方案,配置簡單、性能好。但它也有一些局限性,比如:
物理網(wǎng)卡所連接的交換機,可能會限制同一個物理端口上的 MAC 地址數(shù)量。
許多物理網(wǎng)卡上的 MAC地址數(shù)量也有限制。
七. 狡兔三窟Plus - IPvlan模型
IPvlan是一個比較新的特性,Linux內(nèi)核>= 4.2之后才可以穩(wěn)定的使用。
與Macvlan類似,都是從一個物理網(wǎng)卡(父接口)虛擬出多張網(wǎng)卡(子接口)。與Macvlan不同的是,這些子接口的MAC地址都是一樣的,不一樣的只是它們的IP地址。而且,它不像Macvlan那樣,要求物理網(wǎng)卡打開混雜模式。
IPvlan有兩種模式:L2和L3模式。顧名思義,L2模式跟交換機有關(guān),L3模式則跟路由器有關(guān)。
1. L2模式
IPvlan的L2模式,跟之前的Macvlan非常類似。容器的子接口與父接口在同一子網(wǎng),父接口做為交換機來轉(zhuǎn)發(fā)子接口的數(shù)據(jù)。如果是與外部網(wǎng)絡(luò)通信,則依賴父接口進行路由轉(zhuǎn)發(fā)。
首先,為Docker創(chuàng)建一個L2模式的IPvlan網(wǎng)絡(luò):
$ docker network create --driver=ipvlan --subnet=192.168.0.0/24 --gateway=192.168.0.1 -o ipvlan_mode=l2 -o parent=ens33 ipv_l2
7091b861fd44798d21be6d3dcdd03e79c68d65e9149862b9f21bca42678fda19
該網(wǎng)絡(luò)與Host宿主機同在192.168.0.0/24網(wǎng)段,Host的網(wǎng)卡是ens33,IP是192.168.0.105。
使用該網(wǎng)絡(luò),運行第一個容器:
$ docker run -it --rm --network=ipv_l2 --name=bbox1 busybox sh
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
4: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 00:0c:29:12:5b:f4 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.2/24 brd 192.168.0.255 scope global eth0
valid_lft forever preferred_lft forever
該容器的IP地址為192.168.0.2。MAC地址與Host宿主機的ens33一致。
從容器內(nèi),訪問另外一臺機器B:
/ # ping 192.168.0.112
PING 192.168.0.112 (192.168.0.112): 56 data bytes
64 bytes from 192.168.0.112: seq=0 ttl=64 time=79.549 ms
從另外的機器B,訪問該容器:
$ ping 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=3.94 ms
可以看到,容器的網(wǎng)絡(luò)訪問都是沒問題的。
2. L3模式
這個模式下,容器跟Host宿主機可以不在同一個子網(wǎng)。該模式的配置,網(wǎng)上的資料比較少,Docker官網(wǎng)也是語焉不詳?shù)摹?/p>
假設(shè)Host宿主機的父接口是ens33,IP地址是192.168.0.105/24。
現(xiàn)在想要創(chuàng)建兩個容器,分別屬于不同的子網(wǎng),例如10.0.1.0/24和10.0.2.0/24,并讓它們可以相互通信,也可以訪問外部網(wǎng)絡(luò)。
可以按照如下的步驟來實現(xiàn)。
-
首先,把之前環(huán)境下的容器退出,并清理資源,因為一個父接口不能同時支持L2和L3模式:
$ docker system prune -
創(chuàng)建一個新的IPvlan網(wǎng)絡(luò):
$ docker network create --driver=ipvlan --subnet=10.0.1.0/24 --subnet=10.0.2.0/24 -o parent=ens33 -o ipvlan_mode=l3 ipvlan-l3 -
在兩個Terminal窗口,分布運行一個容器,并連接到剛剛創(chuàng)建的IPvlan網(wǎng)絡(luò)、使用不同的子網(wǎng)。它們的IP地址分別為10.0.1.10和10.0.2.10:
$ docker run -it --rm --name bbox1 --network ipvlan-l3 --ip 10.0.1.10 busybox sh / # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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 4: eth0@if2: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 00:0c:29:12:5b:f4 brd ff:ff:ff:ff:ff:ff inet 10.0.1.10/24 brd 10.0.1.255 scope global eth0 valid_lft forever preferred_lft forever$ docker run -it --rm --name bbox2 --network ipvlan-l3 --ip 10.0.2.10 busybox sh / # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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 5: eth0@if2: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 00:0c:29:12:5b:f4 brd ff:ff:ff:ff:ff:ff inet 10.0.2.10/24 brd 10.0.2.255 scope global eth0 valid_lft forever preferred_lft forever -
容器之間互通性。此時,兩個容器之間已經(jīng)可以互相訪問。
從bbox1訪問bbox2:
/ # ping 10.0.2.10 PING 10.0.2.10 (10.0.2.10): 56 data bytes 64 bytes from 10.0.2.10: seq=0 ttl=64 time=0.270 ms 64 bytes from 10.0.2.10: seq=1 ttl=64 time=0.061 ms從bbox2訪問bbox1:
/ # ping 10.0.1.10 PING 10.0.1.10 (10.0.1.10): 56 data bytes 64 bytes from 10.0.1.10: seq=0 ttl=64 time=0.077 ms 64 bytes from 10.0.1.10: seq=1 ttl=64 time=0.077 ms -
但此時,從外部網(wǎng)絡(luò)訪問這兩個容器依然是無法到達的。因為外部的網(wǎng)絡(luò)環(huán)境里,沒有關(guān)于10.0.1.10或者10.0.2.10這兩個IP地址的路由信息。
需要在外部路由器上添加相應(yīng)的路由規(guī)則,讓它知道如何到達容器網(wǎng)絡(luò)。
假設(shè)外部路由器的接口為eth1,IP地址為192.168.0.1/24。
添加路由規(guī)則,將目標(biāo)地址為10.0.1.0/24或10.0.2.0/24的數(shù)據(jù)包轉(zhuǎn)發(fā)到192.168.0.105,即,轉(zhuǎn)發(fā)到容器的父接口ens33。
$ sudo ip route add 10.0.1.0/24 via 192.168.0.105 dev eth1 $ sudo ip route add 10.0.2.0/24 via 192.168.1.105 dev eth1這樣,就可以實現(xiàn)IPvlan L3模式的容器與外部網(wǎng)絡(luò)的通信。Sorry,我忘了我家里路由器的密碼了,暫時沒法登錄實驗……
綜合運用下來,感覺IPvlan模式應(yīng)該比Macvlan模式更加實用,因為Macvlan擁有的功能,IPvlan的L2模式都有,而且還少了混雜模式、MAC地址數(shù)目的潛在問題。除此之外,IPvlan還多了L3模式的支持。