CNI(容器网络接口)是云原生计算基金会(CNCF)的一个项目,它定义了一个规范,同时提供了一个Go语言的库(Library),用于开发在Linux容器中配置网络接口的插件,CNI项目还内置提供了一系列受支持的插件。 CNI只关心容器创建时的网络分配,以及当容器被删除时已经分配网络资源的释放。 CNI作为容器网络的标准,使得各个容器管理平台可以通过相同的接口调用各种各样的网络插件来为容器配置网络。

CNI规范

当前CNI规范的版本是0.4.0,规范的地址是https://github.com/containernetworking/cni/blob/spec-v0.4.0/SPEC.md

CNI规范主要是为Linux容器提出了一个通用的基于插件的网络解决方案,这个解决方案被称作CNI即容器网络接口。

在CNI规范中对container容器和network网络的定义如下:

container容器: 可以认为是linux newtwork namespace的同义词,一个network namespace具体对应什么,与具体的容器运行时有关 network网络: 是指可以唯一寻址且可互相通信的实体。这些实体可以是一个单独容器(如上定义),一台机器,或者其他的网络设备(如一台路由器)。容器container可以加入一个或多个网络network,也可以从一个或多个network中移除。

CNI规范文档主要用来说明容器运行时(runtimes)和插件(plugins)之间的接口。CNI规范还提出了以下一般性建议:

  • 容器运行时必须在调用任何CNI插件之前为容器创建一个新的network namespace
  • 之后,容器运行时必须确定这个容器应该属于哪些网络,以及对于每个网络确定必须要执行哪些插件
  • 网络的配置是JSON格式的,可以方便地存储在文件中。网络配置包含一些必填字段(如name, type)以及一些插件的特定字段。网络配置允许字段在调用之间更改值。因此存在一个可选字段“args”包含变化的信息。
  • 容器运行时必须按顺序为每个网络执行相应的插件将容器添加到每个网络
  • 在容器生命周期结束后,容器运行时必须以相反的顺序执行插件,以断开容器与网络的连接
  • 容器运行时不能为同一个容器调用并行操作,但允许为不同的容器调用并行操作
  • CNI接口定义了ADD,DEL,VERSION,CHECK等接口。容器运行时必须对一个容器进行的ADD和DEL操作进行排序,这样ADD最终总是在对应的DEL之后,DEL可以跟随其他DEL,插件应该允许多个DEL(即插件的DEL应该是幂等的)
  • 容器必须由ContainerID唯一标识。存储状态的插件应该使用主键(network name, CNI_CONTAINERID, CNI_IFNAME)
  • 如果没有相应的DEL,容器运行时不能对同一个(network name, CNI_CONTAINERID, CNI_IFNAME)调用两次ADD,这意味着只有在每次添加都使用不同的接口名时,才能将给定的ContainerID多次添加到特定的网络中
  • CNI结构中的字段(如Network Configuration和CNI Plugin Result)除非特别标记为可选,都是必需的

CNI Library(CNI库)

CNI库用于开发CNI插件,项目的地址是https://github.com/containernetworking/cni。当前项目源码的包结构如下:

1
2
3
4
5
6
7
|-cni
  |- cnitool   - 可编译为一个命令行小工具cnitool,可用来执行CNI配置,在已经创建好的network namespace中添加、删除网络接口
  |- libcni    - CNI接口供容器运行时调用,转发调用具体的CNI插件。可以看出cnitool也是通过调用libcni实现其功能的
  |- pkg
    |- invoke
    |- skel    - 为CNI插件提供骨架代码,实现了参数的解析和校验
    ......

CNI库十分重要,k8s这类容器管理平台,containerd这类容器运行时都依赖这个库与CNI插件进行交互。flannel、calico等CNI插件的开发也都依赖这个库。

CNI项目内置插件

CNI项目还内置提供了一系列受支持的插件,即作为插件开发的参考实现,也可以直接使用。内置插件项目的地址是https://github.com/containernetworking/plugins 前面在第3节《使用CNI为Containerd容器添加网络能力》时曾整理过这些插件,这里再次罗列一下:

从功能的角度可以划分为以下三类:

  • 主插件: 用于创建网络设备
    • 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给容器网络的进出流量进行一系列限制的插件

参考