安装工具:

apt install bridge-utils net-tools

容器与宿主机的通信

创建一个新的网络命名空间,这里模拟容器内部的网络

$ ip netns add net1  # net1为该网络空间(容器内部网络空间)的名称

查看命名空间的iptable, 路由表,设备

$ ip netns exec net1 route  # 查看容器内部路由表
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

$ ip netns exec net1 iptables -L # 查看容器内部iptable
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination  

$ ip netns exec net1 ip link list # 查看容器内部设备
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    # 目前只有本地回环设备, 并且状态为DOWN(未启动)


创建一对veth,并将veth的一头添加到net1中

Veth Pair 设备的特点是:它被创建出来后,总是以两张虚拟网卡(Veth Peer)的形式成对出现的。并且,从其中一个“网卡”发出的数据包,可以直接出现在与它对应的另一张“网卡”上,哪怕这两个“网卡”在不同的 Network Namespace 里。这就使得 Veth Pair 常常被用作连接不同 Network Namespace 的“网线”。

$ ip link add veth1 type veth peer name veth1_p
$ ip link set veth1 netns net1 # veth1设备被移动到容器中

查看主机上当前设备,只能看到veth1_p这个设备了

$ ip link list | grep veth
1284: veth1_p@if1285: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000

查看net1容器网络命名空间,veth1在这个空间里面

$ ip netns exec net1 ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
1285: veth1@if1284: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 8a:b3:bb:2a:3a:92 brd ff:ff:ff:ff:ff:ff link-netnsid 0

为这对veth配置ip,并启动起来

$ ip addr add 10.68.0.100/24 dev veth1_p # 配置容器中的veth设备ip
$ ip netns exec net1 ip addr add 10.68.0.101/24 dev veth1 
$ ip netns exec net1 ip link set dev veth1 up # 启动net1中的设备
$ ip link set dev veth1_p up # 启动宿主机中的设备

分别查看当前启动的网络设备

$ ifconfig | grep veth1 # 宿主机
veth1_p: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

$ ip netns exec net1 ifconfig # 容器
veth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.101  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 fe80::88b3:bbff:fe2a:3a92  prefixlen 64  scopeid 0x20<link>
        ether 8a:b3:bb:2a:3a:92  txqueuelen 1000  (Ethernet)
        RX packets 57  bytes 7088 (6.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 10  bytes 796 (796.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

测试通信

$ ip netns exec net1 ping 10.68.0.100 -I veth1 # 从容器内部ping宿主机
PING 10.68.0.100 (10.68.0.100) 来自 10.68.0.101 veth1 56(84) 字节的数据。
64 字节,来自 10.68.0.100: icmp_seq=1 ttl=64 时间=0.040 毫秒
64 字节,来自 10.68.0.100: icmp_seq=2 ttl=64 时间=0.096 毫秒
64 字节,来自 10.68.0.100: icmp_seq=3 ttl=64 时间=0.057 毫秒
64 字节,来自 10.68.0.100: icmp_seq=4 ttl=64 时间=0.070 毫秒
64 字节,来自 10.68.0.100: icmp_seq=5 ttl=64 时间=0.070 毫秒
64 字节,来自 10.68.0.100: icmp_seq=6 ttl=64 时间=0.070 毫秒
^C
--- 10.68.0.100 ping 统计 ---
已发送 6 个包, 已接收 6 个包, 0% packet loss, time 5077ms
rtt min/avg/max/mdev = 0.040/0.067/0.096/0.016 ms

宿主机与宿主机上容器成功

宿主机上多个容器的通信

20240228203631

创建网桥

网桥(Bridge)。是一个工作在数据链路层(Data Link)的设备,主要功能是根据 MAC 地址学习来将数据包转发到网桥的不同端口(Port)上。

$ brctl addbr mybridge

设置网桥ip

$ ip addr add 10.68.0.1/24 dev mybridge

启动网桥

$ ip link set dev mybridge up

查看网桥

$ ip addr show mybridge
35: mybridge: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 7e:06:1f:5a:53:f5 brd ff:ff:ff:ff:ff:ff
    inet 10.68.0.1/24 scope global mybridge
       valid_lft forever preferred_lft forever

将设备插入网桥

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

$ ip link set veth1_p master mybridge

查看是否插入成功

$ brctl show
bridge name	bridge id		STP enabled	interfaces
mybridge		8000.7e061f5a53f5	no		veth1_p

从veth对的另一端ping网桥

$ ip netns exec net1 ping 10.68.0.1 -I veth1
PING 10.68.0.1 (10.68.0.1) 来自 10.68.0.101 veth1 56(84) 字节的数据。
64 字节,来自 10.68.0.1: icmp_seq=1 ttl=64 时间=0.051 毫秒
64 字节,来自 10.68.0.1: icmp_seq=2 ttl=64 时间=0.063 毫秒
64 字节,来自 10.68.0.1: icmp_seq=3 ttl=64 时间=0.094 毫秒
^C
--- 10.68.0.1 ping 统计 ---
已发送 3 个包, 已接收 3 个包, 0% packet loss, time 2036ms
rtt min/avg/max/mdev = 0.051/0.069/0.094/0.018 ms

查看net1空间中的路由规则

$ ip netns exec net1 ip route
10.68.0.0/24 dev veth1 proto kernel scope link src 10.68.0.101 

设置一条默认路由:

$ ip netns exec net1 route add default gw 10.68.0.1 dev veth1

当这条默认路由被设置,所有的网络包默认被发往网桥。此时如果有另一个容器网络命名空间,例如net2,其veth设备一端插在网桥上,对应的另一端在net2中,就可以实现从net1到net2的通信。现在为止,已经可以实现在一台宿主机上的两个容器的相互通信。

容器与外部通信

20240228203742

$ brctl addif mybridge eth0

配置路由和转发

sysctl net.ipv4.ip_forward=1

现在,应该可以从net1中的veth1向外界发送消息:

$ ip netns exec net1 ping 8.8.8.8 -I veth1
PING 8.8.8.8 (8.8.8.8) from 10.68.0.101 veth1: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=2 ttl=62 time=39.5 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=62 time=40.5 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=62 time=39.3 ms
64 bytes from 8.8.8.8: icmp_seq=5 ttl=62 time=49.9 ms
64 bytes from 8.8.8.8: icmp_seq=6 ttl=62 time=40.3 ms
64 bytes from 8.8.8.8: icmp_seq=7 ttl=62 time=39.1 ms
64 bytes from 8.8.8.8: icmp_seq=8 ttl=62 time=39.1 ms
^C
--- 8.8.8.8 ping statistics ---
8 packets transmitted, 7 received, 12.5% packet loss, time 7015ms
rtt min/avg/max/mdev = 39.062/41.081/49.859/3.620 ms

现在已经可以实现容器与外部网络环境的相互通信,但是,如何实现不同宿主机上的容器之间互相通信呢?请看下篇。

Refercences

Build your own bridgeHow to Configure Network Bridge on Linux? - zenarmor.comhttps://zhuanlan.zhihu.com/p/65566171https://time.geekbang.org/column/article/64948