本篇主要是简单了解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
 
- findBridge调用
- 如果端口类型是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>
- 如果成功了,删除对应的一些文件
 
- 调用deleteVhostPort删除端口,执行命令
- delLocalNetworkBridge删除bridge,调用deleteBridge执行命令ovs-vsctl del-br <bridge_name>
 



1 条评论
真好呢