Bootstrap

multus-cni之源码解析

在这里插入图片描述
欢迎关注微信公众号*“云原生手记”***


源码地址:https://github.com/k8snetworkplumbingwg/multus-cni.git

源码解析

解析之前,先看下机器上的/opt/cni/bin目录下的可执行文件,这些文件都有可能被cni插件调用:

[root@zhounanjun-test01 ~]# ls /opt/cni/bin/
bandwidth  calico       dhcp      flannel      host-local  kube-ovn  macvlan  portmap  sbr    static  vlan
bridge     calico-ipam  firewall  host-device  ipvlan      loopback  multus   ptp      sriov  tuning  whereabouts

这些 CNI 的基础可执行文件,按照功能可以分为以下三类:

  • 第一类,叫做 Main 插件,它是用来创建具体网络设备的二进制文件。比如,bridge(网桥设备)、ipvlan、loopback(lo设备)、macvlan、ptp(Veth Pari 设备)、以及 vlan。
  • 第二类,叫做 IPAM(IP Address Management)插件,它是负责分配 IP 地址的二进制文件。比如
    • dhcp 会向 DHCP 服务器发起请求;
    • host-local 会使用预先配置的 IP 地址段来进行分配;
    • calico-ipam是clalico cni自己的ip地址分配插件,是一种集中式ip分配插件;
    • whereabouts也是一个集中式ip分配插件,用的比较少,我也是因为使用了sriov设备才用到的,所以节点上有这个文件, 这个是k8snetworkplumbingwg社区开源的。
  • 第三类,是由 CNI 社区维护的内置 CNI 插件,比如
    • flannel,这就是专门为 Flannel 项目提供的 CNI 插件;
    • tunning,是一个通过 sysctl 调整网络设备参数的二进制文件;
    • portmap 是一个通过 iptables 配置端口映射的二进制文件;
    • bandwidth 是一个使用 Token Bucket Filter(TBF)来进行限流的二进制文件;
    • calico是专门为Calico项目提供的CNI插件。

cmdAdd解析

源码解析的过程,也就是实现cni插件的过程,希望读者在本文解析后可以实现自己的cni插件。
multus-cni插件的启动代码在cmd/main.go文件中
在这里插入图片描述

main函数如下:

func main() {

    // Init command line flags to clear vendored packages' one, especially in init()
    flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)

    // add version flag
    versionOpt := false
    flag.BoolVar(&versionOpt, "version", false, "Show application version")
    flag.BoolVar(&versionOpt, "v", false, "Show application version")
    flag.Parse()
    if versionOpt == true {
        fmt.Printf("%s\n", multus.PrintVersionString())
        return
    }

    skel.PluginMain( // 实现cni插件的三个函数cmdAdd, cmdCheck, cmdDel
        func(args *skel.CmdArgs) error {
            // 用于创建容器时构建网络环境
            result, err := multus.CmdAdd(args, nil, nil)
            if err != nil {
                return err
            }
            return result.Print()
        },
        func(args *skel.CmdArgs) error {
            return multus.CmdCheck(args, nil, nil)
        },
        // 用于删除容器时回收 网络环境
        func(args *skel.CmdArgs) error { return multus.CmdDel(args, nil, nil) },
        cniversion.All, "meta-plugin that delegates to other CNI plugins")
}

skel.PluginMain中传入的三个函数类型,就是cmdAdd, cmdCheck, cmdDel函数。那么先看cmdAdd函数,就是下面这段:

func(args *skel.CmdArgs) error {
            // 用于创建容器时构建网络环境
            result, err := multus.CmdAdd(args, nil, nil)
            if err != nil {
                return err
            }
            return result.Print()
        }

调用的是multus.CmdAdd,multus.CmdAdd函数的内容如下,稍加缩减:

func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (cnitypes.Result, error) {
    // 是/etc/cni/net.d/00-multus.conf文件的配置
    n, err := types.LoadNetConf(args.StdinData) // 从stdinData中反序列化配置,
    logging.Debugf("CmdAdd: %v, %v, %v", args, exec, kubeClient)
    if err != nil {
        return nil, cmdErr(nil, "error loading netconf: %v", err)
    }
    // 生成client
    kubeClient, err = k8s.GetK8sClient(n.Kubeconfig, kubeClient)
    if err != nil {
        return nil, cmdErr(nil, "error getting k8s client: %v", err)
    }

    k8sArgs, err := k8s.GetK8sArgs(args) // 参数中包含的是kubelet传递过来的pod信息,比如pod名称,pod所属的namespace等信息
    if err != nil {
        return nil, cmdErr(nil, "error getting k8s args: %v", err)
    }
    ...

    pod, err := getPod(kubeClient, k8sArgs, false) // 从k8s获取pod对象
    if err != nil {
        return nil, err
    }
    ...
    // 获取/etc/cni/net.d/00-multus.conf 配置中的delegates字段数据,该字段中放着的是默认cni插件的配置,比如calico的
    // 这个函数深入看下,适用于获取pod要使用的网络插件信息的,可能存在多个网络插件要使用。
    _, kc, err := k8s.TryLoadPodDelegates(pod, n, kubeClient, resourceMap) // n中的Delegates数组已经更新完毕
    if err != nil {
        return nil, cmdErr(k8sArgs, "error loading k8s delegates k8s args: %v", err)
    }

    // cache the multus config 将Delegates数组内容保存在CNIDir中,
    // CNIDir默认路径是:/var/lib/cni/multus
    if err := saveDelegates(args.ContainerID, n.CNIDir, n.Delegates); err != nil {
        return nil, cmdErr(k8sArgs, "error saving the delegates: %v", err)
    }

    var result, tmpResult cnitypes.Result
    var netStatus []nettypes.NetworkStatus
    cniArgs := os.Getenv("CNI_ARGS")
    for idx, delegate := ran

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;