重学容器19: 从源码理解CNI接口
2021-07-01
前面学习了CNI为容器网络创建定义的配置格式及基本的操作执行流程。 本节将从源码加深对CNI接口的理解。
CNI项目https://github.com/containernetworking/cni
的libcni
下包含CNI接口,供容器运行时调用,转发调用具体的CNI插件。
libcni.CNI
就是具体的go语言的接口,源码如下:
1type CNI interface {
2 AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
3 CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
4 DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
5 GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
6 GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
7
8 AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
9 CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
10 DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
11 GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
12 GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
13
14 ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
15 ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
16}
ValidateNetworkList
和ValidateNetwork
主要用来对CNI网络配置列表和CNI网络配置进行校验。AddNetworkList
、CheckNetworkList
、DelNetworkList
通过网络配置列表调用一组插件将容器添加到网络、检查网络、从网络中删除AddNetwork
、CheckNetwork
、DelNetwork
通过网络配置列表调用一个插件将容器添加到网络、检查网络、从网络中删除
libcni.CNI
接口的各个Add, Del, Check方法中都有一个RuntimeConf
参数,包含了一次CNI调用中,除了网络配置之外的参数信息。通常由容器运行时根据上下文来创建,下面是RuntimeConf这个struct的源码:
1// A RuntimeConf holds the arguments to one invocation of a CNI plugin
2// excepting the network configuration, with the nested exception that
3// the `runtimeConfig` from the network configuration is included
4// here.
5type RuntimeConf struct {
6 ContainerID string
7 NetNS string
8 IfName string
9 Args [][2]string
10 // A dictionary of capability-specific data passed by the runtime
11 // to plugins as top-level keys in the 'runtimeConfig' dictionary
12 // of the plugin's stdin data. libcni will ensure that only keys
13 // in this map which match the capabilities of the plugin are passed
14 // to the plugin
15 CapabilityArgs map[string]interface{}
16
17 // DEPRECATED. Will be removed in a future release.
18 CacheDir string
19}
NetworkConfig
和NetworkConfigList
两个struct对应CNI规范中的网络配置和网络配置列表,每个NetworkConfig对应一个插件Plugin,源码如下:
1type NetworkConfig struct {
2 Network *types.NetConf
3 Bytes []byte
4}
5
6type NetworkConfigList struct {
7 Name string
8 CNIVersion string
9 DisableCheck bool
10 Plugins []*NetworkConfig
11 Bytes []byte
12}
每个NetworkConfig对应一个Plugin实现了libcni.CNI
接口,所以容器运行时调用接口时会转发到具体的插件(二进制执行)。
这个可以从容器运行时containerd的源码中找到踪迹,containerd的源码依赖它自己封装的https://github.com/containerd/go-cni
,
在这个项目中https://github.com/containerd/go-cni/blob/main/namespace.go
中直接对AddNetworkList
、CheckNetworkList
的代码调用。
每个具体的CNI插件开发通过依赖pkg/skel
, pkg/invoke
等包提供的骨架代码实现其功能。每个CNI插件必须实现为一个可执行文件,由容器管理系统或容器运行时调用。插件负责将一个网络接口插入到容器网络名称空间(例如,veth对的一端),并在主机上做任何必要的配置(例如,将veth的另一端连接到网桥bridge上)。然后,CNI插件还应该为网络接口分配IP,并通过调用合适的IPAM插件来设置与IP地址管理部分一致的路由规则。
通过前面的分析可知,容器运行时、容器管理系统、cnitool内部实现上会依赖libcni
,直接调用libcni的接口执行add, check, delete操作,libcni会把操作转发到具体的插件二进制文件执行上。