cni 添加網絡 流程分析


cnitool: Add or remove network interfaces from a network namespace

  cnitool  add  <net>  <netns>
  cnitool  del  <net>  <netns>

cnitool的使用方式如下:其中<net>是配置文件所在目錄,一般為/etc/cni/net.d/*.conf文件,<netns>為network namespace的目錄文件,一般為/var/run/netns/NS-ID

 

1、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中尋找對應插件的可執行文件,然后返回路徑

(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, string(os.PathListSeparator)),
}

  

NetworkConfig的數據結構如下所示,用於表示容器要加入的網絡:

// libcni/api.go
type NetworkConfig struct {
  Network   *types.NetConf
  Bytes    []byte      // 在初始化CNI的時候,會將配置文件寫入Bytes
}

// pkg/types/types.go
// NetConf describes a network
type NetConf struct {
  CNIVersion   string
  Name       string
  IPAM  string {
    Type  string
  }
  DNS DNS
}

  

Runtime的數據結構如下所示,用於表示加入網絡的容器的信息:

type RuntimeConf struct {
  ContainerID   string
  NetNS       string
  IfName      string
  Args       [][2]string
}

 

CNIConfig的數據結構如下所示:CNIconfig包含的是bridge,dhcp等插件的可執行文件的目錄的路徑集合

type CNIConfig struct {
  Path    []string
}

  

2、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) ([]byte, error)
  }
  VersionDecoder interface {     Decode(jsonBytes []byte) (version.PluginInfo, error)   } }

  

3、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等等

// Plugin must return result in same version as specified in netconf

(2)、調用versionDecoder := &version.ConfigDecoder{}和confVersion, err := versionDecoder.Decode(netconf)從netconf中解析處CNI的版本

(3)、最后調用return version.NewResult(confVersion, stdoutBytes)返回相應版本的Result

 

4、cni/pkg/invoke/args.go

func (args *Args) AsEnv() []string

1、調用env := os.Environ()獲取已有的環境變量

2、若args.PluginArgsStr為"",則將args.PluginArgs拼接為“A=B;C=D”的形式

3、調用env = append(env, 

  "CNI_COMMAND=" + args.Command,

  "CNI_CONTAINERID=" + args.ContainerID,

  "CNI_NETNS=" + args.NetNS,

  "CNI_ARGS=" + pluginArgsStr,

  "CNI_IFNAME=" + args.IfName,

  "CNI_PATH="+args.Path)

),將參數都作為環境變量傳遞給plugin

 

RawExec的結構如下所示:

type RawExec struct {
  Stderr  io.Writer
}

 

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(),插件直接將結果輸出到stdout

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM