Kubernetes CNI 实现原理

Posted by caodailiang on October 4, 2023

CNI是什么

Container Nerwork Interface,由一组用于配置 Linux 容器的网络接口的规范和库组成,同时还包含了一些插件。

CNI插件可以分为下面三类(可见官方库https://github.com/containernetworking/plugins):

Main插件:用来创建具体的网络设备的二进制文件,包括:

  • bridge:在宿主机上创建网桥然后通过veth pair的方式连接到容器
  • host-device:移动宿主上已经存在的设备到容器中
  • ipvlan:和macvlan相似,也是通过一个主机接口虚拟出多个虚拟网络接口,不同的是ipvlan虚拟出来的是共享MAC地址,IP地址不同
  • loopback:lo设备(将回环接口设置成up)
  • macvlan:虚拟出多个macvtap,每个macvtap都有不同的mac地址
  • ptp:Veth Pair设备
  • vlan:分配vlan设备

IPAM插件:负责分配IP地址,包括:

  • host-local:使用提前分配好的IP地址段来分配,这是当前最为常用的IPAM插件
  • dhcp:宿主机上运行的守护进程,代表容器发出DHCP请求
  • static:用于为容器分配静态的IP地址,主要是调试使用

Meta插件:由CNI社区维护的内部插件,包括:

  • bandwidth:使用 Token Bucket Filter (TBF)来进行限流的二进制文件
  • firewall:通过iptables或者firewalled添加规则控制容器的进出流量
  • portmap:通过iptables配置端口映射的二进制文件,将端口从主机的地址空间映射到容器
  • tuning:通过sysctl调整网络设备参数的二进制文件

CNI的思想就是在 kubelet 启动infra容器后,就可以直接调用CNI插件为这个infra容器的 Network Namespace 配置符合预期的网络栈。

Kubernetes中如何使用CNI

K8S通过CNI配置文件来决定使用何种CNI插件,基本使用方法:

  1. 读取节点CNI配置文件:/etc/cni/net.d/xxnet.conf
  2. 安装CNI配置文件中对应的二进制,目录:/opt/cni/bin
  3. 在这个节点上创建Pod后,根据CNI配置文件执行CNI二进制来配置容器网络

kubernetes-cni

例如 KinD 中的CNI配置文件:

$ cat /etc/cni/net.d/10-kindnet.conflist
{
    "cniVersion": "0.3.1",
    "name": "kindnet",
    "plugins": [
        {
            "type": "ptp",
            "ipMasq": false,
            "ipam": {
                "type": "host-local",
                "dataDir": "/run/cni-ipam-state",
                "routes": [
                    { "dst": "0.0.0.0/0" }
                ],
                "ranges": [
                    [ { "subnet": "10.244.3.0/24" } ]
                ]
            },
            "mtu": 1500
        },
        {
            "type": "portmap",
            "capabilities": {
                "portMappings": true
            }
        }
    ]
}

这个网络的名字叫 kindnet ,根据前文CNI插件分类可知 type: ptp 即表示是该CNI插件是创建 Veth Pair 类型的网络设备。ipam 下的 type: host-local 表示是从提前分配好给本地的IP地址段来分配,具体可分配的IP段在 ranges 中指定,也就是 subnet: 10.244.3.0/24,IP分配数据保存在 dataDir 指定的 /run/cni-ipam-state 目录中。

$ tree /run/cni-ipam-state/
/run/cni-ipam-state/
└── kindnet
    ├── 10.244.3.2
    ├── 10.244.3.3
    ├── 10.244.3.4
    ├── last_reserved_ip.0
    └── lock

如何选择CNI

通常来说,CNI插件可以分成3种:Overlay路由Underlay kubernetes-cni-network

环境限制

  • 虚拟机环境
  • 物理机环境
  • 公有云环境

功能需求

  • 安全需求
  • 是否需要集群外的资源与集群内的资源互联互通
  • K8S的服务发现与负载均衡的能力

性能需求

  • Pod的创建速度
  • Pod的网络性能

如何开发CNI插件

CNI插件的实现通常包含两个部分:

  1. 一个二进制的CNI插件去配置Pod网卡和IP地址,相当于给Pod插上网线
  2. 一个Daemon进程去管理Pod之间的网络打通,相当于给Pod连上网络

给Pod插上网线

  1. 分配IP地址,即IPAM
  2. 创建节点网桥,若已存在则跳过
  3. 创建网卡,一般是veth-pair,一端放入Pod作为eth0,另一端连接到网桥
  4. 配置Pod的IP和路由
    1. 将分配的IP地址配置到网卡
    2. 在网卡上配置集群网段的路由
    3. 在宿主机上配置到Pod的IP段的路由

给Pod连上网络

一般是由CNI Daemon进程去做网络打通的事情,通常是这样的步骤:

  1. Daemon进程监听kube-apiserver,获取Pod的IP地址与Node信息
  2. 配置网络进程打通
    1. 创建到集群所有节点的通道,如:Overlay隧道、阿里云的VPC路由表、BGP路由等
    2. 将Pod的IP地址与通道进行关联,具体实现如:Linux路由、fdb转发表、ovs流表等

参考文档