本篇主要是简单了解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>
6 条评论
你的文章让我感受到了无尽的欢乐,谢谢分享。 https://www.4006400989.com/qyvideo/54236.html
你的文章总是能给我带来欢乐,谢谢你! https://www.4006400989.com/qyvideo/63768.html
真好呢
《混世游侠》国产剧高清在线免费观看:https://www.jgz518.com/xingkong/39411.html
文章的确不错啊https://www.cscnn.com/
不错不错,我喜欢看 https://www.ea55.com/