CNI定义了一个网络插件的实现标准,网络插件需要是一个可执行程序,它运行时读取环境变量:
CNI_COMMAND : 操作ADD/DEL/VERSION
CNI_CONTAINERID : 容器的ID
CNI_NETNS : 目标net namespace,将容器加入这个ns,或者从这个ns删除
CNI_ARGS : 额外传递的参数
CNI_IFNAME : 设置的容器网卡名称,如eth0
例如:
CNI_COMMAND=ADD CNI_CONTAINERID=bcc8ad23b46b CNI_IFNAME=eth1 CNI_NETNS=
目标网络的配置文件则是从stdin中读取。
插件的实现者只需要调用cni的skel.PluginMain
,加载实现的ADD和DEL函数。
package main
import (
"github.com/containernetworking/cni/pkg/skel"
...
)
...
skel.PluginMain(cmdAdd, cmdDel, cniSpecVersion.All)
在PluginMain在gthub.com/containernetworking/cni/pkg/skel/skel.go
中实现:
func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) {
if e := PluginMainWithError(cmdAdd, cmdDel, versionInfo); e != nil {
...
}
PluginMainWithError
的实现:
func PluginMainWithError(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) *types.Error {
return (&dispatcher{
Getenv: os.Getenv,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}).pluginMain(cmdAdd, cmdDel, versionInfo)
}
dispatcher的pluginMain
函数中:
func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) *types.Error {
...
cmd, cmdArgs, err := t.getCmdArgsFromEnv()
...
switch cmd {
case "ADD":
err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd)
case "DEL":
err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel)
case "VERSION":
err = versionInfo.Encode(t.Stdout)
在t.getCmdArgsFromEnv()
中读取环境变量:
{
"CNI_COMMAND",
&cmd,
reqForCmdEntry{
"ADD": true,
"DEL": true,
},
},
{
"CNI_CONTAINERID",
&contID,
reqForCmdEntry{
"ADD": false,
"DEL": false,
},
},
{
"CNI_NETNS",
&netns,
reqForCmdEntry{
"ADD": true,
"DEL": false,
},
},
{
"CNI_IFNAME",
&ifName,
reqForCmdEntry{
"ADD": true,
"DEL": true,
},
},
{
"CNI_ARGS",
&args,
reqForCmdEntry{
"ADD": false,
"DEL": false,
},
},
{
"CNI_PATH",
&path,
reqForCmdEntry{
"ADD": true,
"DEL": true,
},
},
cni-plugins中提供了几个cni的实现:
bridge
dhcp
flannel
host-local
ipvlan
loopback
macvlan
portmap
ptp
sample
tuning
vlan
还有一些容器的网络项目自己实现了cni插件,例如cni中列出的:
Project Calico - a layer 3 virtual network
Weave - a multi-host Docker network
Contiv Networking - policy networking for various use cases
SR-IOV
Cilium - BPF & XDP for containers
Infoblox - enterprise IP address management for containers
Multus - a Multi plugin
Romana - Layer 3 CNI plugin supporting network policy for Kubernetes
CNI-Genie - generic CNI network plugin
Nuage CNI - Nuage Networks SDN plugin for network policy kubernetes support
Silk - a CNI plugin designed for Cloud Foundry
Linen - a CNI plugin designed for overlay networks with Open vSwitch and fit in SDN/OpenFlow network environment
Vhostuser - a Dataplane network plugin - Supports OVS-DPDK & VPP
Amazon ECS CNI Plugins - a collection of CNI Plugins to configure containers with Amazon EC2 elastic network interfaces (ENIs)
cni约定了网络配置文件的格式:
// NetConf describes a network.
type NetConf struct {
CNIVersion string `json:"cniVersion,omitempty"`
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
Capabilities map[string]bool `json:"capabilities,omitempty"`
IPAM struct {
Type string `json:"type,omitempty"`
} `json:"ipam,omitempty"`
DNS DNS `json:"dns"`
}
type DNS struct {
Nameservers []string `json:"nameservers,omitempty"`
Domain string `json:"domain,omitempty"`
Search []string `json:"search,omitempty"`
Options []string `json:"options,omitempty"`
}
规定了go的api接口:
-+CNIConfig : struct
[fields]
+Path : []string
[methods]
+AddNetwork(net *NetworkConfig, rt *RuntimeConf) : types.Result, error
+AddNetworkList(list *NetworkConfigList, rt *RuntimeConf) : types.Result, error
+DelNetwork(net *NetworkConfig, rt *RuntimeConf) : error
+DelNetworkList(list *NetworkConfigList, rt *RuntimeConf) : error
+GetVersionInfo(pluginType string) : version.PluginInfo, error
-args(action string, rt *RuntimeConf) : *invoke.Args
例如调用AddNetwork()方法时,会运行参数net中的Network.Type对应的二进制程序,将rt中的容器添加到指定ns或者从ns中删除。
// AddNetwork executes the plugin with the ADD command
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path) //注意Type就是插件对应的二进制程序名
if err != nil {
return nil, err
}
net, err = injectRuntimeConfig(net, rt)
if err != nil {
return nil, err
}
return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt))
}
参数rt中指定了ContainerID,netNS等:
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{}
}