主页 > imtoken钱包下载安卓 > 张磊:浅谈容器网络

张磊:浅谈容器网络

imtoken钱包下载安卓 2023-04-26 05:46:36

分享一下Kubernetes社区资深成员、项目维护者“张磊”对这个话题的思考。

以太坊私链_以太坊单机联盟链_以太坊私有链搭建

大家好,我是张磊。 今天和大家分享的话题是:浅谈容器网络。

在解释容器的基础知识时,我提到了 Linux 容器可以看到的“网络堆栈”实际上是隔离在自己的 Network Namespace 中的。

所谓“网络栈”包括:网络接口卡(Network Interface)、环回设备(Loopback Device)、路由表(Routing Table)和iptables规则。 对于一个进程来说,这些元素实际上构成了它发起和响应网络请求的基本环境。

需要指出的是,作为容器,可以声明直接使用宿主机的网络栈(–net=host),即不开启Network Namespace,例如:

$ docker run –d –net=host --name nginx-host nginx

这种情况下,容器启动后,直接监听宿主机的80端口。

像这样直接使用主机网络栈可以为容器提供良好的网络性能,但不可避免地会引入共享网络资源的问题,比如端口冲突。 因此,在大多数情况下,我们希望容器进程能够使用自己的Network Namespace中的网络栈,即拥有自己的IP地址和端口。

这时候一个明显的问题就是:这个隔离的容器进程如何与其他Network Namespaces中的容器进程进行交互?

为了理解这个问题,其实可以把每一个容器想象成一个主机,它们都有一个独立的“网络栈”。

以太坊私链_以太坊单机联盟链_以太坊私有链搭建

如果要实现两台主机之间的通信,最直接的方式就是用网线连接它们; 而如果要实现多台主机之间的通信,则需要使用网线将它们连接到一台交换机上。

在 Linux 中以太坊单机联盟链,可以充当虚拟交换机的网络设备是网桥。 它是工作在数据链路层(Data Link)的设备,其主要功能是根据MAC地址学习,将数据包转发到网桥的不同端口(Ports)。

当然,至于这些主机为什么需要MAC地址才能通信,这是网络分层模型的基础知识。 对此内容不熟悉的读者可以通过本文进行学习。

为了达到上述目的,Docker项目默认会在宿主机上创建一个名为docker0的网桥,所有连接到docker0网桥的容器都可以通过它进行通信。

但是我们如何将这些容器“连接”到 docker0 网桥呢?

这时候我们就需要用到一个虚拟设备,叫做Veth Pair。

Veth Pair设备的特点是在创建之后,总是以两张虚拟网卡(Veth Peer)的形式成对出现。 而且,其中一个“网卡”发出的数据包可以直接出现在对应的另一个“网卡”上,即使两个“网卡”在不同的Network Namespace。

这使得 Veth Pair 经常被用作连接不同网络命名空间的“网线”。

例如,现在我们启动一个名为 nginx-1 的容器:

$ docker run –d --name nginx-1 nginx

然后进入这个容器查看它的网络设备:

以太坊私有链搭建_以太坊单机联盟链_以太坊私链

# 在主机上 $ docker exec -it nginx-1 /bin/bash # 在容器中 root@2b3c181aecf1:/# ifconfigeth0: flags=4163 mtu 1500 inet 172.17.0.2 netmask 255.255.0.0 broadcast 0.0.0.0 inet6 fe80:: 42 :acff:fe11:2 prefixlen 64 scopeid 0x20 ether 02:42:ac:11:00:02 txqueuelen 0(以太网)RX 数据包 364 字节 8137175(7.7 MiB)RX 错误 0 丢弃 0 溢出 0 帧 0 TX 数据包 281 字节 21161 (20.6 KiB) TX 错误 0 dropped 0 overruns 0 carrier 0 collisions 0lo: flags=73 mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10 loop txqueuelen by 0Loopqueuelen 1 Lo0pack (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0$ routeKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Ifacedefault 0.0.17.0.0.0 0.0 UG 0 0 0 eth0 172.17 .0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0

可以看到这个容器中有一个网卡叫eth0,正好是容器中一个Veth Pair设备的末端。

通过route命令查看nginx-1容器的路由表,我们可以看到eth0网卡是这个容器中的默认路由设备; 所有172.17.0.0/16网段的请求也会交给eth0处理(第二条172.17.0.0路由规则)。

而这个 Veth Pair 设备的另一端在主机上。 您可以通过查看主机的网络设备来了解这一点,如下所示:

# 在主机上 $ ifconfig...docker0 Link encap:Ethernet HWaddr 02:42:d8:e4:df:c1 inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::42 : d8ff:fee4:dfc1/64 范围:链接上行广播运行多播 MTU:1500 指标:1 RX 数据包:309 错误:0 丢弃:0 超限:0 帧:0 TX 数据包:372 错误:0 丢弃:0 超限:0 运营商:0collisions:0 txqueuelen:0 RX字节:18944(18.9 KB)TX字节:8137789(8.1 MB)veth9c02e56链接encap:以太网HWaddr 52:81:0b:24:3d:da inet6地址:fe80 :: 5081:bff: fe24:3dda/64 范围:链接上行广播运行多播 MTU:1500 指标:1 RX 数据包:288 错误:0 丢弃:0 溢出:0 帧:0 TX 数据包:371 错误:0 丢弃:0 溢出:0 载体:0 碰撞:0 txqueuelen:0 RX 字节:21608 (21.6 KB) TX 字节:8137719 (8.1 MB)$ brctl showbridge 名称桥 ID STP 启用接口docker0 8000.0242d8e4dfc1 no veth9c02e56

从ifconfig命令的输出可以看出nginx-1容器对应的Veth Pair设备是宿主机上的虚拟网卡。 它的名字叫做veth9c02e56。 并且,通过brctl show的输出,可以看到这张网卡是“插”在docker0中的。

这时候如果我们在这台主机上再启动一个Docker容器,比如nginx-2:

$ docker run –d --name nginx-2 nginx$ brctl showbridge name bridge id STP enabled interfacesdocker0 8000.0242d8e4dfc1 no veth9c02e56 vethb4963f3

你会发现一个名为 vethb4963f3 的新虚拟网卡,它也被“插入”在 docker0 网桥上。

这时候在nginx-1容器中ping一下nginx-2容器(172.17.0.3)的IP地址,会发现同一主机上的两个容器默认是连通的。

这个的原理其实很简单,我来解释一下。

以太坊单机联盟链_以太坊私有链搭建_以太坊私链

当你在nginx-1容器中访问nginx-2容器的IP地址时(比如ping 172.17.0.3),这个目的IP地址会匹配nginx-1容器中的第二条路由规则。 可以看出这条路由规则的网关(Gateway)是0.0.0.0,说明这是一条直连规则,即所有符合这条规则的IP包都要经过本地eth0网卡,并通过通过二层网络直接发送到目的主机。

通过二层网络到达nginx-2容器,需要IP地址172.17.0.3对应的MAC地址。 所以nginx-1容器的网络协议栈需要通过eth0网卡发送ARP广播,​​通过IP地址找到对应的MAC地址。

备注:ARP(Address Resolution Protocol)是一种通过三层IP地址找到对应二层MAC地址的协议。 前面我们提到,这个eth0网卡是一个Veth Pair,一端在nginx-1容器的Network Namespace中,另一端位于宿主机(Host Namespace)上,“插”在主机的 docker0 桥上的主机。 一旦虚拟 NIC 被“插入”到网桥中,它就成为网桥的“奴隶”。 从设备将被“剥夺”调用网络协议栈处理数据包的资格,从而“降级”成为网桥上的一个端口。 这个端口的唯一作用就是接收进来的数据包,然后把这些数据包的“生死权”(比如转发或者丢弃)交给对应的网桥。 因此,docker0网桥收到这些ARP请求后,会作为二层交换机,将ARP广播转发给“插”在docker0上的其他虚拟网卡。 这样docker0连接的nginx-2容器的网络协议栈就会收到ARP请求,将172.17.0.3对应的MAC地址回复给nginx-1容器。 有了这个目的MAC地址,nginx-1容器的eth0网卡就可以发送数据包了。

根据Veth Pair设备的原理,这个数据包会立刻出现在主机上的veth9c02e56虚拟网卡上。 然而此时veth9c02e56网卡的网络协议栈资格已经被“剥夺”了,所以数据包直接流入了docker0网桥。 docker0在转发过程中继续扮演二层交换机的角色。 此时docker0网桥根据数据包的目的MAC地址(即MAC地址)在自己的CAM表(即交换机通过MAC地址学习维护的端口与MAC地址对应表)中查找nginx-2 容器)。 对应的端口(Port)为:vethb4963f3,然后将数据包发送到这个端口。 而这个端口是nginx-2容器“插入”在docker0网桥上的另一个虚拟网卡。 当然,它也是一个 Veth Pair 设备。 这样数据包就进入了nginx-2容器的Network Namespace。 所以nginx-2容器看到的是自己的eth0网卡上有进来的包。 这样nginx-2的网络协议栈就会对请求进行处理,最后将response(Pong)返回给nginx-1。 以上就是同一主机上不同容器之间通过docker0网桥进行通信的过程。 我把这个过程总结成一个示意图,如下:

以太坊单机联盟链_以太坊私链_以太坊私有链搭建

需要注意的是,在实际的数据传输中,上述数据传输过程涉及网络协议栈不同层次的Linux内核Netfilter的参与。 所以,有兴趣的可以通过打开iptables的TRACE功能查看数据包的传输过程,具体方法如下:

# 在主机上执行 $ iptables -t raw -A OUTPUT -p icmp -j TRACE $ iptables -t raw -A PREROUTING -p icmp -j TRACE

通过以上设置,可以在/var/log/syslog中看到数据包传输的日志。 对于这部分内容,大家可以在课后结合iptables的相关知识进行练习,从而验证我分享给大家的发包流程。

如果熟悉docker0网桥的工作方式,就可以理解,默认情况下,受限于Network Namespace的容器进程实际上是通过Veth Pair device + host bridge实现与其他容器相同的进程。 数据交换。

同样,当你在某台主机上访问某台主机上容器的IP地址时,请求的数据包也是按照路由规则先到达docker0网桥,然后转发到对应的Veth Pair设备,最终出现在容器。 这个过程的示意图如下所示:

以太坊私链_以太坊单机联盟链_以太坊私有链搭建

以太坊单机联盟链_以太坊私有链搭建_以太坊私链

同理,当一个容器尝试连接另一台主机时,例如:ping 10.168.0.3,它发送的请求包首先通过docker0网桥出现在主机上。 然后根据主机路由表中的直连路由规则(10.168.0.0/24 via eth0),将10.168.0.3的访问请求交给主机的eth0处理。

所以接下来,这个数据包会通过宿主机的eth0网卡转发到宿主机网络,最终到达10.168.0.3对应的宿主机。 当然,这个过程的实现需要两台主机本身是相连的。 这个过程的示意图如下所示:

以太坊私链_以太坊单机联盟链_以太坊私有链搭建

因此,当遇到无法连接“外网”的容器时,应先尝试是否能ping通docker0网桥,再检查docker0和Veth Pair设备相关的iptables规则是否异常。 您将能够找到问题的答案。

不过,在上一个“Docker容器连接其他主机”的例子中,大家可能已经想到了这样一个问题:如果在另一台主机(例如:10.168.0.3)上有一个Docker容器。 那么,我们的nginx-1容器应该如何访问呢?

这个问题其实就是容器的“跨master通信”问题。

在Docker的默认配置下,一台主机上的docker0网桥与其他主机上的docker0网桥是没有关联的,它们之间无法通信。 因此,连接到这些网桥上的容器自然是没有办法通信的。

但是,一切都保持不变。

如果我们用软件为整个集群创建一个“通用”的网桥,然后将集群中的所有容器都连接到这个网桥上,它们之间不就不能互相通信了吗?

你是对的。

这样,我们整个集群中的容器网络就会如下图所示:

以太坊单机联盟链_以太坊私链_以太坊私有链搭建

以太坊单机联盟链_以太坊私链_以太坊私有链搭建

可以看出,构建这种容器网络的核心是我们需要构建一个覆盖现有宿主网络的虚拟网络,能够在现有宿主网络上通过软件将所有容器连接在一起。 . 因此以太坊单机联盟链,这种技术被称为:Overlay Network(覆盖网络)。

而这个Overlay Network本身可以由每台主机上的“特殊网桥”组成。 例如,当节点1上的Container 1要访问节点2上的Container 3时,Node 1上的“特殊网桥”收到数据包后,可以通过某种方式将数据包发送到正确的目的地。 主机,如节点2。节点2上的“特殊网桥”收到数据包后,也可以通过某种方式将数据包转发到正确的容器,如容器3。

甚至,每个主机都不需要有这样一个特殊的网桥,而只是通过一定的方式配置主机的路由表,就可以将数据包转发到正确的主机上。 这些内容,我会在接下来的文章中一一告诉大家。

总结

在今天的文章中主要给大家介绍一下单机容器网络的实现原理以及docker0网桥在本地环境中的作用。

这里的关键是,如果容器要与外界通信,它发送的 IP 数据包必须从它的网络命名空间中出来,到达主机。

解决这个问题的方法是为容器创建一个Veth Pair设备,一端作为容器中的默认网卡,另一端在宿主机上。

上述单机容器网络的知识是我们后面讲解多机容器网络的重要基础,请务必认真消化理解。

思维问题

容器的Host Network模式虽然有一些劣势,但是其性能好,配置简单,调试容易,所以很多团队会直接使用Host Network。 那么,如果想在生产环境中使用容器的主机网络模式,您认为还需要做哪些额外的准备工作呢?