CNI 通用设计范式 电脑版发表于:2022/11/29 17:06  ># CNI 通用设计范式 [TOC] ## CNI简介 tn2>CNI是由CoreOS公司提出的另一种容器网络规范,定义了容器运行环境与网络插件之间简单接口规范,通过一个JSON定义CNI插件提供的输入和输出参数。一个容器可以通过绑定多个网络插件加入多个网络中。 ## CNI模型概念 tn2>对容器网络的设置和操作都通过插件的方式进行实现,CNI插件包括的类型:CNI Plugin 和 IPAM ( IP Adress Management)Plugin。CNI Plugin 负责为容器配置网络资源,IPAM Plugin 负责对容器的IP地址进行分配和管理。IPAM Plugin 作为CNI Plugin的一部分,与CNI Plugin 一起工作。 ## 容器运行时与CNI插件的关系和工作机制 tn2>将容器添加到网络或者删除某个网络是由容器运行时和CNI插件完成的,容器运行时与CNI插件之间的关系和工作机制通常遵循下面的原则:<br/> 1.容器运行时必须在调用任意插件前为容器创建一个新的网络命名空间。 2.容器运行时必须确定此容器所归属的网络(一个或多个),以及每个网络必须执行哪个插件。 3.网络配置为JSON格式,便于在文件中存储,网络配置包括必填字段,例如`name`和`type`,以及插件(类型)特有的字段。网络配置允许在调用时更改字段的值。为此,必须在可选字段args中包含需要变更的信息。 4.容器运行时必须按照先后顺序为每个网络运行插件将容器添加到每个网络中。 5.容器生命周期结束后,容器运行时必须以反向顺序(相对于添加容器执行顺序)执行插件,以使容器和网络断开连接。 6.容器运行时一定不能为同一个容器的调用执行并行操作,但可以为多个不同的容器调用执行并行的操作。 7.容器运行时必须对容器的ADD和DEL操作设置顺序,以使得ADD操作最终跟随相应的DEL操作。DEL操作后面可能会有其他DEL操作,但插件应自由处理多个DEL操作。 8.容器必须以ContainerID进行唯一标识。存储状态的插件应使用联合主键(network name、CNI_CONTAINERID、CNI_IFNAME)进行存储。 9.容器运行时不得为同一个实例(由联合主键network name、CNI_CONTAINERID、CNI_IFNAME)调用两次ADD操作(无相应的DEL操作)。对同一个容器(ContainerID),仅在每次ADD操作(无相应的DEL操作)。对同一个容器,仅在每次ADD操作都使用不同的网络接口名称时,才可以多次添加到特定的网络中。 10.除非明确标记为可选配置,CNI结构中的字段(例如Network Configuration和CNI Plugin Result)都是必填字段。 tn>简单讲: 一个配置文件,配置文件描述插件的版本、名称、描述等基本信息。 一个可执行的文件,可执行文件就是CNI插件本身会在容器需要建立网络和需要销毁容器时被调用。 读取6个环境变量,获得需要执行的操作、目标网络Namespace、容器的网卡必要信息。 接收1个命令行参数,同样用于获得需要执行的操作、目标网络Namespace、容器的网卡必要信息。 实现2个操作(ADD/DEL) ## Kubernetes使用CNI流程 tn2>Kubernetes 调用CRI创建pause容器,生成对应的network namespace; 调用网络driver(因为配置的是CNI,所以会调用CNI的相关代码); CNI driver根据配置调用具体的CNI插件 CNI插件给pause容器配置正确的网络,Pod中的其他容器都是用pause容器的网络栈。  ## CNI网络配置详解 tn2>CNI 网络配置以JSON格式进行描述。这个配置可以以文件的形式保存在磁盘上,或者由容器运行时自动生成。 我们以ipvlan做为一个例子,配置的路径在`/etc/cni/net.d`目录下,可以看到各种Conf。 ```json { "name": "mynet", "type": "ipvlan", "master": "eth0", "ipam": { "type": "host-local", "subnet": "10.1.2.0/24" } } ``` | 参数 | 描述 | | ------------ | ------------ | | `name` | 网络名称,应在一个节点node或一个管理域内唯一 | | `type` | CNI Plugin可执行文件的名称。 | | `master` | 要从属的主机接口的名称。默认为默认路由接口。 | | `ipam` | IP地址管理的相关配置。 | | `ipam.type` | IPAM可执行的相关配置。 | | `ipam.subnet` | 要分配的网段。 | | `cniVersion` | CNI版本号。 | | `ipMasq` | 是否设置IP Masquerade(需插件支持),适用于主机可作为网关的环境中。 | | `args` | 其他参数,可选。 | | `dns` | DNS服务的相关配置。 | | `dns.nameservers` | 域名服务器列表,可以使用IPv4或IPv6地址。 | | `dns.domain` | 本地域名,用于短主机名查询。 | | `dns.search` | 按优先级排序的域名搜索后缀列表。 | | `dns.options` | 传递给域名解析器的选项列表。 | tn2>关于ipam.type执行的网络配置应用,一般在`/opt/cni/bin`目录下面。  tn2>那么问题来了,加入有新的cni插件怎么放到这个目录下面呢? 其实是通过DaemonSet在每个节点上运行一个CNI相关的容器,通过volume卷挂载本地的`/etc/cni/net.d`目录,然后将插件放到该目录下面。基本上所有的cni插件都是这样做的。 IPAM的工具同样在该目录下面。 (Flannel举例)  ## IPAM tn2>我们知道IPAM主要是用于地址分配的,目前有三个管理插件来支持该功能: 1.dhcp 插件 2.host-local IP 地址管理插件 3.静态IP地址管理插件 >### dhcp 插件 tn2>关于DHCP的插件并不常用。因为dhcp需要在每个节点中运行一个dhcp server所以如果在所有的节点上都装一个这就太消耗性能了。 这里不过多叙述,感兴趣的可以查看官网:https://www.cni.dev/plugins/current/ipam/dhcp/ >### host-local 插件 tn2>主机本地IPAM插件从一组地址范围中分配ip地址。它将状态本地存储在主机文件系统上,从而确保单个主机上IP地址的唯一性。 分配器可以分配多个范围,并支持多个(不相交)子网的集合。分配策略是在每个范围集中进行松散的循环。 简单来讲每个主机可设置不一样的段。(举例:`10.244.0.0/16`,node1就是`10.244.1.0/24`,node2就是`10.244.2.0/24`...以此类推,可避免IP地址冲突)。 https://www.cni.dev/plugins/current/ipam/host-local/ >### 静态IP地址管理 tn2>静态IPAM是一个非常简单的IPAM插件,它将IPv4和IPv6地址静态分配给容器。 这对于调试目的以及在不同vlan/vxlan中为容器分配相同IP地址的情况下非常有用。 (但是一般不太关注Pod的IP,因为我们一般调用都是调用Service。) 缺点:所有的IP地址需要手动填写到配置清单中,还得指定相对应的路由。 https://www.cni.dev/plugins/current/ipam/static/ ## k8s 网络模型 >### Overylay网络 tn2>简单来讲有两台主机是在同一个网段中分别是`10.211.55.11`和`10.211.55.12`,现在里面的Pod1和Pod2分别是`192.168.1.1/24`和`192.168.1.2/24`,如果Pod1 没有overylay的网络支持根本与Pod2不相通。因为这是一个内部IP地址,如果使用overylay的网络把它封装成外部的地址,那么主机很乐意帮忙转发。 Overylay网络具体的实现就有:ipip、vxlan....。  >### 另一种方法 tn2>既然内部网络`10.211.xx.xx`外面的不知道我们可以让外面知道就好了,只不过需要一次路由的学习。那么Calico就提供了BGP、Flannel提供host-gw模式。把一个节点当作一个路由器来做,pod就跳一个节点然后转发包。