containerd自身并不具备为容器提供网络的能力。需要结合CNI为容器配置网络。

CNI简介

CNI (Container Network Interface)是CNCF的一个开源项目,其包含一些用于配置linux容器网络接口的规范、库,以及一些支持插件。CNI只关心容器创建时的网络分配,以及当容器被删除时已经分配网络资源的释放。 CNI作为容器网络的标准,使得各个容器管理平台可以通过相同的接口调用各种各样的网络插件来为容器配置网络。
我们在安装cri-containerd-cni的时候,已经安装好了cni插件以及相关工具,可以在/opt/cni/bin目录下查看相关可执行文件。这些执行文件从功能角度可以分为如下三类:

  • 主插件: 用于创建网络设备
    • bridge: 创建一个网桥设备,并添加宿主机和容器到该网桥
    • ipvlan: 为容器添加ipvlan网络接口
    • loopback: 设置lo网络接口的状态为up
    • macvlan: 创建一个新的MAC地址,并将所有流量转发到容器
    • ptp: 创建Veth对
    • vlan: 分配一个vlan设备
    • host-device: 将已存在的设备移入容器内
  • IPAM插件: 用于IP地址的分配
    • dhcp: 在宿主机上运行dhcp守护程序,代表容器发出dhcp请求
    • host-local: 维护一个分配ip的本地数据库
    • static: 为容器分配一个静态IPv4/IPv6地址,主要用于调试
  • Meta插件: 其他插件,非单独使用插件
    • flannel: flannel网络方案的CNI插件,根据flannel的配置文件创建网络接口
    • tuning: 调整现有网络接口的sysctl参数
    • portmap: 一个基于iptables的portmapping插件。将端口从主机的地址空间映射到容器
    • bandwidth: 允许使用TBF进行限流的插件
    • sbr: 一个为网络接口配置基于源路由的插件
    • firewall: 过iptables给容器网络的进出流量进行一系列限制的插件

containerd网络配置

默认情况下,当安装好cni插件和工具后,使用nerdctl启动了一个容器之后,宿主机上会出现一个nerdctl0网桥:

[root@ecs-350c-0001 ~]# ip addr show nerdctl0 
3: nerdctl0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 96:97:3c:08:11:cf brd ff:ff:ff:ff:ff:ff
    inet 10.4.0.1/24 brd 10.4.0.255 scope global nerdctl0
       valid_lft forever preferred_lft forever
    inet6 fe80::9497:3cff:fe08:11cf/64 scope link 
       valid_lft forever preferred_lft forever

此时创建出来的容器将获得与网桥同网段的ip,并将网关指向网桥,连接示意图如下:

除了默认的bridge网桥模式,containerd还支持host、none以及自定义网络,可以通过如下方式查看containerd支持的容器网络:

[root@ecs-350c-0001 ~]# nerdctl  network ls 
NETWORK ID    NAME              FILE
0             bridge            
              containerd-net    /etc/cni/net.d/10-containerd-net.conflist
              host              
              none

bridge网络

bridge: 这是默认的网络模式

  • 在bridge网络模式中,只要宿主机可以上网,则所有容器都可以上网
  • 如果外部网络想要访问容器,需要为容器配置端口映射
    bridge是containerd的默认网络模式,可以通过如下指令查看bridge的详细配置信息:
[root@ecs-350c-0001 ~]# nerdctl network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "0",
        "IPAM": {
            "Config": [
                {
                    "Subnet": "10.4.0.0/24",
                    "Gateway": "10.4.0.1"
                }
            ]
        },
        "Labels": {}
    }
]

创建使用bridge网络容器的示例:

nerdctl run -d --name web1 --net bridge nginx
基于bridge网络的容器访问外部网络

默认情况下,基于bridge网络容器即可访问外部网络,这是因为默认情况下,bridge使用了iptables的snat转发来实现容器对外部的访问。

外部网络访问基于bridge网络的容器

如果想让外界可以访问到基于bridge网络创建的容器提供的服务,需要为容器配置端口映射:

nerdctl run -d -p 80:80 --name web nginx:1.21

这种端口映射基于iptables的dnat实现

查看容器的端口情况:

nerdctl port web

none网络

故名思议,none网络就是什么都没有的网络。使用none网络的容器除了lo,没有其他任何网卡,完全隔离。用于既不需要访问外部服务,也不允许外部服务访问自己的应用场景。

  • 查看none网络信息:
nerdctl  network inspect none

创建使用none网络容器的示例:

nerdctl run -d --name web_none --net none nginx

none网络应用场景:病毒分析,病毒分析是不需要网络

host网络

host: 在host模式下,不为容器分配IP,而是直接使用宿主机ip

  • host模式将会使容器获得最佳网络性
  • 同一个镜像启动的多个容器,只有第一个能正常启动
  • 使用host网络的主机,与宿主机共享网络地址,可以获得最好的数据转发性能。
  • 缺点是,同一个宿主机上的多个容器共享同一个ip地址,如果多容器使用相同的端口,需要自行解决端口冲突问题。

同样的,可以通过如下方式查看host网络信息:

nerdctl network inspect host

创建一个使用host网络容器的示例:

# 可以看到该容器没有自己的IP地址,因为它直接使用宿主机IP地址
nerdctl run -d --name web_host -net host nginx

host网络应用场景:做负载均衡时可将负载均衡容器设置为host网络

自定义网络

我们可以通过cni的插件和工具轻松的为containerd自定义网络。

自定义bridge网络

我们在执行nerdctl network ls时,可以看到有一个自定义的网络名为containerd-net,其配置文件路径为/etc/cni/net.d/10-containerd-mynet.conflist内容如下:

{
  "cniVersion": "0.4.0",
  "name": "containerd-net",
  "plugins": [
    {
      "type": "bridge",
      "bridge": "cni0",
      "isGateway": true,
      "ipMasq": true,
      "promiscMode": true,
      "ipam": {
        "type": "host-local",
        "ranges": [
          [{
            "subnet": "10.88.0.0/16"
          }],
          [{
            "subnet": "2001:4860:4860::/64"
          }]
        ],
        "routes": [
          { "dst": "0.0.0.0/0" },
          { "dst": "::/0" }
        ]
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true}
    }
  ]
}

这是一个自定义的bridge网络, 我们可以通过如下指令创建一个容器使用该网络:

nerdctl run -d --name web2 --net containerd-net nginx

自定义flannel网络

从上面的例子中,我们知道,对于containerd的自定义网络。只要安装对应的网络插件,然后定义cni配置文件即可。而对于像flannel这种可以配置跨主机网络互联的网络插件,cni只为我们提供了配置网络的工具,而并没有安装flannel,需要我们自行安装。

部署etcd

flannel网络依赖etcd, 所以需要先安装etcd:

nerdctl run -d --net host --name etcd -v /var/lib/etcd:/var/lib/etcd registry.cn-zhangjiakou.aliyuncs.com/breezey/etcd:v3.5.0 \
etcd --name etcd \
--data-dir /var/lib/etcd \
--listen-client-urls http://192.168.0.180:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.0.180:2379,http://127.0.0.1:2379 \
--listen-peer-urls http://localhost:2380,http://192.168.0.180:2380 \
--enable-v2  # flannel目前仍然只支持etcd v2,所以需要将v2 api开启
安装flannel

往etcd当中写入flannel的网段:

# 安装etcd客户端
yum install -y etcd 
# 使用etcd v2 api向etcd中添加flannel的网段
export ETCDCTL_API=2;etcdctl --endpoints http://127.0.0.1:2379 set /coreos.com/network/config \
'{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.1.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "vxlan"}}'

安装flannel:

nerdctl run -d --privileged -v /run/flannel:/run/flannel --net host --name flannel registry.cn-zhangjiakou.aliyuncs.com/breezey/flannel:v0.14.0 \
-etcd-prefix /coreos.com/network -iface eth0 -etcd-endpoints http://192.168.0.180:2379 -ip-masq

为flannel配置cni:

# vim /etc/cni/net.d/10-flannel.conflist
{
  "name": "flannel",
  "cniVersion": "0.3.1",
  "plugins": [
    {
      "type": "flannel",
      "delegate": {
        "isDefaultGateway": true
      }
    },
    {
      "type": "portmap",
      "capabilities": {
        "portMappings": true
      }
    }
  ]
}

关于flannel的cni配置,可参考: flannel-cni/flannel.conflist.default at master · coreos/flannel-cni (github.com)

此时查看containerd网络:

[root@ecs-350c-0001 net.d]# nerdctl network ls 
NETWORK ID    NAME              FILE
0             bridge            
              containerd-net    /etc/cni/net.d/10-containerd-net.conflist
              flannel           /etc/cni/net.d/10-flannel.conflist
              host              
              none

由于containerd-net网络在创建时会使用cni0,而flannel网络在创建时也会使用cni0,会出现冲突,所以这里先把containerd-net网络干掉:

# 清掉所有容器
nerdctl ps -aq |xargs nerdctl rm 
# 停掉cni0接口
ifconfig cni0 down 
# 删除cni0接口
yum install -y bridge-utils
brctl delbr cni0
# 移除containerd-net配置文件
mv /etc/cni/net.d/10-containerd-net.conflist /tmp/

此时创建一个使用flannel网络的容器即可:

[root@ecs-350c-0001 net.d]# nerdctl run -d --net flannel --name flannel busybox:1.28  sleep 3600 
07a5035f7836b5331c849912df71861fd22ad8c3bf257be81a8c9f36132d5034

再次查看cni0的宿主机网络:

# ip a 
...
39: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default 
    link/ether 76:16:d1:84:98:60 brd ff:ff:ff:ff:ff:ff
    inet 10.0.7.0/32 brd 10.0.7.0 scope global flannel.1
       valid_lft forever preferred_lft forever
    inet6 fe80::7416:d1ff:fe84:9860/64 scope link 
       valid_lft forever preferred_lft forever
43: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
    link/ether 66:d4:88:0c:eb:7d brd ff:ff:ff:ff:ff:ff
    inet 10.0.7.1/24 brd 10.0.7.255 scope global cni0
       valid_lft forever preferred_lft forever
    inet6 fe80::64d4:88ff:fe0c:eb7d/64 scope link 
       valid_lft forever preferred_lft forever
...

查看busybox的网络:

[root@ecs-350c-0001 net.d]# nerdctl exec -it busybox ip a 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
3: eth0@if44: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
    link/ether ea:2e:f7:96:b8:19 brd ff:ff:ff:ff:ff:ff
    inet 10.0.7.3/24 brd 10.0.7.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::e82e:f7ff:fe96:b819/64 scope link 
       valid_lft forever preferred_lft forever

至此,flannel网络配置完成。将其他宿主机的网络也接入该flannel网络,则可实现跨宿主机的容器网络互联。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐