计算机系统应用教程网站

网站首页 > 技术文章 正文

K8S的网络之Docker容器网络

btikc 2024-12-02 15:34:50 技术文章 31 ℃ 0 评论

原理

k8s的知识体系中,网络是一个难点。我将用一系列文章,对k8s的网络方案做一个深入浅出的分享。

虽然容器可以通过–net=host来指定使用宿主机的网络,但是这样就会引入共享网络资源的问题,比如端口冲突。所以一般情况下,我们希望容器进程能使用自己 Network Namespace 里的网络栈,即:拥有属于自己的 IP 地址和端口

那么这就出现了一个根本的问题

这个被隔离的容器进程,该如何跟其他 Network Namespace 里的容器进程进行交互呢

可以类比物理机进行思考,要让两台物理机互通,最简单的方法就是用一根网线把他们连起来。如果是多台物理机,我们就把他们都接到一台交换机上。

那么对于容器,也是同样的道理。

在 Linux 中,能够起到虚拟交换机作用的网络设备,是网桥(Bridge)。网桥是一个二层设备,工作在数据链路层。根据MAC地址将数据包转发到不同的端口。

Docker 会默认在宿主机上创建一个名叫 docker0 的网桥,

凡是连接在 docker0 网桥上的容器,就都可以通过它来进行通信


那么,下一个问题,docker容器怎么连接到docker0网桥上?

答案就是叫做Veth Pair的虚拟设备

根据名字就可以知道,Veth Pair设备的特点就是,虚拟的,并且是成对的。

可以将veth pair想象成一根管子的两端。从这个口放进去的数据包,可以直接出现在对应的另一个口上。即使这两个虚拟网卡在不同的network namespace里。

做个实验

docker run -d -it --name busy-yys busybox

启动一个busybox容器,然后进入容器查看网卡

docker exec -it busy-yys /bin/sh

看一下容器里的路由

# 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 来处理


再看宿主机上

# 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 10.250.151.208 netmask 255.255.255.0 broadcast 10.250.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在宿主机上的另一端

然后查看一下网桥的情况

# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02421e0d1279       no             veth066ee4f

可以看到,虚拟网卡veth066ee4f已经被注册到网桥docker0上了。

(brctl通过yum -y install bridge-utils安装)


我们再启动一个容器

docker run -d -it --name busy-2 busybox


# 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


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

# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02421e0d1279       no             veth066ee4f
                                                      veth3950859

新增的veth:veth3950859,也增加到了网桥docker0上

容器ping容器

在容器里互相ping,发现可以ping通。

原理也很简单,从容器的route信息看到

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

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网络。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表