从本节开始学习容器网络,根据前面已经学习过的内容,单机模式下使用容器的最佳组合是nerdctl+containerd+buildkit,这套组合已经可以在本地开发、测试和单机容器部署方面替代docker了。关于容器网络的学习,就先从nerdctl启动容器的创建的brdige网络开始吧。

nerdctl0网桥

当使用nerdctl启动了一个容器之后,宿主机上会出现一个nerdctl0网桥。 下面的命令使用nerdctl启动一个redis容器:

1
nerdctl run -d --name redis redis:alpine3.13

在宿主机上查看nerdctl0网桥:

1
2
3
4
5
6
7
8
9
ifconfig nerdctl0
nerdctl0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.4.0.1  netmask 255.255.255.0  broadcast 10.4.0.255
        inet6 fe80::ed:32ff:fe87:b6cb  prefixlen 64  scopeid 0x20<link>
        ether 02:ed:32:87:b6:cb  txqueuelen 1000  (Ethernet)
        RX packets 16  bytes 992 (992.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5  bytes 446 (446.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

网桥(bridge)是一种网络设备,负责网络桥接(network bridging)。网桥将网络的多个网段在数据链路层(OSI模型第2层)连接起来(即桥接)。 网桥可以有多个端口,每个端口与一个网段相连,最简单的网桥有两个端口。从上面nerdctl0inet 10.4.0.1 netmask 255.255.255.0可以看出nerdctl0的子网是10.4.0.0/24,nerdctl创建的容器都会从该子网选用一个未被占用的IP,并连接到nerdctl0网桥上。

在Linux系统中可以使用brctl命令查看网桥信息,CentOS使用yum install -y bridge-utils安装这个命令。 前面已经启动了一个redis容器,下面再继续启动2个redis容器并在宿主机上使用brctl查看一下此时的网桥信息:

1
2
3
4
5
6
7
8
nerdctl run -d --name redis2 redis:alpine3.13
nerdctl run -d --name redis3 redis:alpine3.13

brctl show
bridge name	bridge id		      STP enabled	  interfaces
nerdctl0		8000.02ed3287b6cb	no		  veth075d39e6
							          veth386f6026
							          vethe13542b2

可以看到nerdctl0网桥上面连接了3个veth设备,veth总是成对出现的,一端连接网桥,另一端连接容器内部的eth0网卡,如下图所示:

nerdctl-bridge-network.png

路由表和ip转发

使用route查看一下宿主机的路由信息:

1
2
3
4
5
route -n
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.100.1   0.0.0.0         UG    0      0        0 eth0
10.4.0.0        0.0.0.0         255.255.255.0   U     0      0        0 nerdctl0
192.168.100.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0

可以看到10.4.0.0/24使用的nerdctl0。这里还有一点需要注意的是我们在部署containerd的时候需要把内核参数net.ipv4.ip_forward设置为1。这个参数用于指定系统当前是否支持路由转发功能,0表示禁止进行IP转发功能,1表示允许IP转发功能。所谓转发是指当机器有多块网卡时,其中一块收到数据包,该网卡根据路由表及数据包的目的ip地址将数据包发往本机另一块网卡,出于安全考虑,Linux系统默认是禁止IP转发的。

nerdctl bridge网络的CNI配置

还要了解一点,containerd本身不提供容器网络实现,而是交给CNI和CNI插件实现,前面我们手动配置过containerd启动容器并与cni插件集成。 当用nerdctl启动容器时,nerdctl为我们自动配置好了。可以使用nerdctl network ls查看网络:

1
2
3
4
5
6
nerdctl network ls
NETWORK ID    NAME              FILE
0             bridge
              containerd-net    /etc/cni/net.d/10-containerd-net.conflist
              host
              none

注意上面的输出中brdigehost是nerdctl为我们准备的,而containerd-net是我们在部署containerd和cni时手动添加的配置。 当使用nerdctl启动容器时默认使用的bridge网络。可以使用nerdctl network inspect bridge命令查看一下bridge网络的CNI配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
nerdctl network inspect bridge
[
    {
        "CNI": {
            "cniVersion": "0.4.0",
            "name": "bridge",
            "nerdctlID": 0,
            "plugins": [
                {
                    "type": "bridge",
                    "bridge": "nerdctl0",
                    "isGateway": true,
                    "ipMasq": true,
                    "hairpinMode": true,
                    "ipam": {
                        "type": "host-local",
                        "routes": [
                            {
                                "dst": "0.0.0.0/0"
                            }
                        ],
                        "ranges": [
                            [
                                {
                                    "subnet": "10.4.0.0/24",
                                    "gateway": "10.4.0.1"
                                }
                            ]
                        ]
                    }
                },
                {
                    "type": "portmap",
                    "capabilities": {
                        "portMappings": true
                    }
                },
                {
                    "type": "firewall"
                },
                {
                    "type": "tuning"
                }
            ]
        },
        "NerdctlID": 0
    }
]

总结

最后总结一下nerdctl启动容器时,当网络选择为brdige(默认就是)时,网络的初始化过程。 首先会创建一个veth pair,名称为vethxxx,一端连接到nerdctl0网桥用于连接宿主机的network namespace,另一端连接到容器的eth0网卡用于连接容器的network namespace,并从nerdctl0网桥的子网中选用一个未使用的IP分配个eth0,设置好容器网络路由和网络。

参考