混部环境指的是当前服务器不是 XDP 程序独占的,部署有其它的网络服务。
如果服务器使用的是 Intel 网卡、而且 XDP 程序采用 Native 模式挂载到网卡上,那么在挂载和卸载 XDP 程序时,会导致网络中断,这会影响到其它网络服务。
本文介绍了一种无损升级 XDP 程序的思路,能够解决这个问题。
XDP 部署方式
XDP 程序采用 Native 模式挂载到网卡上,并且 pin 到指定 bpffs 路径上。
将 XDP 程序 pin 到 bpffs 是为了预防用户态程序异常退出导致 XDP 程序被卸载;意即,即使用户态程序异常退出,XDP 程序仍然能够继续运行。
而在项目实战中,会将所有 bpf 对象都 pin 到 bpffs 路径上,这样能够方便管理所有 bpf 对象。
不过,这种部署方式有一个缺点:在升级 XDP 程序时,如果 bpf map 的 key/value size 发生变化,那么就需要将 XDP 程序卸载,将所有 bpf 对象 unpin 掉。
所以,当遇到混部环境时,这种部署方式就会导致网络中断,影响到其它网络服务。
无损升级 XDP 程序的思路
能够无损升级 Native 模式的 XDP 程序的前提,是网卡驱动支持原子更新 XDP 程序。
支持原子更新 XDP 程序的网卡驱动有:mlx5、ixgbe、i40e。ice 驱动还没支持。
所以,只要网卡驱动支持原子更新 XDP 程序,就能够无损升级 XDP 程序。
思路:
将 bpf 对象所在的目录 mv
到新的目录;将 XDP link 对象克隆到原有的路径下; 在原有路径下,重新生成所有 bpf 对象。 使用克隆的 XDP link 去更新 XDP 程序。 删除新的目录。
不过,其中第 2 步是如何克隆 XDP link 对象呢?
如果直接使用 cp
命令,会得到错误:cp: cannot open 'xdp' for reading: Input/output error
。
这是因为 bpffs 文件系统不支持 cp
命令。
不过,却支持 mv
命令,因为 mv
命令只是修改了文件的路径,而没有修改文件的 inode。
如何绕过这个错误呢?
克隆 XDP link 对象的思路是:LoadPinnedLink()
然后 NewFromID()
。
bpfbak `cloneObj():link`[1]
因此,弄了个小工具:bpfbak[2]。bpfbak
能够克隆 pinned bpf 对象,然后 pin 到另一个路径上。
$ ./bpfbak -h
bpfbak is a tool to backup eBPF objects
Usage:
bpfbak [flags]
Flags:
--auto-mount automatically mount bpffs at the destination directory or --mount-bpffs
-d, --dst string destination filepath to backup the bpf object
-h, --help help for bpfbak
--mount-bpffs string path to the directory where bpffs is mounted
-s, --src string source bpf object to be backed up
--unpin-src unpin the source bpf object after backing up
效果展示:
$ ./bpfbak -s ./test-bpffs-dir/traceroute/xdp -d ./test-bpffs-dir/bak/xdp
$ bpftool l s p ./test-bpffs-dir/bak/xdp
6: xdp prog 67
ifindex ens33(2)
$ bpftool l s p ./test-bpffs-dir/traceroute/xdp
6: xdp prog 67
ifindex ens33(2)
总结
本文介绍了一种无损升级 XDP 程序的思路,能够解决在混部环境下升级 XDP 程序导致网络中断的问题。
由此需求,衍生了一个小工具:bpfbak[3]。“阅读全文” 即可查看 bpfbak
的源码。
bpfbak cloneObj():link
: https://github.com/Asphaltt/bpfbak/blob/c96bde64671f842681c5ec7eca663a1d21357a28/bpfbak.go#L94
bpfbak: https://github.com/Asphaltt/bpfbak
[3]bpfbak: https://github.com/Asphaltt/bpfbak