CNI规范(译)
本文翻译自 https://github.com/containernetworking/cni/blob/main/SPEC.md,为了更好的在中文的语境下理解原意,本译文可能在某些地方并未原封不动的翻译原文。
版本
当前规范版本为 1.0.0
注意,这个规范与 containernetworking/cni 中的代码是独立的。
下面是已经发布的规范版本:
tag | spec permalink | major changes |
---|---|---|
spec-v1.0.0 |
spec at v1.0.0 | Removed non-list configurations; removed version field of interfaces array |
spec-v0.4.0 |
spec at v0.4.0 | Introduce the CHECK command and passing prevResult on DEL |
spec-v0.3.1 |
spec at v0.3.1 | none (typo fix only) |
spec-v0.3.0 |
spec at v0.3.0 | rich result type, plugin chaining |
spec-v0.2.0 |
spec at v0.2.0 | VERSION command |
spec-v0.1.0 |
spec at v0.1.0 | initial version |
概览
本文档起草了容器网络的通用解决方案,即 CNI(Container Networking Interface)。
为了明确目的,我们定义了以下 3 个术语(译者注:为了表述更准确,直接贴上原文):
- container is a network isolation domain, though the actual isolation technology is not defined by the specification. This could be a network namespace or a virtual machine, for example.
- network refers to a group of endpoints that are uniquely addressable that can communicate amongst each other. This could be either an individual container (as specified above), a machine, or some other network device (e.g. a router). Containers can be conceptually added to or removed from one or more networks.
- runtime is the program responsible for executing CNI plugins.
- plugin is a program that applies a specified network configuration.
本文档主要目的是定义 runtimes 与 plugins 之间的接口。文档中的关键字 MUST、MUST NOT、REQUIRED、SHALL、SHALL NOT、SHOULD、SHOULD NOT、RECOMMENDED、MAY 和 OPTIONAL 的定义见 RFC 2119。
总结
CNI 规范定义了如下内容:
- 管理员定义网络配置的格式(译者注:通常是配置文件)
- runtime 请求 plugin 的 protocol 格式
- plugin 执行网络配置的流程
- plugin 为其它 plugin 提供的功能
- plugin 返回给 runtime 的类型
Section 1:Network configuration format
CNI 为管理员定义了网络配置的格式。网络配置的消费者有 runtime 与 plugin。在执行 plugin 时,runtime 会将配置转换后,再传给 plugin。
通常来说,网络配置是静态的。虽然 CNI 规范没有要求网络配置的存储方式,但它通常保存在磁盘中。
Configuration format
一份网络配置包含一个 JSON 对象,对象包含如下字段:
cniVersion
(string):格式遵循 Semantic Version 2.0,当前版本是 1.0.0name
(string):网络名。在同一主机上不同网络配置的网络名应该(SHOULD)保持唯一,必须以数字或者字母开头,后面可选的(OPTIONAL)跟着一个或者多个字母、数字、下划线(_)、点(.)、连字符(-)disableCheck
(boolean):如果disableCheck
为true
,runtime 则不会调用CHECK
检查网络配置列表。当合并网络配置时,错误是已知的,这时可以使用这个参数plugins
(list):CNI 插件和其配置列表
Plugin configuration objects
Plugin 配置对象中除了本文档定义的字段,还可以包含额外的字段。runtime 必须将这些额外的字段原封不动的传给 plugin(详情见 section 3)。
Required keys
type
(string):与 CNI plugin 二进制名匹配
Optional keys,used by the protocol
capabilities
(dictionary):详情见 section 3
Reserved keys,used by the protocol
这些字段由 runtime 在执行期间生成,所以不能在配置文件中使用。
runtimeConfig
args
- 任何以
cni.dev/
为前缀的字段
Optional keys,well-known
这些字段不能被 protocol 使用,但是对于 plugin 来说是有意义的。如果 plugin 使用这些字段,应该(SHOULD)遵循它们的语义。
ipMasq
(boolean):如果 plugin 支持,将会在主机上为这个网络配置 IP masqueradeipam
(dictionary)type
(string)
dns
():nameservers
domain
search
options
Other keys
这些字段只能被 plugin 识别。当 runtime 在使用网络配置时必须将这些字段保留下来。
Example configuration
{
"cniVersion": "1.0.0",
"name": "dbnet",
"plugins": [
{
"type": "bridge",
// plugin specific parameters
"bridge": "cni0",
"keyA": ["some more", "plugin specific", "configuration"],
"ipam": {
"type": "host-local",
// ipam specific
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1",
"routes": [
{"dst": "0.0.0.0/0"}
]
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
},
{
"type": "tuning",
"capabilities": {
"mac": true
},
"sysctl": {
"net.core.somaxconn": "500"
}
},
{
"type": "portmap",
"capabilities": {"portMappings": true}
}
]
}
Section 2:Execution Protocol
Overview
CNI protocol 是 runtime 与 plugin 二进制之间的通信协议。
CNI plugin 主要负责以某种方式配置容器的网络接口。主要分为两大类:
- Interface plugin:创建容器网络接口,并保证其连通性
- Chained plugin:调整已经创建的容器网络接口(也许会因此创建更多的接口)
Runtime 通过环境变量和配置(配置通过 sdtin 传递)传递参数给 plugin。Plugin 的执行结果(格式见 section 5)通过 stdout 或者 stderr 返回给 runtime。配置和执行结果都遵循 JSON 格式。
每次调用,runtime 传给 plugin 的参数通常是不同的,但其中配置是不变的(译者注:意思是每次调用只有环境变量会发生变化)。
Runtime 必须在它自己的网络隔离域中执行 plugin(通常是根网络命名空间 dom0)。
Parameters
Runtime 调用 plugin 使用的环境变量如下:
CNI_COMMAND
:分为ADD
、DEL
、CHECK
、VERSION
CNI_CONTAINERID
:由 runtime 生成,用来唯一标识一个容器。必须非空。必须以数字或者字母开头,后面可选的(OPTIONAL)跟着一个或者多个字母、数字、下划线(_)、点(.)、连字符(-)CNI_NETNS
:容器隔离域的引用。如果使用网络命令空间,那么CNI_NETNS
形如 /run/netns/[nsname]CNI_IFNAME
:容器内创建的网络接口名CNI_ARGS
:额外的参数,形如 FOO=BAR;ABC=123CNI_PATH
:用来寻找 CNI 二进制的路径列表,Linux 下以冒号分割
译者注:
CNI_COMMAND
与CNI_CONTAINERID
共同组成 plugin 操作网络的唯一标识。
Errors
如果执行成功,插件返回 0;如果执行失败,插件返回特定的 JSON 对象(详情见 section 5)。
CNI operations
CNI 操作通过 CNI_COMMAND
传给 plugin,有如下 4 种操作:
ADD
向容器中添加网络。当 plugin 接受到 ADD
命令后,会完成以下两件事之一:
- 在
CNI_NETNS
中创建一个名为CNI_IFNAME
的接口(译者注:新增) - 调整
CNI_NETNS
中CNI_IFNAME
接口的配置(译者注:修改)
如果容器中已经存在相同的接口名,plugin 必须返回错误。
input
输入分为 3 部分:
- JSON 格式的配置文件,通过 stdin 传给 plugin
- 必要的(REQUIRED)环境变量参数:
CNI_COMMAND
、CNI_CONTAINERID
、CNI_NETNS
、CNI_IFNAME
- 可选的(OPTIONAL)环境变量参数:
CNI_ARGS
、CNI_PATH
DEL
从容器中删除网络。当 plugin 接受到 DEL
命令后,会完成以下两件事之一:
- 在
CNI_NETNS
中删除一个名为CNI_IFNAME
的接口 - 回滚
ADD
中的变更
通常来说,在执行 DEL
的过程中,即使发生了某些错误(译者注:例如需要操作的容器网络命名空间被删除),也应该忽略。
input
输入分为 3 部分:
- JSON 格式的配置文件,通过 stdin 传给 plugin
- 必要的(REQUIRED)环境变量参数:
CNI_COMMAND
、CNI_CONTAINERID
、CNI_IFNAME
- 可选的(OPTIONAL)环境变量参数:
CNI_NETNS
、CNI_ARGS
、CNI_PATH
译者注:这里 CNI_NETNS 变成了可选参数
CHECK
Runtime 通过 CHECK
命令检查容器的网络是否符合预期。
对于 plugin 来说:
- TODO
对于 runtime 来说:
- TODO
input
输入分为 3 部分(对于同一个容器,所有参数除了 CNI_PATH
,必须与 ADD
保持一致 ):
- JSON 格式的配置文件,通过 stdin 传给 plugin
- 必要的(REQUIRED)环境变量参数:
CNI_COMMAND
、CNI_CONTAINERID
、CNI_NETNS
、CNI_IFNAME
- 可选的(OPTIONAL)环境变量参数:
CNI_ARGS
、CNI_PATH
VERSION
检查 plugin 支持的版本。
input
输入分为 3 部分:
- JSON 格式的配置文件(额外加上
cniVersion
字段),通过 stdin 传给 plugin - 必要的(REQUIRED)环境变量参数:
CNI_COMMAND
Section 3:Execution of Network Configurations
本节讨论 runtime 如何将配置转换成对 plugin 的各种 ADD
、DEL
、 VERSION
等操作。
将配置文件应用到容器上的操作称为 attachment,由 (CNI_CONTAINERID, CNI_IFNAME)
元组唯一标识。
Lifecycle & Ordering
- 在调用 plugin 前,runtime 必须为容器创建一个新的网络命名空间
- 对于同一个容器,runtime 不能并行执行 attachment;对于不同的容器,runtime 可以并行执行 attachment
- (上面说到,对于不同容器,runtime 可以并行执行 attachment)plugin 需要自己对共享资源(如 IPAM 数据库)加上互斥
- Runtime 必须确保
ADD
有对应的DEL
(即使ADD
失败,也需要执行DEL
),唯一的例外是 node 失联。 DEL
后可以接着执行多个DEL
- 一对
ADD
/DEL
应该使用同一份配置文件 - 多个 attachment 应该使用同一份配置文件
- Runtime 应该负责清理容器的网络命名空间
Attachment Parameters
尽管不同 attachment 应该使用同一份配置文件,但是以下参数在不同 attachment 间是变化的:
CNI_CONTAINERID
CNI_NETNS
CNI_IFNAME
CNI_ARGS
- Capability Arguments
另外,需要向 runtime 提供 plugin 的地址列表,这个列表还需要通过环境变量 CNI_PATH
传递给 plugin。
Adding an attachment
遍历配置文件中的 plugins 字段(plugins 字段的类型是 list),每次遍历依次完成如下工作:
- 查找名为 type 的二进制文件
- 生成请求 plugin 的请求参数(如果不是第一个插件,需要带上
prevResult
,即上一个插件的执行结果) - 使用 CNI_COMMAND=ADD 执行 plugin 二进制,并按照规范提供参数给 plugin
注:过程中遇到错误则返回
Deleting an attachment
译者注:与 Adding an attachment 中的流程基本相同,具体区别见原文。
Checking an attachment
Runtime 可以通过 plugin 检查 attachment 是否生效。检查时使用的参数与执行 ADD
时的参数保持一致。
译者注:与 Adding an attachment 中的流程基本相同,具体区别见原文。
Deriving execution configuration from plugin configuration
配置文件中的 plugins 列表需要转换成 plugin 能识别的格式(即 plugin configuration 转换成 execution configuration)。转换过程中,大部分内容不会变更,除了向每个 plugin 的 execution configuration 中插入:
cniVersion
name
prevReslut
(即上一个 plugin 的执行结果)runtimeConfig
Deriving runtimeConfig
Runtime 需要移除 capabilities
字段,并将其转换成 plugin 能识别的方式。
转换前:
{
"type": "myPlugin",
"capabilities": {
"portMappings": true
}
}
转换后:
{
"type": "myPlugin",
"runtimeConfig": {
"portMappings": [ { "hostPort": 8080, "containerPort": 80, "protocol": "tcp" } ]
}
...
}
Section 4:Plugin Delegation
TODO
Section 5:Result Types
Plugin 通常返回以下 3 种类型之一:
- Success
- Error
- Version
Success
Runtime 会提供给 plugin 上一个 plugin 执行的结果 prevResult
,然后将 prevResult
返回。这个过程中,plugin 可能会修改 prevResult
的值。如果 plugin 没有修改 prevResult
,那么说明 plugin 的执行结果为 success。
执行 ADD
成功后,返回如下 JSON 结构:
cniVersion
:与 plugin 接受到的保持一致interfaces
:本次 attachment 中创建的所有 interface(容器级 or 主机级)name
:名字mac
:mac 地址sandbox
:如果是容器级接口,通常是网络命名空间的文件路径(即CNI_NETNS
);如果是主机级,则为空
ips
:本次 attachment 分配的 ip 列表address
:CIDR 格式的地址gateway
:子网的默认网关interface
:interfaces
字段的索引
routes
:本次 attachment 创建的路由dst
:路由的目的地址gw
:路由的下一跳地址
dns
:nameservers
:domain
:search
:options
:
Error
如果 plugin 在执行过程遇到错误,它应该返回如下格式的错误信息:
{
"cniVersion": "1.0.0",
"code": 7,
"msg": "Invalid Configuration",
"details": "Network 192.168.0.0/31 too small to allocate from."
}
错误码 0-99 为保留错误码。
错误码 | 错误描述 |
---|---|
1 |
CNI 版本不兼容 |
2 |
配置文件中存在不支持的字段。错误信息中必须包含不支持的字段相关信息 |
3 |
容器不存在。如果返回这个错误,意味着 runtime 不用负责清理(例如,执行 DEL 动作) |
4 |
无效的参数(如 CNI_COMMAND, CNI_CONTAINERID 等) |
5 |
I/O 错误( 如无法从 stdin 中读取配置文件) |
6 |
反序列化错误(如将配置文件反序列化成结构体出错) |
7 |
配置文件无效 |
11 |
可以重试的错误 |
另外, stderr 可以输出非结构化的信息(如日志)
Version
执行 VERSION
成功后,返回如下 JSON 结构:
{
"cniVersion": "1.0.0",
"supportedVersions": [ "0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0" ]
}
Latest update: 2022/06/24