容器怎么去完成通信的

2023年 12月 22日 67.7k 0

容器作为一个被隔离的应用程序,它是怎么去完成自己的对外通信的呢?

之前提过Namespace的核心作用就是修改进程的视图,在Linux 提供的 Namespace技术中有一类 Network Namespace就是用来让容器隔离在自己的“网络栈”,

那么容器是如何完成对外通信的,下图就是容器网络示意图

容器通过自己的eth0网络接口经由docker0网桥,再经过宿主机网卡向外通信,那么容器eth0如何通过eth0去接入docker0网桥,主要借助的技术就是Veth Pair虚拟设备, Veth Pair的特点:它在创建时总是成对出现的,一个设备“发出”的数据包可以直接出现在另外一个设备上,而且这两个设备支持跨Namespace去部署

我们直接启动一个容器并直接进入其中查看它的网络情况

[root@bogon ~]# docker run -it busybox /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:736 (736.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)

/ # ip route
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0 scope link  src 172.17.0.2 

在busybox这个容器中,有一个网卡eth0以及一个loopback回环接口,以及两条路由,都是通过eth0向外发送数据流量

继续查看宿主机的网络状态

[root@bogon ~]# 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:dff:fe80:34ef  prefixlen 64  scopeid 0x20<link>
        ether 02:42:0d:80:34:ef  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 9  bytes 966 (966.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.130  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::20c:29ff:feb2:9ff1  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:b2:9f:f1  txqueuelen 1000  (Ethernet)
        RX packets 12229  bytes 1030066 (1005.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 9209  bytes 981682 (958.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
...
vethb84c6bf: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::7c8c:8aff:fefb:b096  prefixlen 64  scopeid 0x20<link>
        ether 7e:8c:8a:fb:b0:96  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 15  bytes 1226 (1.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

发现在容器启动之后出现了vethb84c6bf,这个虚拟网卡busybox容器来docker0进行交互的,继续查看vethb84c6bf可以发现它是被插入到了docker0这个网桥上了

[root@bogon ~]# ip link show vethb84c6bf
9: vethb84c6bf@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether 7e:8c:8a:fb:b0:96 brd ff:ff:ff:ff:ff:ff link-netnsid 0

经过上述的查看能明确了容器对外访问的流程了,比如容器1内执行了ping 8.8.8.8这个命令,首先容器本身路由匹配到了默认路由发往了eth0网卡,但是这个网卡是宿主机vethb84c6bf网卡的一部分,那么容器内的eth0接收到数据的那一刻,vethb84c6bf立马收到数据报文,但是由于其插入了docker0网桥,本身只能将数据转发给docker0网桥,网桥查看报文请求的ip并没有对应的mac,那么路由规则,发往ens33网卡,向外通信。

Veth Pair实验演示

我们通过给运行中的容器增加一张网卡来演示veth Pair设备的在容器中的作用

//创建一个veth pair 包含两张虚拟网卡 veth-test-in veth-test-out
[root@bogon ~]# ip link add veth-test-in type veth peer name veth-test-out

//进入容器的networkns并将veth-test-in网卡插入这个network ns
//3216是容器的PID,
[root@bogon ~]# sudo nsenter -t 3216 -n
//将网卡插入这个空间内,并启动
[root@bogon ~]# ip link set veth veth-test-in netns 3216
[root@bogon ~]# ip lin set veth-test-in up

//exit退出空间之后再宿主机启动veth-test-out 并放入网桥中
[root@bogon ~]# ip link set veth-test-out up
[root@bogon ~]# ip link set veth-test-out master docker0

执行完之后可以发现在docker0网桥上已经有了veth-test-out,现在来验证一下veth pair的通信

//进入ns中
[root@bogon ~]# sudo nsenter -t 3216 -n
//关闭之前docker自带的eth0并删除默认路由
[root@bogon ~]# ip link set eth0 down
[root@bogon ~]# ip route delete default

//给自己的veth添加地址以及默认路由
[root@bogon ~]# ip addr add 172.17.0.100/16 dev veth-test-in
[root@bogon ~]# ip route add default via 172.17.0.1 dev veth-test-in

[root@bogon ~]# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=38.8 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=38.4 ms
^C
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1004ms
rtt min/avg/max/mdev = 38.435/38.593/38.751/0.158 ms

发现通过我们自己使用linux生成的veth pair网卡也可以使得容器正常访问

上述的实验只为了演示容器的底层通信的原理,容器向外部公网访问,单宿主机上多容器间的访问,

但是很多应用场景会碰到跨主机容器的通信问题,这个就要涉及到Overlay的通讯,如Flannel,这个场景的分析就比较繁琐了。

相关文章

KubeSphere 部署向量数据库 Milvus 实战指南
探索 Kubernetes 持久化存储之 Longhorn 初窥门径
征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
那些年在 Terraform 上吃到的糖和踩过的坑
无需 Kubernetes 测试 Kubernetes 网络实现
Kubernetes v1.31 中的移除和主要变更

发布评论