容器网络

Posted xzkzzz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了容器网络相关的知识,希望对你有一定的参考价值。

容器的网络栈

所谓的网络栈包括:网卡、回环设备、路由表、Iptables规则,而对于一个进程来讲,这些要素是构成发起和响应网络请求的基本环境。而对于容器来讲可以直接使用宿主机的网络栈,即不开启Network Namespace,例如:

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

直接监控到宿主机80端口

 netstat -ntlp  | grep nginx 
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      29133/nginx: master 

虽然这种方式会提高容器网络性能,但也会不可避免的引入共享网络资源的问题,例如端口冲突等。所以,在大多数情况下,我们都希望容器进程能使用自己的Netword Namespace里的网络栈,拥有自己的IP和端口。

容器与容器之间如何通信

这里我们可以把每一个容器看做一台主机,他们都有自己的一套网络栈。如下想让两个容器互通最简单的办法就是连接一根网线。而想要多台主机相互通信,那就需要一根网线连接到交换机上。在Linux中能起虚拟交换机的作用设备那就是网桥(Bridge),它是工作在数据链路层的设备,主要功能就是根据MAC地址学习来将数据包转发到网桥的不同端口。

而Docker创建的时候会默认在宿主机上创建一个docker 0的网桥,凡是连接在docker 0网桥上的容器,就可以通过他来通信。连接docker 0网桥需要一个Veth Pair的虚拟设备。

Veth Pair特点:它被创建后,总是以两张虚拟网卡的形式成对出现,并且,从其中一个“网卡”发出的数据包可以直接出现在另一张“网卡”上,哪怕这两张网卡在不通的namespace里。这使得Veth Pair常常被称作两个容器间的“网线”。

演示:

启动一个myapp启动,命名为myapp-1

docker run -d --name myapp-1 ikubernetes/myapp:v1

进入到容器,查看一下网络

#进去容器
$ docker exec -it myapp-1 /bin/sh
#查看网络
$ ifconfig 
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:13 errors:0 dropped:0 overruns:0 frame:0
          TX packets:11 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1046 (1.0 KiB)  TX bytes:866 (866.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          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)
$ 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设备在容器的这一端,通过route命令查看myapp-1的路由表,可以看到,这个eth0网卡是这个容器的默认路由设备。所有对172.17.0.0/16 网络的请求,也会被交给eth0来处理(第二条规则)

而Veth Pair设备另一端而在宿主机上,我们可以查看宿主机网络设备。如下:

$ ifconfig 
.....
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
        inet6 fe80::42:a0ff:fed8:f7fb  prefixlen 64  scopeid 0x20<link>
        ether 02:42:a0:d8:f7:fb  txqueuelen 0  (Ethernet)
        RX packets 41  bytes 2581 (2.5 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 15  bytes 1178 (1.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
vethf34d9da: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::7cf9:b4ff:fefc:fc3c  prefixlen 64  scopeid 0x20<link>
        ether 7e:f9:b4:fc:fc:3c  txqueuelen 0  (Ethernet)
        RX packets 16  bytes 1173 (1.1 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 19  bytes 1458 (1.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
.....
$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242a0d8f7fb       no              vethf34d9da

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

我们再启动一个容器,我们看一下会发生什么

docker run -d --name myapp-2 ikubernetes/myapp:v1

宿主机上查看一下网络设备

brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242a0d8f7fb       no              veth90a25df
                                                        vethf34d9da

我们发现vethf34d9da的虚拟网卡“插”在docker0上

我们在myapp-1上ping一下myapp-2的ip,我们会发现是通的。

$ docker exec -it myapp-1 /bin/sh 
/ # ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.296 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.310 ms

解释一下为什么会通。

当我们myapp-1访问myapp-2的时候,这个目的IP地址会匹配到myapp-1容器里路由规则的第二条,可以看到,这条路由规则的网关是“*”,这就意味是直连规则,即:凡是匹配这个规则的IP包,应该经过eth 0网卡,通过二层网络直接发往目的主机。而要通过二层网络到达nginx-2容器,就需要有172.17.0.3这个IP对应的MAC地址,所以myapp-1容器的网络协议栈,就需要通过eth0网卡发送一个ARP广播,来通过IP地址查找对应的MAC地址。这个eth0网卡是一个Veth Pair,它的一段在myapp-1容器的Network Namespace里,而另一端则位于宿主机上,并“插”在docker0网桥上。

一旦一张虚拟网卡“插”在网桥上,它就变成该网桥的“从设备”,“从设备”会被“剥夺”调用网络协议栈处理数据包的资格,从而降级成为网桥上的一个端口,而这个端口的唯一作用,就是接收流入的数据包,然后这个数据包的“生杀大权”(例如转发、丢弃)全部交给对应的网桥。

所以,在收到这些ARP请求后,docker0网桥就会扮演二层交换机的角色,把ARP广播转发到其他被“插”在docker0上虚拟网卡上。这样,同样连接在docker0上的myapp-2容器的网络协议栈就会收到这个ARP请求,从而将172.17.0.3所对应的MAC地址回复给myapp-1容器。有了这个目的MAC地址,myapp-1容器的eth0网卡就可以将数据包发出去了。而根据Veth Pair设备原理,这个数据包会立即出现在宿主机vethf34d9da虚拟网卡上,不过,此时这个vethf34d9da虚拟网卡的网络协议栈被宿主机docker0剥夺,所以这个数据包将直接流入到docker0网桥里。

对于网桥上流入的数据包,就会通过宿主机的网络协议栈进行处理。需要注意的是,在宿主机上,Docker会为你设置如下路由规则:

route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         bogon           0.0.0.0         UG    100    0        0 ens160
172.16.138.0    0.0.0.0         255.255.255.0   U     100    0        0 ens160
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0

而这次流入的数据包的目的地址是172.17.0.3,所以,当他出现宿主机之后,就会按照上述172.17.0.0这条路由规则,在进过docker0网桥(FORWORD)转发出去。

docker0处理转发的过程,则继续扮演二层交换机的角色,此时,docker0根据数据包的目的MAC地址(myapp-2容器的MAC地址),在它的CAM表(既交换机通过MAC地址学习维护的端口和MAC地址对应表)里查到对应的端口为: vethf34d9da,然后将数据转发到这个端口。而这个端口是myapp-2“插”在docker0网桥上的另一张虚拟网卡,当然他也是Veth Pair设备,这样数据包就进入到myapp-2容器里Network Namespace里。这样myapp-2容器的网卡上出现了流入的数据包,就可以对请求进行处理了,处理完成将响应返回给myapp-1。

 上述流程示意图

技术分享图片

与此类推,当宿主机访问容器的时候,如下示意图:

技术分享图片

 

当一个容器访问另一个宿主机上的容器时,例如:ping 172.16.138.40,它发出的请求数据包,首先经过docker0网桥出现在宿主机上。然后根据宿主机的路由表里直连路由规则(172.16.138.0/24  ens160)对172.16.138.40的访问请求就会交给宿主机的eth0。数据包经过宿主机的eth0 网卡转发宿主机网络上,最终到达172.16.138.40对应的宿主机上,要实现这个首先两台宿主机的网络是连通的,如下图:

技术分享图片

所以说当遇到容器连不通“外网”的时候,应该先试试ping docker0的地址通不通,然后在查docker0 和 Veth Pair 设备相关的iptables规则是不是正常。

网络栈

极客时间版权所有: https://time.geekbang.org/column/article/64948网络

网络栈

极客时间版权所有: https://time.geekbang.org/column/article/64948

网络栈

极客时间版权所有: https://time.geekbang.org/column/article/64948

1在大多数情况下,我们都希望容器进程能使用自己 Network...

极客时间版权所有: https://time.geekbang.org/column/article/64948

以上是关于容器网络的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段14——Vue的axios网络请求封装

将Android片段移动到不同的容器无法更改片段的容器ID

如何在 Android 中的特定片段容器中显示片段

尝试将片段添加到我的片段容器 FrameLayout

尝试将片段添加到我的片段容器 FrameLayout

使用 BottomBar 和片段容器禁用 Android 片段重新加载