前文剖析了 LVS 作为负载平衡的原理。随着 eBPF 的倒退,咱们曾经能够将 eBPF/XDP 程序间接部署在一般服务器上来实现负载平衡,从而节俭掉用于专门部署 LVS 的机器。

本文不打算间接到这一步,而是首先看看如何用 eBPF/XDP 依照惯例模式来代替 LVS,也就是说咱们还是将负载平衡程序(software load balance 简称 SLB)部署在专用机器上,只不过不必 LVS,而是用 eBPF/XDP 来实现。

试验步骤

创立网络环境

# 不同发行版命令不一样systemctl start dockerdocker network create south --subnet 172.19.0.0/16 --gateway 172.19.0.1# checkdocker network inspect south# orip link# 先用 ifconfig 取得刚创立的 network 应的 bridge# 后续则能够在宿主机上抓取这个 network 的所有 IP 包tcpdump -i br-3512959a6150 ip# 也能够取得某个容器的 veth ,抓取这个容器进出的所有包tcpdump -i vethf01d241  ip# 当然,如果是 offload 的模式,则调试的确不易,须要嗅探本地网络的数据包并抓取了# 在容器网络里,咱们尚有宿主机这个上帝视角,在裸机网络里,则可能得去捯饬路由器了

创立两个RS

echo "rs-1" > rs1.htmlecho "rs-2" > rs2.htmldocker run -itd --name rs1 --hostname rs1 --privileged=true --net south -p 8888:80 --ip 172.19.0.2 --mac-address="02:42:ac:13:00:02" -v "$(pwd)"/rs1.html:/usr/share/nginx/html/index.html:ro nginx:stabledocker run -itd --name rs2 --hostname rs2 --privileged=true --net south -p 9999:80 --ip 172.19.0.3 --mac-address="02:42:ac:13:00:03" -v "$(pwd)"/rs2.html:/usr/share/nginx/html/index.html:ro nginx:stable# check on hostcurl 127.0.0.1:8888curl 127.0.0.1:9999

另:
即便是 nginx 对于咱们调试负载平衡也不是足够简略,调试阶段能够用 nc 来进行调试
dnf install nc or apt install netcat
server side nc -l -vv -p 5000
client side nc 172.19.0.2 5000

实现SLB

为了不影响 RS,本文采纳 NAT 模式的进一步:Full-NAT 模式实现 SLB。这种模式有缺点:rs 不能取得实在的 client ip,然而对部署环境要求绝对较少(网络相通,无需设置默认网关)。

实现剖析

源码都在 https://github.com/MageekChiu/xdp4slb。欢送大家提出缺点和倡议!

外围框架如下:

if (dest_ip = vip && dest_port = vport){    ingress,包来源于 client,要转发给 rs        筛选本地一个可用的 port1-ip1 作为新包的 src    应用负载平衡算法筛选一个 rs,并将其 port2-ip2 作为新包的 dst    相应的批改 src mac 和 dst mac        此外保留 client 的 port3-ip3 和 port1-ip1 的双向映射关系    便于后续 ingress 和 egress 应用}else{    egress,包来源于 rs, 要转发给 client    依据包的 dst 找到 port1-ip1    依据 ingress 外面的映射找到对应的 client 的 port3-ip3 作为新包的 dst    应用 vip 和 vport 作为新包的 src    相应的批改 src mac 和 dst mac }从新计算校验和应用 XDP_TX 将包从本网卡从新扔回去

这外面还有些校验细节就不讲了,大家能够间接看代码

本地测试

开发实现后,能够先在本地进行编译和load,以提前裸露问题,没问题后,在将指标文件放到容器里进行测试

# CORE, if you want to include vmlinux.hbpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h# local compile and testrm -f /sys/fs/bpf/slb \    && rm -f slb.bpf.o \    && clang -target bpf -g -O2 -c slb.bpf.c -o slb.bpf.o \    && bpftool prog load slb.bpf.o /sys/fs/bpf/slb \    && ll /sys/fs/bpf/slb \    # for testing, you can cp newly compiled object to container    docker cp slb.bpf.o slb:/tmp/

部署和配置SLB

Dockerfile 如下

FROM debian:bullseye# modify source to get faster installationRUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list \    && apt-get update -y && apt-get upgrade -y \    && apt install -y procps bpftool iproute2 net-tools telnet kmod curl tcpdumpWORKDIR /tmp/COPY slb.bpf.o /tmp/

构建镜像并运行

docker build -t mageek/slb:0.1 .docker run -itd --name slb --hostname slb --privileged=true --net south --ip 172.19.0.5 --mac-address="02:42:ac:13:00:05" mageek/slb:0.1

进入容器加载 xdp 指标文件

docker exec -it slb bash# 在SLB中启用VIP# reuse mac addr from slb ip# ifconfig eth0:0 172.19.0.10/32 up# add new mac for vipifconfig eth0:0 172.19.0.10/32 hw ether 02:42:ac:13:00:10 up# to delete# ifconfig eth0:0 downbpftool net detach xdpgeneric dev eth0rm /sys/fs/bpf/slbbpftool prog load slb.bpf.o /sys/fs/bpf/slb# ls -l  /sys/fs/bpfbpftool prog list# bpftool prog show name xdp_lb  --pretty# bpftool net attach xdpgeneric name xdp_lb  dev eth0# orbpftool net attach xdpgeneric id 211 dev eth0# check with ip linkcat /sys/kernel/debug/tracing/trace_pipe# better use code bellowbpftool prog tracelog# won't get any result, cause the packets haven't got theretcpdump host 172.19.0.10

留神,尽管官网文档上说,attach xdp 会本人抉择适合的模式,然而咱们在虚构网卡上面,只能抉择 attach xdpgeneric,前者不会失效,预计是个bug。

测试

新起一个client容器

docker run -itd --name client --hostname client --privileged=true --net south -p 10000:80 --ip 172.19.0.9 --mac-address="02:42:ac:13:00:09" nginx:stable

进入 client

docker exec -it client bash# visit rs firstcurl 172.19.0.2:80curl 172.19.0.3:80# visit slb curl 172.19.0.10:80rs-1curl 172.19.0.10:80rs-2curl 172.19.0.10:80rs-1curl 172.19.0.10:80rs-2

可见的确实现了 round_robin 算法。

限度

TCP的负载平衡是比较复杂的,还有各种条件须要思考,比方:多实例 SLB 之间的状态同步、conntrack 条目标回收、端口主动治理、arp动静解决等等。残缺的实现是非常复杂和体系化的,本文作为一个简略的实现,目标是体验ebpf/xdp,生产级别的实现请自行实现(工作量较大)或参考社区已有版本(尽管不多)。

参考

  • https://github.com/torvalds/linux/blob/master/net/netfilter/nf_nat_core.c#L504
  • https://github.com/lizrice/lb-from-scratch/blob/main/README.MD
  • https://github.com/xdp-project/xdp-tutorial
  • https://blog.csdn.net/hbhgyu/article/details/109600180
  • https://lists.iovisor.org/g/iovisor-dev/topic/30315706
  • https://github.com/iovisor/bcc/issues/2463
  • https://github.com/facebookincubator/katran/blob/master/katran/lib/bpf/balancer_helpers.h
  • https://man.archlinux.org/man/bpftool-net.8.en
  • https://stackoverflow.com/questions/75849176/why-my-xdp-program-with-xdp-tx-not-working
  • https://www.kernel.org/doc/html/latest/core-api/printk-format...

下文预报

本文采纳了 bpftool 来手动加载 eBPF 程序,并且 VIP 和 RIP 都是 hard code。前面能够应用 libbpf 来反对 eBPF 的程序化加载和 VIP 配置。

另,本文体验了 xdp 如何替换 LVS 实现负载平衡性能,然而并没有充分体现 xdp 的劣势,下回将剖析 xdp 的真正劣势场景:间接部署在一般服务器上,去掉专用的 LVS 服务器。