0%

cni_spec

CNI系列之规范学习

CNI是什么吗?

  • 全称是Container Network Interface
  • 也是CNCF管理的一个项目;
  • 该项目包含规范和实现库(golang版本);
  • CNI只关注于容器启动时网络的联接以及容器退出时网络资源的释放;

本文主要是学习CNI规范(其实就是翻译😀),golang版本的实现库,后续文章继续学习。

版本

当前CNI版本号为0.4.0,已发布的版本如下:

版本 主要变更
spec v0.4.0 引入CHECK命令;DEL命令需要传递prevResult
spec v0.3.1
spec v0.3.0 丰富result类型,支持链式插件
spec v0.2.0 支持VERSION命令
spec v0.1.0 初始版本

概述

本文旨在说明为Linux上的应用容器提供一种通用的基于插件的网络解决方案,即容器网络接口(Container network interface),简称CNI。源自rkt网络解决方案,rkt旨在适应多种网络设计思路。

为了更好的描述该规范,约定下面两个术语,本文后续使用networkcontainer描述:

  • 容器(container):可以认为等同于Linux网络命名空间。具体的承载单元,取决于特定的容器runtime实现。例如,rkt中每一个pod都是运行在一个独一无二的网络命名空间中;而docker中,每一个独立的Docker容器都存在一个独立的网络命名空间;
  • 网络(network):指一组实体,这些实体都是独一无二可寻址的而且可以相互通信的。单个实体可以是一个独立的容器container,一台机器或者其他网络设备(例如一个路由器)。逻辑上containers可以添加到一个或者多个网络network,也可以从中退出。

本文旨在规范runtimesplugins之间的接口。尽管有一些众所周知的字段,runtimes可能希望增加一些额外的信息给plugins。这些拓展不是规范的一部分,而是归属于约定

关键字”must”, “must not”, “required”, “shall”, “shall not”, “should”, “should not”, “recommended”, “may”和”optional”都是遵循规范RFC 2119。注意:本文对应于必须,必须不等等。

基本准则

  • 为容器执行任何插件之前,容器runtime必须创建一个新的网络命名空间;
  • runtime必须决定这个容器应该属于那些networks,而且对于每个network那些插件必须执行;
  • network配置是JSON格式,而且容易存储到文件。network配置包含强制性字段,如”name”和”type”;”type”对应于插件名。network配置允许字段在调用过程中变更值。为此,可选配置”args”,必须包含可变信息;
  • 容器runtime必须依次把容器添加到network中,通过执行相应的插件实现;
  • 容器生命周期结束之后,runtime必须以逆序(相对于加入network的操作顺序)执行插件,把容器从这些networks中退出;
  • 容器runtime必须不能对同一个容器并发执行network操作,但是允许对不同容器并发执行network操作;
  • 容器runtime必须保证对容器的ADDDEL操作是对应的,ADD之后总是对应一个DEL操作。DEL之后可能有其他的DELS,但是插件应该允许处理多个DEL操作,即插件的DEL应该是幂等的;
  • 容器必须通过独一无二的容器ID来标识。插件存储状态信息,应该使用(network name, CNI_CONTAINERID, CNI_IFNAME)作为主键;
  • 容器runtime必须不能对同一个(network name, container id, name of the interface inside the container)执行两次ADD操作(没有执行相应的DEL操作)。这意味着对同一个容器ID添加到特定的网络多次,只能是每次添加操作针对不同的接口名;
  • CNI结构(Network Configuration 和CNI Plugin Result)中的字段,除非标记为可选的,否则是必须的;

CNI插件

CNI插件必须以可执行文件的方式存在,由容器管理系统(rkt或者K8S)调用。

CNI插件负责把网络接口(例如veth对的一端)插入到容器网络命名空间,并且在host上面做必要的修改(例如把veth对的另外一端链接到bridge上面)。然后CNI插件需要给网络接口分配一个IP,并且通过调用适当的IPAM插件设置与IP地址管理部分一致的路由配置。

ADD操作参数

作用:把容器加入到network中。

支持的参数如下:

参数 作用
Container ID runtime分配的独一无二的明文标识符,必须以字母数字开头,后面可以跟一个或者多个字母数字、下划线_、点.或者中划线-字符的任意组合。
Network namespace path 表示加入的网络命名空间的路径,例如/proc/[pid]/ns/net或者bind-mount/link的路径。
Network configuration 描述容器将加入的network的JSON文档,下面会有章节详细描述。
Extra arguments 提供了一个替代的机制,允许对每个基础容器上的插件进行简单的配置。
Name of the interface inside the container 指定容器(网络命名空间)内创建的接口名称;该名称必须遵守Linux对接口名称的标准规范,必须不为空,必须不为”.”或者”..”,必须小于16个字符,而且不能包含”/“、”:”或者其他任何空白字符。

返回结果结构如下:

字段 描述
Interfaces list 取决于插件实现,可以包含sandbox(如容器或者hypervisor)中的接口名(如网卡名)或者主机上的接口名,每个接口的物理地址,以及这些接口所在的sandbox的详细信息。
IP configuration assigned to each interface 分配给sandbox或/和主机的接口的IPV4/IPV6的地址,网关和路由信息。
DNS information 包含DNS信息(nameservers,domain,search domains,options)的字典

DEL操作参数

作用:把容器从network中退出。

支持参数如下:

参数 作用
Container ID 同ADD
Network namespace path 同ADD
Network configuration 同ADD
Extra arguments 同ADD
Name of the interface inside the container 同ADD

功能规范要求:

  • 传递给DEL的参数,必须和对应的ADD的一样;
  • DEL操作应该释放container对已配置network持有的所有资源;
  • 如果已知对container的前一个ADD操作,那么runtime必须把前一个ADD返回的Result传递给当前JSON配置的插件(或者插件链的所有插件)的prevResult字段。runtime可能希望使用libcni提供的Result cache
  • 如果没有提供CNI_NETNS和/或prevResult,插件应该尽可能的释放资源(如释放IPAM分配的IP),并返回成功响应;
  • 如果runtime cache中缓存了给定容器的上一个ADD操作的Result,那么必须在DEL操作成功之后,删除该缓存的Result

即使某些资源丢失,插件一般也应该无错误的完成DEL操作。例如,即使容器网络命名空间不存在,IPAM插件也应该释放分配的IP,并且返回成功;除非这个网络命名空间对于IPAM管理器至关重要。虽然DHCP通常会给容器网络接口发送一个”释放”消息,但是由于DHCP租约存在一个生命周期,所以这个释放操作不会被认为是关键的,也不应该返回任何错误。

再例如,即使容器网络命名空间和/或容器的接口不存在了,bridge插件也应该把DEL操作委托给IPAM插件去清理自己的资源(如果有)。

CHECK操作参数

作用:检测容器的网络是否符合预期。

支持参数如下:

参数 作用
Container ID 同ADD
Network namespace path 同ADD
Network configuration 同ADD,必须包含prevResult字段,值为该容器的上一个ADD操作返回的Result
Extra arguments 同ADD
Name of the interface inside the container 同ADD

返回结果:

  • 无任何返回或者返回错误

功能规范要求:

  • 插件必须通过pervResult确定期望的网络接口和地址;

  • 插件必须允许插件链后续的插件修改网络资源,例如路由;

  • 如果CNI结果类型中包含的资源(网络接口、地址或者路由)是由插件创建的,并且在prevResult中列出,但丢失或者处于非法状态,那么插件应该返回错误;

  • 如果非CNI结果类型中的资源,丢失或者处于非法状态,如下面的资源,插件也应该返回错误:

    • 防火墙规则;
    • 流量控制;
    • IP预订;
    • 外部依赖,如链接需要的守护进程;
    • 其他。。。
  • 如果插件感知到容器处于一般无法到达的状态,应该返回错误;

  • 插件必须处理ADD之后立刻执行CHECK的调用,因此应该允许任何异步资源有合理的收敛延迟;

  • 插件应该在委托的所有插件(如IPAM)上面调用CHECK,而且返回错误给调用者;

  • runtime必须不能对未执行ADD或者执行过最后ADD之后的DEL的容器执行CHECK

  • 如果网络配置的disableCheck设置为true,那么runtime必须不能执行CHECK

  • runtime必须把前一个ADD返回的Result传递给当前JSON配置的插件(或者插件链的所有插件)的prevResult字段。runtime可能希望使用libcni提供的Result cache

  • 当一个插件返回错误,runtime可能选择停止对插件链后续插件执行CHECK

  • runtime可以在ADD之后立即执行CHECK,直到容器从network中删除;

  • runtime可能认为CHECK失败的容器,将永久处于错误状态;

VERSION操作参数

作用:报告版本信息;

参数:无;

结果:插件支持的CNI规范版本信息,详细结构如下

1
2
3
4
{
"cniVersion": "0.4.0", // the version of the CNI spec in use for this output
"supportedVersions": [ "0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0" ] // the list of CNI spec versions that this plugin supports
}

插件执行说明

runtime必须根据networktype字段指定的名字作为可执行文件的名字调用。runtime应该从一组预定义的目录(该规范没有指定目录列表,但库实现有:/opt/cni/bin)中搜索该可执行文件。一旦找到,runtime必须使用以下的环境变量传递参数:

  • CNI_COMMAND:表示期望执行的操作;ADDDELCHECK或者VERSION
  • CNI_CONTAINERID:容器ID;
  • CNI_NETNS:网络命名空间文件路径;
  • CNI_IFNAME:构建的接口名,如果插件无法使用该接口名,必须返回一个错误;
  • CNI_ARGS:调用时用户传入的额外参数,以’;’分割的字母数字键值对,例如:”FOO=BAR;ABC=123”
  • CNI_PATH:列出搜索CNI插件可执行文件的路径列表,路径由特定于OS的列表分割符分割,例如,Linux的’:’,Windows的’;’

JSON格式的网络配置信息,必须通过stdin流的方式传递给插件。这意味着它不需要绑定到磁盘的特定文件,而且可能包含调用过程中变化的信息。

CNI插件总结

runtime可以通过两种方式传递参数或者信息给插件:

  1. 网络配置信息,通过stdin流传递给插件;
  2. 环境变量;

Result

注意IPAM插件应该返回一个简洁的Result结构体,如[IP Allocation](#IP Allocation)章节所描述。

执行ADD操作,插件必须返回0表示成功,而且把下面的JSON结构输出到stdout

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
{
"cniVersion": "0.4.0",
"interfaces": [ (this key omitted by IPAM plugins)
{
"name": "<name>",
"mac": "<MAC address>", (required if L2 addresses are meaningful)
"sandbox": "<netns path or hypervisor identifier>" (required for container/hypervisor interfaces, empty/omitted for host interfaces)
}
],
"ips": [
{
"version": "<4-or-6>",
"address": "<ip-and-prefix-in-CIDR>",
"gateway": "<ip-address-of-the-gateway>", (optional)
"interface": <numeric index into 'interfaces' list>
},
...
],
"routes": [ (optional)
{
"dst": "<ip-and-prefix-in-cidr>",
"gw": "<ip-of-next-hop>" (optional)
},
...
],
"dns": { (optional)
"nameservers": <list-of-nameservers> (optional)
"domain": <name-of-local-domain> (optional)
"search": <list-of-additional-search-domains> (optional)
"options": <list-of-options> (optional)
}
}

ipsdns字段应该和IPAM创建返回的结果一样,除此之外插件应该合理的填充interface字段;由于IPAM插件对interfaces无感知,因此IPAM返回的输出中无interface字段。

cniVersion描述当前插件使用的CNI规范版本。插件可能支持多个CNI规范版本(通过VERSION操作获取),插件返回的cniVersion必须和网络配置中设置的cniVersion一致。如果插件不支持网络配置指定的cniVersion,插件应该返回一个错误码”1”(详情见[Error Code](#Error Codes))。

interfaces描述插件创建的网络接口。如果CNI_IFNAME环境变量存在,插件必须使用它指定的名字创建sandbox/hypervisor接口,如果不能创建则返回错误。

  • mac:字符串类型,值为接口的物理地址。如果L2地址对插件无意义,那么该字段是可选的;
  • sandbox:字符串类型,容器(基于命名空间的)环境应该返回该sandbox的网络命名空间的完整文件系统路径。hypervisor(基于虚拟机的)插件应该返回创建接口的虚拟sandbox的唯一ID。对于创建或者移动到sandbox(网络命名空间)或者hypervisor(VM)的接口,必须指定此项。

ips字段表示一组IP配置信息。详情见well-know ips

routes字段表示一组路由的配置信息。详情见well-know routes

dns字段表示常见DNS信息组成的字典。详情见well-know DNS。本规范未声明CNI消费者必须如何处理这些信息。常用的例子,生成一个/etc/resolv.conf文件并且注入容器的文件系统,或者在主机上运行一个DNS转发器。

错误

错误通过非0的返回值和如下的stdout的输出描述:

1
2
3
4
5
6
{
"cniVersion": "0.4.0",
"code": <numeric-error-code>,
"msg": <short-error-message>,
"details": <long-error-message> (optional)
}

cniVersion表示插件使用的CNI规范版本,错误码”0-99”为保留的常用的错误。”100+”的错误码插件可以自由使用。

此外,stderr可以用于输出未格式化的信息,例如日志。

Network Configuration

网络配置以JSON格式呈现。该配置可能存储在磁盘,也可能由容器runtime的其他组件生成。网络配置包含下面的通用字段:

  • cniVersion:字符串类型,当前配置符合的CNI规范版本;
  • name:字符串类型,网络名称。在主机(或者其他管理域)上的所有容器中应该是唯一的。必须以数字字符开头,后面可以跟一个或者多个字母数字、下划线_、点.或者中划线-字符的任意组合;
  • type:字符串类型,表示CNI插件可执行文件名;
  • args:字典类型(可选),容器runtime提供的额外参数。例如,可以通过设置args的一项,可以把一个标签字典传递给CNI插件;
  • ipMasq:布尔类型(可选),如果插件支持,会在主机上为该网络设置IP伪装。如果主机作为子网的网关,而子网无法路由到分配给容器的IP,那么配置ipMasq是必须的;
  • ipam:字典类型(可选),IPAM特定值的字典:
    • type:字符串类型,表示IPAM插件可执行文件名;
  • dns:字典类型(可选),DNS特定值的字典:
    • nameservers:字符串数组(可选),该网络能够识别的DNS域名服务器的优先顺序列表。数组每项包含一个IPV4或者IPV6的地址;
    • domain:字符串类型,用于短主机名查找的本地域;
    • search:字符串数组类型,为短主机名查找的优先顺序搜索域的列表。大部分解析都优先于domain执行。
    • options:字符串数组类型,传递给解析器的一组选项。查看CNI插件Result获取更多信息。

插件可以定义他们能接受的附加字段,但是如果传递未知的字段可能会报错。而args字段可以传递任意的数据,如果未知插件会忽略该字段。

示例

bridge的网络配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"cniVersion": "0.4.0",
"name": "dbnet",
"type": "bridge",
// type (plugin) specific
"bridge": "cni0",
"ipam": {
"type": "host-local",
// ipam specific
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
}

ovs网络配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"cniVersion": "0.4.0",
"name": "pci",
"type": "ovs",
// type (plugin) specific
"bridge": "ovs0",
"vxlanID": 42,
"ipam": {
"type": "dhcp",
"routes": [ { "dst": "10.3.0.0/16" }, { "dst": "10.4.0.0/16" } ]
},
// args may be ignored by plugins
"args": {
"labels" : {
"appVersion" : "1.0"
}
}
}

macvlan网络配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"cniVersion": "0.4.0",
"name": "wan",
"type": "macvlan",
// ipam specific
"ipam": {
"type": "dhcp",
"routes": [ { "dst": "10.0.0.0/8", "gw": "10.0.0.1" } ]
},
"dns": {
"nameservers": [ "10.0.0.1" ]
}
}

Network Configuration Lists

网络配置列表提供了对单容器按特定顺序执行多个插件的机制,并且把每个插件的执行结果传递给它的下一个插件。结果和网络配置一样,以JSON为格式,包含一些通用的配置项和一组[网络配置](#Network Configuration)的列表。该配置可能存储在磁盘,也可能由容器runtime的其他组件生成。下面是通用配置的描述:

  • cniVersion:字符串类型,当前配置符合的CNI规范版本;
  • name:字符串类型,网络名称。在主机(或者其他管理域)上的所有容器中应该是唯一的。必须以数字字符开头,后面可以跟一个或者多个字母数字、下划线_、点.或者中划线-字符的任意组合;
  • disableCheck:字符串类型,值为true或者false。如果disableChecktrueruntime必须不对该网络配置列表进行CHECK操作。这可以允许管理员阻止CHECK组合插件已知的假错误。
  • plugins:网络配置列表类型,一组CNI网络配置字典(详细信息见上节);

执行插件链时,runtime必须替换plugins中的每个网络配置的namecniVersion为网络配置列表本身的namecniVersion。这样可以保证执行所有插件时CNI版本一致,防止由于版本不一致导致的错误。

如果插件的网络配置文件的capabilities字段声明支持某种能力,那么runtime可能把基于该能力的配置通过插件的配置JSON的runtimeConfig字段传递过去。

对于ADD操作,runtime必须在执行第一个插件之后,把上一次插件执行结果(Result),添加到JSON配置的prevResult字段。而且插件应该把它收到的prevResult的内容输出到stdout,从而允许后续的插件(和runtime)接收到这些内容,除非是当前插件需要修改或者屏蔽到之前的结果。插件允许修改或者屏蔽prevResult的全部或者部分内容。然而,插件支持的CNI规范版本包好了prevResult字段,那么插件必须处理prevResult字段,可以是修改、透传或者屏蔽。对prevResult字段一无所知是不符合规范的。

对于CHECKDEL操作,runtime必须(除非不可用,DEL可以忽略)为每一个插件的JSON配置添加prevResult字段,其值必须是前一个ADD操作的JSON格式的结果。

runtime必须以相同的环境执行列表中的每一个插件。

DEL操作的顺序必须是和列表顺序相反。

网络配置列表错误处理

当插件列表执行操作(ADD或者DEL)出现错误时,runtime必须停止该列表的执行。

如果ADD执行失败,当runtime决定处理该错误时,它应该对列表中的所有插件执行DEL操作(相对于ADD的逆序),即使执行ADD操作时某些插件没有被调用。

网络配置列表示例

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
{
"cniVersion": "0.4.0",
"name": "dbnet",
"plugins": [
{
"type": "bridge",
// type (plugin) specific
"bridge": "cni0",
// args may be ignored by plugins
"args": {
"labels" : {
"appVersion" : "1.0"
}
},
"ipam": {
"type": "host-local",
// ipam specific
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
},
{
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
}
]
}

网络配置列表运行示例

ADD操作

以上节的网络配置列表的JSON为例,runtime运行ADD操作,会执行如下步骤。注意:runtimeplugins的所有网络配置添加cniVersionname字段,以保证所有插件运行时版本和名字一致。

  1. 首先,使用如下JSON配置,调用bridge插件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    {
    "cniVersion": "0.4.0",
    "name": "dbnet",
    "type": "bridge",
    "bridge": "cni0",
    "args": {
    "labels" : {
    "appVersion" : "1.0"
    }
    },
    "ipam": {
    "type": "host-local",
    // ipam specific
    "subnet": "10.1.0.0/16",
    "gateway": "10.1.0.1"
    },
    "dns": {
    "nameservers": [ "10.1.0.1" ]
    }
    }
  2. 然后,以下面的JSON配置,调用tuning插件,prevResult字段包含了bridge插件的返回JSON

    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
    {
    "cniVersion": "0.4.0",
    "name": "dbnet",
    "type": "tuning",
    "sysctl": {
    "net.core.somaxconn": "500"
    },
    "prevResult": {
    "ips": [
    {
    "version": "4",
    "address": "10.0.0.5/32",
    "interface": 2
    }
    ],
    "interfaces": [
    {
    "name": "cni0",
    "mac": "00:11:22:33:44:55"
    },
    {
    "name": "veth3243",
    "mac": "55:44:33:22:11:11"
    },
    {
    "name": "eth0",
    "mac": "99:88:77:66:55:44",
    "sandbox": "/var/run/netns/blue"
    }
    ],
    "dns": {
    "nameservers": [ "10.1.0.1" ]
    }
    }
    }

CHECK操作

以相同的网络配置列表的JSON为例,runtime运行CHECK操作,会执行如下步骤。

  1. 首先,以如下JSON,调用bridge插件,prevResult字段包含了ADD插件的返回JSON

    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
    {
    "cniVersion": "0.4.0",
    "name": "dbnet",
    "type": "bridge",
    "bridge": "cni0",
    "args": {
    "labels" : {
    "appVersion" : "1.0"
    }
    },
    "ipam": {
    "type": "host-local",
    // ipam specific
    "subnet": "10.1.0.0/16",
    "gateway": "10.1.0.1"
    },
    "dns": {
    "nameservers": [ "10.1.0.1" ]
    },
    "prevResult": {
    "ips": [
    {
    "version": "4",
    "address": "10.0.0.5/32",
    "interface": 2
    }
    ],
    "interfaces": [
    {
    "name": "cni0",
    "mac": "00:11:22:33:44:55"
    },
    {
    "name": "veth3243",
    "mac": "55:44:33:22:11:11"
    },
    {
    "name": "eth0",
    "mac": "99:88:77:66:55:44",
    "sandbox": "/var/run/netns/blue"
    }
    ],
    "dns": {
    "nameservers": [ "10.1.0.1" ]
    }
    }
    }
  2. 然后,以如下JSON,调用tuning插件,prevResult字段包含了ADD插件的返回JSON

    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
    {
    "cniVersion": "0.4.0",
    "name": "dbnet",
    "type": "tuning",
    "sysctl": {
    "net.core.somaxconn": "500"
    },
    "prevResult": {
    "ips": [
    {
    "version": "4",
    "address": "10.0.0.5/32",
    "interface": 2
    }
    ],
    "interfaces": [
    {
    "name": "cni0",
    "mac": "00:11:22:33:44:55"
    },
    {
    "name": "veth3243",
    "mac": "55:44:33:22:11:11"
    },
    {
    "name": "eth0",
    "mac": "99:88:77:66:55:44",
    "sandbox": "/var/run/netns/blue"
    }
    ],
    "dns": {
    "nameservers": [ "10.1.0.1" ]
    }
    }
    }

DEL操作

以相同的网络配置列表的JSON为例,runtime运行DEL操作,会执行如下步骤。注意:插件执行的顺序与ADDCHECK是相反的。

  1. 首先,以如下JSON配置,执行tuning插件,prevResult字段包含了ADD插件的返回JSON

    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
    {
    "cniVersion": "0.4.0",
    "name": "dbnet",
    "type": "tuning",
    "sysctl": {
    "net.core.somaxconn": "500"
    },
    "prevResult": {
    "ips": [
    {
    "version": "4",
    "address": "10.0.0.5/32",
    "interface": 2
    }
    ],
    "interfaces": [
    {
    "name": "cni0",
    "mac": "00:11:22:33:44:55"
    },
    {
    "name": "veth3243",
    "mac": "55:44:33:22:11:11"
    },
    {
    "name": "eth0",
    "mac": "99:88:77:66:55:44",
    "sandbox": "/var/run/netns/blue"
    }
    ],
    "dns": {
    "nameservers": [ "10.1.0.1" ]
    }
    }
    }
  2. 然后,以如下JSON配置,执行bridge插件,prevResult字段包含了ADD插件的返回JSON

    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
    {
    "cniVersion": "0.4.0",
    "name": "dbnet",
    "type": "bridge",
    "bridge": "cni0",
    "args": {
    "labels" : {
    "appVersion" : "1.0"
    }
    },
    "ipam": {
    "type": "host-local",
    // ipam specific
    "subnet": "10.1.0.0/16",
    "gateway": "10.1.0.1"
    },
    "dns": {
    "nameservers": [ "10.1.0.1" ]
    },
    "prevResult": {
    "ips": [
    {
    "version": "4",
    "address": "10.0.0.5/32",
    "interface": 2
    }
    ],
    "interfaces": [
    {
    "name": "cni0",
    "mac": "00:11:22:33:44:55"
    },
    {
    "name": "veth3243",
    "mac": "55:44:33:22:11:11"
    },
    {
    "name": "eth0",
    "mac": "99:88:77:66:55:44",
    "sandbox": "/var/run/netns/blue"
    }
    ],
    "dns": {
    "nameservers": [ "10.1.0.1" ]
    }
    }
    }

IP Allocation

CNI插件被期望去分配IP地址,并且安装接口相关的任何必要的路由配置。这个CNI插件带来巨大的灵活性,同时也带来巨大的负担。有些CNI插件可能需要通过相同的代码支持多种用户期望的IP管理策略(例如,dhcp或host-local)。

为了减少这种负担,并且使得IP管理策略和CNI插件类型正交,本规范定了第二种插件类型(IP Address Management Plugin = IPAM插件)。而CNI插件需要在执行过程中,选择合适的时间调用IPAM插件。IPAM插件必须决定接口的IP/子网、网关和路由并且把这些信息返回给调用它的”主“插件。

IPAM插件可以通过协议(如dhcp)、本地文件系统存储,网络配置文件的ipam字段甚至以上方式的集合获取信息。

IPAM Interface

和CNI插件类似,IPAM插件作为可执行文件被运行。可执行文件通过搜索预定义的一批路径搜索获得,通过CNI_PATH传递给CNI插件。IPAM插件必须接受CNI插件完全一样的环境变量。IPAM插件也是通过stdin接受网络配置的。

执行成功,则返回0并且输出如下JSON信息到stdout(以ADD操作为例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"cniVersion": "0.4.0",
"ips": [
{
"version": "<4-or-6>",
"address": "<ip-and-prefix-in-CIDR>",
"gateway": "<ip-address-of-the-gateway>" (optional)
},
...
],
"routes": [ (optional)
{
"dst": "<ip-and-prefix-in-cidr>",
"gw": "<ip-of-next-hop>" (optional)
},
...
]
"dns": { (optional)
"nameservers": <list-of-nameservers> (optional)
"domain": <name-of-local-domain> (optional)
"search": <list-of-search-domains> (optional)
"options": <list-of-options> (optional)
}
}

注意:不像常规的CNI插件,IPAM插件应该返回一个简洁的Result结构,不包含interfaces字段,由于IPAM插件应该对interfaces无感知,而是由父插件配置,除非IPAM由特殊依赖(例如,dhcp IPAM插件)。

cniVersion描述当前插件使用的CNI规范版本。插件可能支持多个CNI规范版本(通过VERSION操作获取),插件返回的cniVersion必须和网络配置中设置的cniVersion一致。如果插件不支持网络配置指定的cniVersion,插件应该返回一个错误码”1”(详情见[Error Code](#Error Codes))。

ips字段表示一组IP配置信息。详情见well-know ips

routes字段表示一组路由的配置信息。详情见well-know routes

dns字段表示常见DNS信息组成的字典。详情见well-know DNS

错误和日志和CNI插件使用相同的方式。详情见Result错误
IPAM插件的例子:

  • host-local:选择指定区间内一个未使用的IP(相同主机上其他容器);
  • dhcp:使用DHCP协议区获取和维护租约。DHCP请求将通过创建的容器接口发送,因此,相关的网络必须支持广播。

注意

  • 默认路由可能设置为”0.0.0.0/0”。由于其他网络可能已经设置默认路由,因此CNI插件应该准备跳过默认路由设置。
  • 期望以0为指标添加路由

Well-know Structures

本章主要是说明众所周知的结构体,例如,IP、路由以及DNS,还有错误码。

ips

1
2
3
4
5
6
7
8
9
"ips": [
{
"version": "<4-or-6>",
"address": "<ip-and-prefix-in-CIDR>",
"gateway": "<ip-address-of-the-gateway>", (optional)
"interface": <numeric index into 'interfaces' list> (not required for IPAM plugins)
},
...
]

ips字段的值是由插件决定的一组IP配置信息列表。每一项表示一个网络接口的IP配置信息字典。多个网络接口或者一个接口的多个网络配置,可能通过独立ips的项表示。应该提供插件所知道的所有属性,即使不是严格要求的。

  • version:string类型,值范围为”4”或者”6”,对应当前项的IP地址的版本。所有的IP地址和网关对于给定版本必须是合法的。
  • address:string类型,CIDR格式的IP地址,例如”192.168.1.3/24”。
  • gateway:string类型,为子网设置默认的网关,如果该网关存在。CNI插件不会为该网关设置任何路由配置,路由配置是通过routes项单独设置。使用该配置的一个例子,CNI插件bridge为Linux桥配置一个IP,使得该桥成为网关。
  • interface:uint类型,CNI插件Resultinterface链表的下标,指明该IP配置将应用到那个接口上。IPAM插件不应该返回该字段,因为它们没有关于网络接口的信息。

routes

1
2
3
4
5
6
7
"routes": [
{
"dst": "<ip-and-prefix-in-cidr>",
"gw": "<ip-of-next-hop>" (optional)
},
...
]

每个条目包含如下字段。条目中,所有的IP地址必须是相同的IP版本,4或者6。

  • dst:string类型,CIDR格式的目的地址子网;
  • gw:string类型,网关的IP,如果省略,则假定为默认网关(由CNI插件决定);

每个条目必须和CNI_IFNAME指定的 sandbox的接口关联。

DNS

1
2
3
4
5
6
"dns": {
"nameservers": <list-of-nameservers> (optional)
"domain": <name-of-local-domain> (optional)
"search": <list-of-additional-search-domains> (optional)
"options": <list-of-options> (optional)
}

dns字段包含了常用DNS信息的字典。

  • nameservers:字符串数组类型,该网络能够识别的DNS域名服务器的优先顺序列表。数组每项包含一个IPV4或者IPV6的地址;
  • domain:字符串类型,用于短主机名查找的本地域;
  • search:字符串数组类型,为短主机名查找的优先顺序搜索域的列表。大部分解析都优先于domain执行。
  • options:字符串数组类型,传递给解析器的一组选项。查看CNI插件Result获取更多信息。

Error Codes

1-99为规范保留的通用错误码,必须不能被其他地方使用。

Error Code Error Description
-1 Incompatible CNI version
-2 Unsupported field in network configuration. The error message must contain the key and value of the unsupported field.
-3 Container unknown or does not exist. This error implies the runtime does not need to perform any container network cleanup (for example, calling the DEL action on the container).
-4 Invalid necessary environment variables, like CNI_COMMAND, CNI_CONTAINERID, etc. The error message must contain the names of invalid variables.
-5 I/O failure. For example, failed to read network config bytes from stdin.
-6 Failed to decode content. For example, failed to unmarshal network config from bytes or failed to decode version info from string.
-7 Invalid network config. If some validations on network configs do not pass, this error will be raised.
-11 Try again later. If the plugin detects some transient condition that should clear up, it can use this code to notify the runtime it should re-try the operation later.