本文最后更新于:January 17, 2023 pm
本文主要介绍如何在calico集群上开启eBPF加速网络数据转发,同时会对eBPF及其在calico中的一些优势特点进行介绍。
关于本次使用的calico集群的部署过程可以参考之前的文章k8s系列13-calico部署BGP模式的高可用k8s集群 。
此前写的一些关于k8s基础知识和集群搭建的一些方案 ,有需要的同学可以看一下。
1、eBPF 1.1 关于eBPF eBPF 是一项革命性的技术,起源于 Linux 内核,可以在操作系统内核中运行沙盒程序。它用于安全有效地扩展内核的功能,而无需更改内核源代码或加载内核模块。
它允许将小程序加载到内核中,并附加到钩子上,这些钩子在某些事件发生时被触发。这甚至允许大量定制内核的行为。虽然 eBPF 虚拟机对于每种类型的钩子都是相同的,但钩子的功能却有很大差异。因为将程序加载到内核中可能很危险,所以内核通过非常严格的静态验证器运行所有程序;静态验证器对程序进行沙盒处理,确保它只能访问允许的内存部分,并确保它必须迅速终止。
eBPF is a Linux kernel feature that allows fast yet safe mini-programs to be loaded into the kernel in order to customise its operation.
1.2 eBPF的优势 ebpf的几个优势:
更高的吞吐IO
更低的CPU资源占用
无需kube-proxy即可对K8S服务实现原生支持
更低的首个数据包延迟
外部请求可保留客户端源IP(Preserves external client source IP)
支持 DSR (Direct Server Return)
相比kube-proxy,ebpf数据面同步转发规则时占用的资源更少
更多的信息可以查看calico官方的这篇介绍文章 。
2、calico配置eBPF 2.1 升级内核 我们使用的centos7系统,根据文档比较适合的内核版本需要大于5.8,因此我们直接使用elrepo源升级最新的6.1.4版本内核。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 [root@k8s-calico-master-10-31-90-1 ~] Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile elrepo-kernel | 3.0 kB 00:00:00 elrepo-kernel/x86_64/primary_db | 2.1 MB 00:00:00 Available Packages elrepo-release.noarch 7.0-6.el7.elrepo elrepo-kernel kernel-lt.x86_64 5.4.228-1.el7.elrepo elrepo-kernel kernel-lt-devel.x86_64 5.4.228-1.el7.elrepo elrepo-kernel kernel-lt-doc.noarch 5.4.228-1.el7.elrepo elrepo-kernel kernel-lt-headers.x86_64 5.4.228-1.el7.elrepo elrepo-kernel kernel-lt-tools.x86_64 5.4.228-1.el7.elrepo elrepo-kernel kernel-lt-tools-libs.x86_64 5.4.228-1.el7.elrepo elrepo-kernel kernel-lt-tools-libs-devel.x86_64 5.4.228-1.el7.elrepo elrepo-kernel kernel-ml.x86_64 6.1.4-1.el7.elrepo elrepo-kernel kernel-ml-devel.x86_64 6.1.4-1.el7.elrepo elrepo-kernel kernel-ml-doc.noarch 6.1.4-1.el7.elrepo elrepo-kernel kernel-ml-headers.x86_64 6.1.4-1.el7.elrepo elrepo-kernel kernel-ml-tools.x86_64 6.1.4-1.el7.elrepo elrepo-kernel kernel-ml-tools-libs.x86_64 6.1.4-1.el7.elrepo elrepo-kernel kernel-ml-tools-libs-devel.x86_64 6.1.4-1.el7.elrepo elrepo-kernel perf.x86_64 5.4.228-1.el7.elrepo elrepo-kernel python-perf.x86_64 5.4.228-1.el7.elrepo elrepo-kernel sudo yum --enablerepo=elrepo-kernel install kernel-ml -y sudo grubby --info=ALL sudo grubby --set-default-index=0 sudo grubby --default-kernel init 6uname -a
确认系统内核升级成功后我们继续下一步
$ ansible calico -m shell -a "uname -rv" 10.31.90.4 | CHANGED | rc=0 >> 6.1.4-1.el7.elrepo.x86_64 10.31.90.5 | CHANGED | rc=0 >> 6.1.4-1.el7.elrepo.x86_64 10.31.90.3 | CHANGED | rc=0 >> 6.1.4-1.el7.elrepo.x86_64 10.31.90.2 | CHANGED | rc=0 >> 6.1.4-1.el7.elrepo.x86_64 10.31.90.1 | CHANGED | rc=0 >> 6.1.4-1.el7.elrepo.x86_64 10.31.90.6 | CHANGED | rc=0 >> 6.1.4-1.el7.elrepo.x86_64
2.2 配置API Server 在默认情况下,calico是通过kube-proxy
和集群内的apiserver进行通信的。当我们开启了ebpf之后,往往会关闭集群的kube-proxy
功能,此时为了保证calico能够和apiserver进行通信,就需要我们先手动配置一个稳定可用的地址。一般来说,使用我们初始化集群的时候配置的VIP和端口即可。
$ cat kubernetes-services-endpoint.yaml kind: ConfigMap apiVersion: v1 metadata: name: kubernetes-services-endpoint namespace: tigera-operator data: KUBERNETES_SERVICE_HOST: "k8s-calico-apiserver.tinychen.io" KUBERNETES_SERVICE_PORT: "8443" $ kubectl create -f kubernetes-services-endpoint.yaml configmap/kubernetes-services-endpoint created
更新配置后检查pod是否重启成功,以及集群是否正常
$ watch kubectl get pods -n calico-system $ calicoctl node status
2.3 配置kube-proxy 因为ebpf会和kube-proxy
冲突,比较好的方式是禁用掉kube-proxy
。而禁用kube-proxy主要有两种方式,一种是直接将其对应的daemonset
删除,另一种则是通过nodeSelector
的方式将特定的node设置为不运行kube-proxy
。从官方的文档来看两种方式各有优势,如果是初始化k8s集群的时候就直接使用ebpf的话可以考虑直接在初始化参数中将kube-proxy
禁用,这里我们已经安装好了kube-proxy
,则使用nodeSelector
的方式控制会更为优雅。
$ kubectl patch ds -n kube-system kube-proxy -p '{"spec":{"template":{"spec":{"nodeSelector":{"non-calico": "true"}}}}}'
此时我们再查看集群的kube-proxy状态可以发现DESIRED
和CURRENT
均为0
$ kubectl get ds -n kube-system kube-proxy NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE kube-proxy 0 0 0 0 0 kubernetes.io/os=linux,non-calico=true 4d9h
2.4 配置eBPF 开启eBPF只需要修改linuxDataplane
参数即可,注意eBPF模式不支持配置hostPorts
,因此需要同时将其设置为空。
$ kubectl patch installation.operator.tigera.io default --type merge -p '{"spec":{"calicoNetwork":{"linuxDataplane":"BPF", "hostPorts":null}}}' installation.operator.tigera.io/default patched
等待calico滚动重启完成,即成功开启eBPF。注意在滚动重启的过程中,会存在部分node使用eBPF而部分node使用iptables的情况。
2.5 配置DSR eBPF还有一项不错的功能称之为DSR(Direct Server Return),即pod在接收到外部的请求之后,直接由pod本身对客户端进行回包,而不是再经由原来的路径回包,这样可以有效缩短回包路径从而提升性能。
DSR模式的工作效果和LVS的DR模式十分相似,但是一般来说,DSR需要pod和客户端之间的网络本身是正常联通的,所以默认情况下该项功能并没有开启。
我们可以使用calicoctl
来修改felixconfiguration
中的bpfExternalServiceMode
参数,将其从默认的Tunnel
修改为DSR
即可启用。
$ calicoctl patch felixconfiguration default --patch='{"spec": {"bpfExternalServiceMode": "DSR"}}' Successfully patched 1 'FelixConfiguration' resource
如果需要关闭DSR模式只需要逆向修改回去即可
$ calicoctl patch felixconfiguration default --patch='{"spec": {"bpfExternalServiceMode": "Tunnel"}}'
3、检验eBPF 3.1 calico-bpf 官方在calico-node中内置了calico-bpf来帮助我们排查定位eBPF模式下的一些问题,我们也可以用它来检测集群的eBPF是否工作正常。
对应的ds/calico-node也可以替换为某个特定node上面的calico-node的pod名称
$ kubectl exec -n calico-system ds/calico-node -- calico-node -bpf $ kubectl exec -n calico-system ds/calico-node -- calico-node -bpf counters dump --iface=eth0 $ kubectl exec -n calico-system ds/calico-node -- calico-node -bpf conntrack dump $ kubectl exec -n calico-system ds/calico-node -- calico-node -bpf routes dump
3.2 访问服务 我们在集群外的机器进行测试,分别访问podIP 、clusterIP和loadbalancerIP,查看是否能够正确返回客户端的IP地址10.31.100.100
。
root@tiny-unraid:~ 10.31.100.100:43240 root@tiny-unraid:~ 10.31.100.100:52758 root@tiny-unraid:~ 10.31.100.100:7319
在配置了eBPF的情况下,从集群外访问clusterIP和loadbalancerIP都是能够正常返回客户端的IP地址的。
4、关闭eBPF 关闭eBPF的操作也是非常简单,只需要逆向操作上面的过程即可,这里直接关闭calico的eBPF功能(DSR只在eBPF下生效),再开启k8s集群的kube-proxy即可。
$ kubectl patch installation.operator.tigera.io default --type merge -p '{"spec":{"calicoNetwork":{"linuxDataplane":"Iptables"}}}' $ kubectl patch ds -n kube-system kube-proxy --type merge -p '{"spec":{"template":{"spec":{"nodeSelector":{"non-calico": null}}}}}'
配置完成后,等待全部节点的calico重启完成并且kube-proxy启动完成即可。