cni 添加网络 流程分析
Posted Monster-Z
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cni 添加网络 流程分析相关的知识,希望对你有一定的参考价值。
cnitool的使用方式如下:其中<net>是配置文件所在目录,一般为/etc/cni/net.d/*.conf文件,<netns>为network namespace的目录文件,一般为/var/run/netns/NS-ID
cnitool: Add or remove network interfaces from a network namespace cnitool add <net> <netns> cnitool del <net> <netns>
1、cni/cnitool/cni.go
main函数:
(1)、首先从环境变量NETCONFPATH中获取netdir,若不存在则设置为默认值"/etc/cni/net.d",之后调用netconf, err := libcni.LoadConf(netdir, os.Args[2])加载配置变量,netns赋值为os.Args[3]
(2)、调用获得CNIConfig和RuntimeConf
cninet := &libcni.CNIConfig{ Path: strings.Split(os.Getenv(EnvCNIPath), ":") } rt := &libcni.RuntimeConf{ ContainerID: "cni", NetNS: netns, IfName: "eth0" }
(3)、os.Args[1]为add时,调用_, err := cninet.AddNetwork(netconf, rt)添加网络
NetworkConfig的数据结构如下所示:
type NetworkConfig struct { Network *types.NetConf Bytes []byte }
type NetConf struct { CNIVersion string Name string Type string IPAM struct { Type string } DNS DNS }
Runtime的数据结构如下所示:
type RuntimeConf struct { ContainerID string NetNS string IfName string Args [][2]string }
2、cni/libcni/api.go
// AddNetwork executes the plugin with ADD command
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error)
(1)、首先调用pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path),该函数用于在c.Path中寻找net.Network.Type,然后返回全路径
(2)、调用 return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt)),net.Bytes是配置文件的序列化二进制码,其中c.args函数主要的作用是填充并返回一个*invoke.Args类型:
return &invoke.Args { Command: action, ContainerID: rt.ContainerID, NetNS: rt.NetNS, PluginArgs: rt.Args, IfName: rt.IfName, Path: strings.Join(c.Path, ":"), }
3、cni/pkg/invoke/exec.go
func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error)
该函数只是简单地返回 return defaultPluginExec.WithResult(pluginPath, netconf, args)
其中defaultPluginExec是一个*PluginExec的类型变量,赋值过程如下所示:
var defaultPluginExec = &PluginExec{ RawExec: &RawExec{Stderr: os.Stderr}, --->RawExec又是在raw_exec.go中定义的一个结构类型,其中只有一个Stderr的io.Writer类型 VersionDecoder: &version.PluginDecoder{}, } // 其中PluginExec的定义如下所示: type PluginExec struct { RawExec interface { ExecPlugin(pluginPath string, stdinData []byte, environ []string) } VersionDecoder interface { Decode(jsonBytes []byte) (version.PluginInfo, error) } }
4、cni/pkg/invoke/exec.go
func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error)
(1)、调用stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv()),args.AsEnv()将args里的内容转变为环境变量返回,例如CNI_COMMAND=ADD等等
(2)、res := &types.Result{},再调用json.Unmarshal(stdoutBytes, res)解析出Result并返回
5、cni/pkg/invoke/raw_exec.go
func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error)
(1)、首先获得stdout := bytes.Buffer{}作为输出缓冲器
(2)、创建执行命令并调用c.Run()运行
c := exec.Cmd { Env: environ, Path: pluginPath, Args: []string{pluginPath}, Stdin: bytes.NewBuffer(stdinData), Stdout: stdout, Stderr: e.Stderr, }
(3)、最后返回stdout.Bytes()
----------------------------------------------------------------------------------- plugin的执行框架 -----------------------------------------------------------------------
每个插件的main函数都调用了skel.PluginMain(cmdAdd, cmdDel, version.Legacy),其中skel包是一个框架库,所有新添加的插件只要调用skel.PluginMain传入差价的cmdAdd和cmdDel方法就可以了。
1、cni/pkg/skel/skel.go
// PluginMain is the "main" for a plugin. It accepts two callbacks functions for add and del commands
func PluginMain(cmdAdd, cmdDel)
(1)、首先构造一个一个dispatcher类型的caller:
caller := dispatcher { Getenv: os.Getenv, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderrr, }
之后再简单地调用 err := caller.pluginMain(cmdAdd, cmdDel, versionInfo)
2、cni/pkg/skel/skel.go
func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) *types.Error
(1)、首先调用cmd, cmdArgs, err := t.getCmdArgsFromEnv(),该函数从之前传入给本进程的环境变量中解析出很多例如CNI_COMMAND,CNI_CONTAINERID之类的信息,填充获得cmdArgs,如下所示:
cmdArgs := &CmdArgs { ContainerID: contID, Netns: netns, IfName: ifName, Args: args, Path: path, StdinData: stdinData, }
(2)、根据cmd调用相应的处理函数进行处理,例如我们对add进行分析,调用err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd)
3、cni/pkg/skel/skel.go
func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func (*CmdArgs) error)
(1)、首先调用configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)和verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo)对version进行检查
(2)、最后调用return toCall(cmdArgs)函数,进入具体的插件执行网络的add或者delete操作
------------------------------------------------------------------------------------ 当type为bridge时 ------------------------------------------------------------------------
1、cni/plugins/main/bridge/bridge.go
以上是关于cni 添加网络 流程分析的主要内容,如果未能解决你的问题,请参考以下文章