Kubernetes Calico VPP软协议栈 电脑版发表于:2022/11/24 14:05  >#Kubernetes Calico VPP软协议栈 [TOC] ## VPP简介 tn2>VPP(Vector Packet Processor)是一个高性能、开源的用户空间网络数据平面,用C语言编写,在 fd.io 旗下开发。它支持许多标准网络功能(L2、L3路由、NAT、封装),并且可以使用插件轻松扩展。VPP数据平面使用插件来有效地实现Kubernetes服务负载平衡和Calico策略。 tn>简单来说,它就是个跑在用户空间的TCP/IP协议栈,不需要进入内核。那么它的实现就如下图所示:  ## 安装VPP ### 安装前提 tn2>首先我们需要一个空白的集群环境,也就是相当执行`kubeadm init`后没有安装CNI的其他插件。 第二就是内核版本需要很高。 ### HugePages概述 tn2>HugePages是集成到Linux内核2.6中的一项特性。启用HugePages使操作系统可以支持的内存页大于默认值(通常为4 KB)。使用非常大的页面大小可以通过减少访问page table entries所需的系统资源量来提高系统性能。HugePages对32位和64位配置都很有用。HugePage大小从2 MB到256 MB不等,具体取决于内核版本和硬件架构。对于Oracle数据库,使用HugePages可以减少page states的操作系统维护,并提高转换后备缓冲区(Translation Lookaside Buffer ,TLB)命中率。 更多请参考:https://www.cnblogs.com/plluoye/p/10836322.html ### 配置HugePages tn2>通过以下hugepages配置可能使VPP能够使用更高效的驱动程序: 至少512 x 2MB hugepage可用(`grep hugepages_Free/proc/meminfo`) 加载`vfio_pci`(centos上的`vfio_pci`)或`uio_pci_generic`内核模块。例如: ```bash echo "vfio-pci" > /etc/modules-load.d/95-vpp.conf modprobe vfio-pci echo "vm.nr_hugepages = 512" >> /etc/sysctl.conf # 生效 sysctl -p # 重新启动kubelet以将更改考虑在内 # 根据kubelet的安装方式,您可能需要使用不同的命令 systemctl restart kubelet ``` ### 安装Calico与配置VPP tn2>安装Calico与VPP ```bash kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.24.5/manifests/tigera-operator.yaml kubectl apply -f https://raw.githubusercontent.com/projectcalico/vpp-dataplane/v3.24.0/yaml/calico/installation-default.yaml ```  tn2>然后我们等待一下Pod的安装。 ### 安装VPP数据平面组件 tn2>首先获取VPP数据平面资源的适当yaml清单: ```bash # 如果设置了hugepages curl -o calico-vpp.yaml https://raw.githubusercontent.com/projectcalico/vpp-dataplane/v3.24.0/yaml/generated/calico-vpp.yaml # 没有设置,或不确定使用 curl -o calico-vpp.yaml https://raw.githubusercontent.com/projectcalico/vpp-dataplane/v3.24.0/yaml/generated/calico-vpp-nohuge.yaml ``` tn2>这里我们选者后者的下载。 VPP快的原因除了不走内核,还有就是CPU是专用的。 `vpp_dataplane_interface`是vpp将使用的主要接口。它必须是Linux接口的名称,并使用地址进行配置。此接口上配置的地址必须是Kubernetes中的节点地址。 ```bash # 查看我们的网络接口 ifconfig # 查看虚拟网口的速度 ethtool ens33 vim calico-vpp # 修改vpp_dataplane_interface: ens33 ```    tn2>我们知道VPP可以进行各种各样的包数据进行处理,但是它发出去的时候是通过uplink_interface进行发出去的,`vpp_uplink_driver`这个参数可以设置具体应该设置什么来快速处理包发出去。举例:dpdk。 这里我们就选默认的即可(模式是`af_packet` : 使用af_packet套接字驱动接口(未优化,但在任何地方都有效))。 更多请参考:  tn2>接下来我们还需要设置Kubernetes中Service的网段也就是`service_prefix`参数。(可通过如下命令进行查看) ```bash kubectl cluster-info dump | grep -m 1 service-cluster-ip-range ```  tn2>最后Apply一下,完成VPP的安装。 ```bash kubectl apply -f calico-vpp.yaml ``` ### 物理网卡变了? tn2>我们通过`ethtool ens33`查询发现它的速度从千兆网速度变成了`10MB/s`,并且物理网卡变成了一个TAP设备。 到这里你可能会问了为什么会这样?   tn2>由于我们VPP是用户态的,所以通过`TAP/TUN`来连接处理数据包。 更多大家可以参考我以往的文章:http://tnblog.net/hb/article/details/7233#TAP%E4%B8%8ETUN%E8%AE%BE%E5%A4%87 tn>简单来讲就是一端连在我们的协议栈一端连接在我们的IP层。  tn2>那既然这样包都出不去,我们的ens33物理端口被谁拿走了呢? 答案是被VPP拿走了。我们可以安装`calicovpctl`来进行查看。 ### 安装calivpctl tn2>calivpctl是vpp容器映像附带的一个助手bash脚本。它可以通过以下方法安装到主机上,并有助于收集日志和调试安装了VPP数据平面的正在运行的集群。 ```bash curl https://raw.githubusercontent.com/projectcalico/vpp-dataplane/v3.24.0/test/scripts/vppdev.sh \ | tee /usr/bin/calivppctl chmod +x /usr/bin/calivppctl ``` ### VPP处理过程 tn2>然后我们进入到VPP中。 ```bash calivppctl # 这里的bpf1是通过查看安装好的VPP控制Pod的后缀名称获取到的 calivppctl vppctl bpf1 ```  tn2>我们知道VPP是用户态的协议栈,内核协议栈能够处理的它同样能够处理,所以我们把它ens33的主机网卡拿走了我们同样可以处理。不然拿走了我们的ssh就连接不上主机了。 这里我们来查看一下接口处理情况。 ```bash show interfaces ```  tn2>我们还可以通过查看物理网卡对接的情况来看。 `host-ens33`就是对接的我们的物理网卡ens33,并且连物理mac地址都没有变化。 ```bash show hardware-interfaces ```  tn2>再来我们在查看接口中有ipip0和ipip1,那么是不是有tunnl呢? 通过查看网卡并没有发现tunnel相关的网卡。 我们可以通过查看Tunnl的方式来查看相关信息。 ```bash show ipip tunnel ```  tn2>我们发现这里写好了从源ip地址和目标地址,意思就是现在ipip由vpp来进行处理。  tn2>在来我们这里有tap0设备,它是一个vpp的tap文件但这把它当成一个网卡,那么其他pod的另一端在哪儿呢? 我们首先尝试创建几个pod。 ```bash kubectl apply -f cni.yaml # 运行起来后查看pod的接口 kubectl get pod kubectl exec -it cni-kpfnz -- bash ifconfig ip tuntap list ```  tn2>可以看到这个eth0它是没有mac地址的,卧槽为什么没有地址啊?因为它是点对点(`P-t-P`)的设备一个tun设备,就像两头直接连着线的所以不需要MAC地址,就IP地址就可以了。 我们在VPP中也是可以找到的,我们可以看到有很多`tun`设备就是它们连接的vpp这一端。 ```bash show interfaces show interfaces address ```  tn2>刚刚我们连接的容器就是tun5的`10.244.11.75`。 tn>所以整个创建的流程是这样: 当在主机上调度Pod时,kubelet服务将为新Pod创建网络名称空间,然后使用CNI请求Calico在此名称空间中配置接口。Calico将首先计算主机的IP配置(地址和路由),然后将其传递给VPP代理。然后,VPP将在所需的容器名称空间中创建tun接口,并使用所需的地址和路由对其进行配置。这使得所有的集装箱流量都通过VPP。  tn2>我们可以看到路由表都是直接走eth0,然后交给VPP协议栈处理,看看其他的tun对应的ip在哪儿。 ```bash # 一个pod中ping ping xxxxx # vpp中抓包 pcap trace rx tx max 30000 intfc any pcap trace off ```  tn2>这里抓的包在calico vpp的 pod里面,所以我们可以复制拿出来,当然也可以从节点中直接获取到。 ```bash find / -name rxtx.pcap ```  tn2>然后直接拿下来进行分析,我们发现它直接实现了ipip。  ### Vxlan模式能否实现? tn2>首先我们改成Vxlan的配置。  tn2>然后我们在来看看接口。 ```bash calivppctl calivppctl vppctl bpf1 show interfaces ``` tn2>我们发现IPIP的没了,现在是vxlan的tunnel。  ```bash show vxlan tunnel ```  tn2>我们发现源ip,目的ip,源端口4789,目标端口4789,vni为4096。 接下来我们再抓个包看看。  tn2>虽然有相关vxlan信息但是解不开数据。 当我们向外发包的时候,它会修改外部Mac地址为VPP的MAC地址,可以通过`show tap`来进行查看。 ## 总结 