本篇主要是简单了解CNI的一个第三方插件,代码链接在此

概览

用户态CNI插件对接的是基于DPDK的应用程序(ovs-dpdk或者vpp),他会将接口memif/vhost-user添加到主机上,容器内的流量会bypass内核,所以对场景会有影响。

请输入图片描述

使用

建议使用go 1.11.10和ovs-dpdk 2.9.0-3或者VPP 19.04编译插件。

cat ~/.bashrc
export GOPATH=~/go
export CNI_PATH=$GOPATH/src/github.com/containernetworking/plugins/bin
cd $GOPATH/src/
go get github.com/intel/userspace-cni-network-plugin
cd github.com/intel/userspace-cni-network-plugin
make
cp userspace/userspace $CNI_PATH/.
make clean

更新vendor glide update --strip-vendor

配置解析

sudo cat > /etc/cni/net.d/90-userspace.conf <<EOF
{
    "cniVersion": "0.3.1",
        "type": "userspace",
        "name": "memif-network",
        "host": {
                "engine": "vpp",
                "iftype": "memif",
                "netType": "bridge",
                "memif": {
                        "role": "master",
                        "mode": "ethernet"
                },
                "bridge": {
                        "bridgeName": "4"
                }
        },
        "container": {
                "engine": "vpp",
                "iftype": "memif",
                "netType": "interface",
                "memif": {
                        "role": "slave",
                        "mode": "ethernet"
                }
        },
    "ipam": {
        "type": "host-local",
        "subnet": "10.56.217.0/24",
        "rangeStart": "10.56.217.131",
        "rangeEnd": "10.56.217.190",
        "routes": [
            {
                "dst": "0.0.0.0/0"
            }
        ],
        "gateway": "10.56.217.1"
    }
}
EOF
  • type (string, required): "userspace"
  • name (string, required): 名称
  • host (dictionary, required): 宿主机配置,包含usespace接口配置数据以及宿主机网络数据的userspace接口。
  • container (dictionary, optional): 容器配置,包含userspace接口配置数据以及容器网络数据的userspace接口。
  • ipam (dictionary, optional): 该网络的IPAM配置。

集成Multus插件

请输入图片描述

Multus插件支持将多个网络接口附加到Kubernetes中的pod。

请输入图片描述

这是Multus CNI提供的连接到Pod的网络接口的图示。该图显示了具有三个接口的容器:eth0,net0和net1。 eth0连接kubernetes集群网络以连接kubernetes服务器/服务(例如kubernetes api-server,kubelet等)。 net0和net1是其他网络附件,并通过使用其他CNI插件(例如vlan/vxlan/ptp)连接到其他网络。

# cat > /etc/cni/net.d/10-multus.conf <<EOF
{
    "name": "multus-demo-network",
    "type": "multus",
    "delegates": [
        {
            "type": "sriov",
            "if0": "ens786f1",
            "if0name": "net0",
            "dpdk": {
                "kernel_driver": "ixgbevf",
                "dpdk_driver": "igb_uio",
                "dpdk_tool": "/path/to/dpdk/tools/dpdk-devbind.py"
            }
        },
        {
            "cniVersion": "0.3.1",
            "type": "userspace",
            "name": "memif-network",
            "host": {
                "engine": "vpp",
                "iftype": "memif",
                "netType": "bridge",
                "memif": {
                    "role": "master",
                    "mode": "ethernet"
                },
                "bridge": {
                    "bridgeName": "4"
                }
            },
            "container": {
                "engine": "vpp",
                "iftype": "memif",
                "netType": "interface",
                "memif": {
                    "role": "slave",
                    "mode": "ethernet"
                }
            },
            "ipam": {
                "type": "host-local",
                "subnet": "10.56.217.0/24",
                "rangeStart": "10.56.217.131",
                "rangeEnd": "10.56.217.190",
                "routes": [
                    {
                        "dst": "0.0.0.0/0"
                    }
                ],
                "gateway": "10.56.217.1"
            }
        },
        {
            "type": "flannel",
            "name": "control-network",
            "masterplugin": true,
            "delegate": {
                "isDefaultGateway": true
            }
        }
    ]
}
EOF

代码学习

因为新版的CNI插件已经舍去cmdGet了,所以我们只看cmdAdd和cmdDel

cmdAdd操作

  • 调用loadNetConf-->json.Unmarshal解析配置
  • 调用ns.GetNS获取容器的namespace
  • 调用getPodAndSharedDir

    • 调用k8sclient.GetPod检索podSpec找到对应的pod
    • 调用annotations.GetPodVolumeMountHostSharedDir从podSpec中检索sharedDir,目录socket文件写入宿主机,并标记为found
    • 如果没有标记found,需要生成sharedDir
  • 如果是vpp,那么调用vpp.AddOnHost申请端口和网络

    • 调用vppinfra.VppOpenCh创建channel

      • govpp.Connect连接vpp
      • vppCh.conn.NewAPIChannel创建API channel
    • addLocalDeviceMemif创建本地端口

      • getMemifSocketfileName生成一个socketfile
      • 根据配置信息生成后续创建端口的参数
      • vppmemif.CreateMemifSocket创建memif socket

        • findMemifSocket循环浏览memif socket列表,并确定输入的socketfile是否存在,如果存在,则返回关联的socketid,如果没有,则返回下一个可用的socketid
        • filepath.Dir获取socketfile获取他的目录,判定是否存在,不存在则调用os.MkdirAll创建目录
        • 调用ch.SendRequest(req).ReceiveReply(reply)发送请求,并且接受reply
      • vppmemif.CreateMemifInterface创建端口,也是类似上面的channel发送请求
    • vppinterface.SetState将端口设置为up
    • 如果是bridge模式,表示L2网络,则调用vppbridge.AddBridgeInterface将端口添加到bridge
    • 如果是interface模式,表示L3网络,则调用vppinterface.AddDelIpAddress添加IP地址
    • SaveConfig存储数据,后续删除的时候会用到
  • 如果是ovs,调用ovs.AddOnHost申请端口和网络

    • 调用addLocalNetworkBridge创建bridge

      • findBridge调用ovs-vsctl --bare --columns=name find bridge name=<bridge_name>查看bridge名称
      • 如果没有发现,createBridge调用ovs-vsctl add-br <bridge_name> -- set bridge <bridge_name> datapath_type=netdev创建bridge
    • 如果端口类型是vhostuser,addLocalDeviceVhost创建端口

      • 判断sharedDir是否存在,不存在则创建
      • 判断是配置client模式还是server模式
      • createVhostPort创建vhost端口,ovs-vsctl add-port <bridge_name> <sock_name> -- set Interface <sock_name> type=<dpdkvhostuser|dpdkvhostuserclient>
      • getVhostPortMac获取mac地址,ovs-vsctl --bare --columns=mac find port name=<sock_name>
    • SaveVppConfig存储数据,后续删除的时候会用到
  • ipam.ExecAdd申请IP地址
  • 如果是vpp,则调用vpp.AddOnContainer给容器添加端口和网络,configdata.SaveRemoteConfig

    • 如果kubeClient存在,则调用annotations.WritePodAnnotation将容器内的配置发送给kubeClient
    • 如果不存在,则会通过ioutil.WriteFile给socketfile发送信息
  • 如果是ovs,则调用ovs.AddOnContainer给容器添加端口和网络,configdata.SaveRemoteConfig类似以上
  • 将结果返回

cmdDel操作

  • 调用loadNetConf-->json.Unmarshal解析配置
  • 调用getPodAndSharedDir

    • 调用k8sclient.GetPod检索podSpec找到对应的pod
    • 调用annotations.GetPodVolumeMountHostSharedDir从podSpec中检索sharedDir,目录socket文件写入宿主机,并标记为found
    • 如果没有标记found,需要生成sharedDir
  • 如果vpp的话,vpp.DelFromHost

    • 调用vppinfra.VppOpenCh创建channel

      • govpp.Connect连接vpp
      • vppCh.conn.NewAPIChannel创建API channel
    • LoadVppConfig从之前保存的数据
    • 如果是bridge的类型的话,vppbridge.RemoveBridgeInterface删除端口

      • 生成删除端口的请求,调用ch.SendRequest(req).ReceiveReply(reply)发送请求
      • DeleteBridge删除bridge,主要是先调用findBridge查找,然后生成删除bridge的请求,然后发送请求
      • delLocalDeviceMemif删除memif端口

        • getMemifSocketfileName获取memif对应的socketfile
        • vppmemif.DeleteMemifInterface删除端口

          • findMemifInterface删除memif
          • 生成请求,ch.SendRequest(req).ReceiveReply(reply)
          • 如果socketid不为0,则删除对应的socketfile,DeleteMemifSocket
  • 如果ovs的话,ovs.DelFromHost

    • LoadConfig加载之前保存的数据
    • 如果是vhostuser端口的话,调用delLocalDeviceVhost删除端口

      • 调用deleteVhostPort删除端口,执行命令ovs-vsctl del-port <bridge_name> <sock_name>
      • 如果成功了,删除对应的一些文件
    • delLocalNetworkBridge删除bridge,调用deleteBridge执行命令ovs-vsctl del-br <bridge_name>
最后修改:2021 年 08 月 20 日
如果觉得我的文章对你有用,请随意赞赏