前面学习了CNI为容器网络创建定义的配置格式及基本的操作执行流程。 本节将从源码加深对CNI接口的理解。
CNI项目https://github.com/containernetworking/cni的libcni下包含CNI接口,供容器运行时调用,转发调用具体的CNI插件。
libcni.CNI就是具体的go语言的接口,源码如下:
type CNI interface {
AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
}ValidateNetworkList和ValidateNetwork主要用来对CNI网络配置列表和CNI网络配置进行校验。AddNetworkList、CheckNetworkList、DelNetworkList通过网络配置列表调用一组插件将容器添加到网络、检查网络、从网络中删除AddNetwork、CheckNetwork、DelNetwork通过网络配置列表调用一个插件将容器添加到网络、检查网络、从网络中删除
libcni.CNI接口的各个Add, Del, Check方法中都有一个RuntimeConf参数,包含了一次CNI调用中,除了网络配置之外的参数信息。通常由容器运行时根据上下文来创建,下面是RuntimeConf这个struct的源码:
// A RuntimeConf holds the arguments to one invocation of a CNI plugin
// excepting the network configuration, with the nested exception that
// the `runtimeConfig` from the network configuration is included
// here.
type RuntimeConf struct {
ContainerID string
NetNS string
IfName string
Args [][2]string
// A dictionary of capability-specific data passed by the runtime
// to plugins as top-level keys in the 'runtimeConfig' dictionary
// of the plugin's stdin data. libcni will ensure that only keys
// in this map which match the capabilities of the plugin are passed
// to the plugin
CapabilityArgs map[string]interface{}
// DEPRECATED. Will be removed in a future release.
CacheDir string
}NetworkConfig和NetworkConfigList两个struct对应CNI规范中的网络配置和网络配置列表,每个NetworkConfig对应一个插件Plugin,源码如下:
type NetworkConfig struct {
Network *types.NetConf
Bytes []byte
}
type NetworkConfigList struct {
Name string
CNIVersion string
DisableCheck bool
Plugins []*NetworkConfig
Bytes []byte
}每个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会把操作转发到具体的插件二进制文件执行上。