K8S的网络之Docker容器网络
虽然容器可以通过–net=host来指定使用宿主机的网络,但是这样就会引入共享网络资源的问题,比如端口冲突。所以一般情况下,我们希望容器进程能使用自己 Network Namespace 里的网络栈,即:拥有属于自己的 IP 地址和端口
那么这就出现了一个根本的问题
这个被隔离的容器进程,该如何跟其他 Network Namespace 里的容器进程进行交互呢
可以类比物理机进行思考,要让两台物理机互通,最简单的方法就是用一根网线把他们连起来。如果是多台物理机,我们就把他们都接到一台交换机上。
那么对于容器,也是同样的道理。
在 Linux 中,能够起到虚拟交换机作用的网络设备,是网桥(Bridge)。网桥是一个二层设备,工作在数据链路层。根据MAC地址将数据包转发到不同的端口。
Docker 会默认在宿主机上创建一个名叫 docker0 的网桥,

凡是连接在 docker0 网桥上的容器,就都可以通过它来进行通信
那么,下一个问题,docker容器怎么连接到docker0网桥上?
答案就是叫做Veth Pair的虚拟设备
根据名字就可以知道,Veth Pair设备的特点就是,虚拟的,并且是成对的。
可以将veth pair想象成一根管子的两端。从这个口放进去的数据包,可以直接出现在对应的另一个口上。即使这两个虚拟网卡在不同的network namespace里。
做个实验
1
| docker run -d -it --name busy-yys busybox
|
启动一个busybox容器,然后进入容器查看网卡
1
| docker exec -it busy-yys /bin/sh
|

看一下容器里的路由
1 2 3 4 5
| # route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default 172.17.0.1 0.0.0.0 UG 0 0 0 eth0 172.17.0.0 * 255.255.0.0 U 0 0 0 eth0
|
这个容器里有一张叫作 eth0 的网卡,它正是一个 Veth Pair 设备在容器里的这一端
根据第二条路由,所有对 172.17.0.0/16 网段的请求,也会被交给 eth0 来处理
再看宿主机上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| # ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 inet6 fe80::42:1eff:fe0d:1279 prefixlen 64 scopeid 0x20<link> ether 02:42:1e:0d:12:79 txqueuelen 0 (Ethernet) RX packets 3 bytes 125 (125.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 11 bytes 858 (858.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 1x.xxx.151.208 netmask 255.255.255.0 broadcast 1x.xxx.151.255 inet6 fe80::f816:3eff:fe35:5192 prefixlen 64 scopeid 0x20<link> ether fa:16:3e:35:51:92 txqueuelen 1000 (Ethernet) RX packets 1789919595 bytes 294856793516 (274.6 GiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 1774732821 bytes 281152040711 (261.8 GiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth066ee4f: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::7829:83ff:fe0e:c99 prefixlen 64 scopeid 0x20<link> ether 7a:29:83:0e:0c:99 txqueuelen 0 (Ethernet) RX packets 3 bytes 167 (167.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 11 bytes 858 (858.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
虚拟设备veth066ee4f就是veth pair在宿主机上的另一端
然后查看一下网桥的情况
1 2 3
| # brctl show bridge name bridge id STP enabled interfaces docker0 8000.02421e0d1279 no veth066ee4f
|
可以看到,虚拟网卡veth066ee4f已经被注册到网桥docker0上了。
(brctl通过yum -y install bridge-utils安装)
我们再启动一个容器
1
| docker run -d -it --name busy-2 busybox
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| # docker exec -it busy-2 /bin/sh / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02 inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 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:656 (656.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
看到busy-2的ip是172.17.0.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| veth3950859: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::8ce9:31ff:fecf:6df0 prefixlen 64 scopeid 0x20<link> ether 8e:e9:31:cf:6d:f0 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 8 bytes 656 (656.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth066ee4f: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::7829:83ff:fe0e:c99 prefixlen 64 scopeid 0x20<link> ether 7a:29:83:0e:0c:99 txqueuelen 0 (Ethernet) RX packets 3 bytes 167 (167.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 11 bytes 858 (858.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
宿主机上增加了对应的veth
1 2 3 4
| # brctl show bridge name bridge id STP enabled interfaces docker0 8000.02421e0d1279 no veth066ee4f veth3950859
|
新增的veth:veth3950859,也增加到了网桥docker0上
容器ping容器
在容器里互相ping,发现可以ping通。
原理也很简单,从容器的route信息看到
1
| 172.17.0.0 * 255.255.0.0 U 0 0 0 eth0
|
gateway是*,表示这个网段172.17.0.0 /16是本地直连路由,不需要经过网关,应该经过本机的 eth0 网卡,通过二层网络直接发往目的主机 。
然后,对容器来说,本机的eth0,是veth pair的一端。根据前面的介绍,数据包会直接到宿主机上的另一端。
而另一端的虚拟网卡是插在网桥docker0上的。一旦插到网桥上,这个虚拟网卡的功能就只剩下接收数据包,对数据包的处理全部有网桥docker0掌握。
网桥docker0是一个二层设备,通过mac地址转发数据包。所以会通过arp广播,根据ip得到所有对应虚拟网卡的mac。
有了mac地址,网桥docker0就会将数据包发给对应的veth端口。
这样,数据包就会到达容器内对应的eth0口。
宿主机ping容器
同理,可以分析。在宿主机上直接ping 容器的ip,也是通的。
查看宿主机的route
1
| 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
|
可以看到这一条规则。0.0.0.0和*一样,同表示这是一条直连规则。
也是到网桥docker0。后面就一样了。
容器ping宿主机
容器的数据包,直接出现在docker0上,本身就是通的。
容器ping其他主机
只要目标主机和容器的宿主机是通的
那么容器的数据包经过 docker0 网桥出现在宿主机上,之后根据宿主机的路由表里的路由规则,就可以到达目标主机。
其他主机ping容器
很明显,不会通的。因为其他主机上并没有这个网桥docker0。
网桥docker0只能处理本机的网络。
一台宿主机上的 docker0 网桥,和其他宿主机上的 docker0 网桥,没有任何关联,它们互相之间也没办法连通
进而根据这种网桥的思路,如果我们要做到跨主机的容器互通,一个办法就是打造一个集群公用的超级网桥,将所有的容器都注册上来。
那么就可以做到跨节点的容器互通了。
这也就是下面要说的k8s的overlay网络。