0%

学习 loxilb(1):本地构建与测试

最近又在研究一个 ebpf 的项目 loxilb,它是一个云原生负载均衡器,使用 Golang/ebpf 实现,可以满足不同环境下(包括本地、公有云或 k8s 混合环境)的负载均衡需求。

首先我们本地构建 loxilb 项目,然后再对 loxilb 进行简单功能测试,以对 loxilb 有一个快速了解。

构建 loxilb

首先我们需要从 loxilb 仓库下载源码,因为 loxilb 的 ebpf 数据平面实现是在 loxilb-ebpf 这个子模块中,所以需要使用 --recursive 下载完整的代码:

1
2
# git clone --recursive https://github.com/loxilb-io/loxilb.git loxilb-debug
# cd loxilb-debug

loxilb 也是通过 github workflow 来进行持续集成(CI)的,同时 loxilb 源码目录下的 cicd/ 子目录中提供了大量自动化测试脚本,每次代码变更都会触发 CI 流量来运行这些测试脚本。所以熟悉 cicd/ 目录下的测试脚本,也可以帮助我们快速了解、测试 loxilb 的功能。

为了在我本地 Ubuntu 22.04 机器上构建并运行 loxilb,我直接参考 loxilb 目录下的 .github/workflows/tcp-sanity-ubuntu-22.yml 工作流,这样就可以让我的本地测试环境尽可能与官方 CI 环境保持一致。

  • 安装 Python 和 Golang

根据 workflow 文件,在 check out 代码后,会安装 Python 和 Golang 环境,Golang 版本需要在 1.18 以上。

1
2
3
4
5
6
7
- uses: actions/checkout@v2
with:
submodules: recursive
- uses: actions/setup-python@v2
- uses: actions/setup-go@v3
with:
go-version: '>=1.18.0'

由于我本地已经安装 Python 和 Golang,这一步就跳过:

1
2
3
4
# python --version
Python 3.10.12
# go version
go version go1.22.0 linux/amd64
  • 安装其他依赖组件
1
2
# apt-get update
# apt-get -y install clang llvm libelf-dev gcc-multilib libpcap-dev linux-tools-$(uname -r) elfutils dwarves git libbsd-dev bridge-utils unzip build-essential bison flex iperf iproute2 nodejs socat ethtool
  • 构建并安装 loxilb 自己的 tc,称为 ntc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# cd loxilb-debug

# 安装 libbpf
# git clone --recurse-submodules https://github.com/loxilb-io/iproute2 iproute2-main
# cd iproute2-main/libbpf/src/
# make install
# mkdir build
# DESTDIR=build OBJDIR=build make install


# 构建 iproute2 中的 tc
# cd -
# cd iproute2-main/
# export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:`pwd`/libbpf/src/ && LIBBPF_FORCE=on LIBBPF_DIR=`pwd`/libbpf/src/build ./configure && make && sudo cp -f tc/tc /usr/local/sbin/ntc && cd -
  • 挂载好 loxilb 使用的 bpf 文件系统
1
# loxilb-ebpf/utils/mkllb_bpffs.sh
1
2
# mount -l | grep loxilb
bpf on /opt/loxilb/dp type bpf (rw,relatime)
  • 构建 loxilb 并执行测试
1
2
env "PATH=$PATH" make
env "PATH=$PATH" make test
  • 基于生成的交付件,生成适用于本地环境的 docker 镜像,并对镜像打上 tag latestu22
1
2
3
4
5
6
# docker pull ghcr.io/loxilb-io/loxilb:latest
# docker run -u root --cap-add SYS_ADMIN --restart unless-stopped --privileged -dit -v /dev/log:/dev/log --name loxilb ghcr.io/loxilb-io/loxilb:latestu22
# pwd && ls && sudo -E env "PATH=$PATH" make docker-cp-ebpf
# docker exec -dit loxilb mkllb_bpffs
# id=`docker ps -f name=loxilb | cut -d " " -f 1 | grep -iv "CONTAINER"` && docker commit $id ghcr.io/loxilb-io/loxilb:latest
# docker stop loxilb && docker rm loxilb && docker image tag ghcr.io/loxilb-io/loxilb:latest ghcr.io/loxilb-io/loxilb:latestu22

这样我们就在本地构建好了 loxlilb 镜像,之后所有的测试都将基于 docker 镜像进行测试。

1
2
3
# docker images | grep loxilb
ghcr.io/loxilb-io/loxilb latest dec558036925 2 hours ago 316MB
ghcr.io/loxilb-io/loxilb latestu22 dec558036925 2 hours ago 316MB

四层 LB 基础功能测试

接下来我们就使用这个构建好的 loxilb 镜像来测试最基本的四层负载均衡功能。继续看 .github/workflows/tcp-sanity-ubuntu-22.yml 这个工作流,它在完成 loxilb 进行构建后,就开始执行 cicd/ 目录下的一系列测试脚本,这个 workflow 一共执行 cicd/tcplb/cicd/tcplbmark/cicd/tcplbdsr1/cicd/tcplbdsr2/cicd/tcplbl3dsr/cicd/tcplbhash/ 这几个测试 case,每个测试 case 都是按照如下步骤进行:

1
2
3
4
5
6
7
8
# 搭建测试环境
# ./config.sh

# 验证功能
# ./validation.sh

# 清理测试环境
# ./rmconfig.sh

接下来我们就手动执行 cicd/tcplb/ 目录下的测试流程,看看是否能够正常执行。

搭建测试环境

首先执行 .config.sh 脚本搭建测试环境:

1
2
# cd cicd/tcplb
./config.sh

.config.sh 脚本主要完成了以下工作:

  • 创建一个作为负载均衡器的 loxilb 容器,名为 llb1
  • 创建一个作为 client 的 host 容器,名为 l3h1
  • 创建三个作为 endpoint 的 host 容器,名为 l3ep1l3ep2l3ep3
  • 另外,该脚本还会将每个容器的 netns mount 到 /var/run/netns 目录下,这样后续可以直接在宿主机上通过 ip netns 命令操作不同容器的网络配置(学到了~~)。这段脚本代码如下:
1
2
3
4
5
6
7
8
9
# dname 表示容器名称
get_docker_pid $dname
echo $pid
if [ ! -f "$hexist/$dname" -a "$pid" != "" ]; then
sudo mkdir -p /var/run/netns
sudo touch /var/run/netns/$dname
#echo "sudo mount -o bind /proc/$pid/ns/net /var/run/netns/$2"
sudo mount -o bind /proc/$pid/ns/net /var/run/netns/$dname
fi
  • 通过 veth pairl3h1llb1llb1l3ep1llb1l3ep2llb1l3ep3 连接起来,并配置 IP 地址,实现 L3 互通
  • llb1 容器中执行 loxicmd 命令,添加 LB 规则,总共添加了如下 3 条 LB 规则
1
2
3
create_lb_rule llb1 20.20.20.1 --tcp=2020:8080 --endpoints=31.31.31.1:1,32.32.32.1:1,33.33.33.1:1
create_lb_rule llb1 10.10.10.254 --tcp=2020:8080 --endpoints=31.31.31.1:1,32.32.32.1:1,33.33.33.1:1
create_lb_rule llb1 10.10.10.3 --tcp=2020:8080 --endpoints=31.31.31.1:1,32.32.32.1:1,33.33.33.1:1

经过上述脚本后,就搭建了如下网络拓扑,并且添加了 3 条 LB 转发规则:

我们可以实际检查下这些配置的确生效:

1
2
3
4
# docker exec -it l3h1 ip -br addr show
lo UNKNOWN 127.0.0.1/8
el3h1llb1@if6 UP 10.10.10.1/24
eth0@if66 UP 172.17.0.6/16
1
2
3
4
5
6
7
8
# docker exec -it llb1 ip -br addr show
lo UNKNOWN 127.0.0.1/8 10.10.10.3/32
llb0 UNKNOWN
ellb1l3ep1@if2 UP 31.31.31.254/24
ellb1l3ep2@if2 UP 32.32.32.254/24
ellb1l3ep3@if2 UP 33.33.33.254/24
ellb1l3h1@if2 UP 10.10.10.254/24
eth0@if58 UP 172.17.0.2/16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# docker exec -it l3ep1 ip -br addr show
lo UNKNOWN 127.0.0.1/8
el3ep1llb1@if3 UP 31.31.31.1/24
eth0@if60 UP 172.17.0.3/16

# docker exec -it l3ep2 ip -br addr show
lo UNKNOWN 127.0.0.1/8
el3ep2llb1@if4 UP 32.32.32.1/24
eth0@if62 UP 172.17.0.4/16

# docker exec -it l3ep3 ip -br addr show
lo UNKNOWN 127.0.0.1/8
el3ep3llb1@if5 UP 33.33.33.1/24
eth0@if64 UP 172.17.0.5/16

loxilb 容器中执行 loxicmd 命令,可以查看 LB 相关配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# docker exec -it loxilb /bin/bash

# loxicmd get loadbalancer
| EXT IP | PORT | PROTO | NAME | MARK | SEL | MODE | # OF ENDPOINTS | MONITOR |
|--------------|------|-------|------|------|-----|---------|----------------|---------|
| 10.10.10.254 | 2020 | tcp | | 0 | rr | default | 3 | Off |
| 10.10.10.3 | 2020 | tcp | | 0 | rr | default | 3 | Off |
| 20.20.20.1 | 2020 | tcp | | 0 | rr | default | 3 | Off |


# loxicmd get endpoint
| HOST | NAME | PTYPE | PORT | DURATION | RETRIES | MINDELAY | AVGDELAY | MAXDELAY | STATE |
|------------|---------------------|-------|------|----------|---------|----------|----------|----------|-------|
| 31.31.31.1 | 31.31.31.1_tcp_8080 | none: | 8080 | 0 | 0 | | | | ok |
| 32.32.32.1 | 32.32.32.1_tcp_8080 | none: | 8080 | 0 | 0 | | | | ok |
| 33.33.33.1 | 33.33.33.1_tcp_8080 | none: | 8080 | 0 | 0 | | | | ok |

运行测试

接下来运行该测试流程中的 验证脚本,即 validation.sh,它首先分别在三个 endpoint 容器中启动 HTTP 服务,不同 endpoint 容器里的 HTTP 服务会返回不同的字符串,这样方便我们判断是哪个 endpoint 容器处理了 HTTP 请求。

其实 validation.sh 脚本的主要工作就是访问该 LB 提供的三个 VIP,并检查每次访问 LB 的 VIP 时,LB 都是按照 RR 轮询算法将请求转发到不同的 endpoint 容器。我们也可以手动完成这个测试:

  • 首先在三个 endpoint 容器中启动 HTTP 服务
1
2
3
4
5
hexec="sudo ip netns exec "

$hexec l3ep1 node ../common/tcp_server.js server1 &
$hexec l3ep2 node ../common/tcp_server.js server2 &
$hexec l3ep3 node ../common/tcp_server.js server3 &
  • l3h1 中通过 curl 访问 LB 的 VIP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# $hexec l3h1 curl --max-time 10  10.10.10.254:2020
server1
# $hexec l3h1 curl --max-time 10 10.10.10.254:2020
server2
# $hexec l3h1 curl --max-time 10 10.10.10.254:2020
server3

# $hexec l3h1 curl --max-time 10 10.10.10.3:2020
server1
# $hexec l3h1 curl --max-time 10 10.10.10.3:2020
server2
# $hexec l3h1 curl --max-time 10 10.10.10.3:2020
server3

# $hexec l3h1 curl --max-time 10 20.20.20.1:2020
server1
# $hexec l3h1 curl --max-time 10 20.20.20.1:2020
server2
# $hexec l3h1 curl --max-time 10 20.20.20.1:2020
server3

这里说明一下为什么能够从 l3h1 访问 20.20.20.1 这个 VIP,因为 l3h1 里默认路由就是通过 llb1 转发:

1
2
3
4
# $hexec l3h1 ip route
default via 10.10.10.254 dev el3h1llb1
10.10.10.0/24 dev el3h1llb1 proto kernel scope link src 10.10.10.1
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.6

抓包分析

当我们在 l3h1 容器通过curl 访问 20.20.20.1.2020
我们在 llb1 容器内对流量进行抓包,看下 llb1 到底是怎么对四层流量进行负载分担的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# $hexec llb1 tcpdump -i any -n -l
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
# 收到 l3h1 的 SYN 包
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
18:37:10.337301 ellb1l3h1 In IP 10.10.10.1.38148 > 20.20.20.1.2020: Flags [S], seq 4098335908, win 62720, options [mss 8960,sackOK,TS val 3345845189 ecr 0,nop,wscale 7], length 0

# llb1 通过 DNAT,将目的 IP 从 20.20.20.1 改为 33.33.33.1,目的 Port 从 2020 改为 8080
18:37:10.337323 ellb1l3ep3 Out IP 10.10.10.1.38148 > 33.33.33.1.8080: Flags [S], seq
4098335908, win 62720, options [mss 8960,sackOK,TS val 3345845189 ecr 0,nop,wscale 7], length 0

# 收到 l3ep3 的 SYN/ACK 包
18:37:10.337336 ellb1l3ep3 In IP 33.33.33.1.8080 > 10.10.10.1.38148: Flags [S.], seq 576370836, ack 4098335909, win 62636, options [mss 8960,sackOK,TS val 3915933462 ecr 3345845189,nop,wscale 7], length 0

# llb1 将 SYN/ACK 回包的源 IP 从 33.33.33.1 改为 20.20.20.1,源 Port 从 8080 改为 2020
18:37:10.337344 ellb1l3h1 Out IP 20.20.20.1.2020 > 10.10.10.1.38148: Flags [S.], seq 576370836, ack 4098335909, win 62636, options [mss 8960,sackOK,TS val 3915933462 ecr 3345845189,nop,wscale 7], length 0
......

可以看到,loxilb 默认通过 NAT 模式来实现四层负载分担。

移除测试环境

在完成测试之后,只需要运行 ./rmconfig.sh 即可完成本次测试环境的移除。

其他测试

cicd/tcplb/ 只是 tcp-sanity-ubuntu-22.yml 工作流中的第一个测试 case,接下来简单介绍下剩余测试 case 的功能:

  • cicd/tcplbmark/:测试 firewall 规则以及 LB 规则中的 mark 选项
  • cicd/tcplbdsr1/:基本的 DSR 功能测试
  • cicd/tcplbdsr2/:回包不过 LB 的 DSR 测试
  • cicd/tcplbl3dsr/:IPIP 隧道模式功能测试
  • cicd/tcplbhash/:测试 LB hash balance 算法

我们重点看这几个与 LB DSR 模式相关的测试 case。

cicd/tcplbdsr1/

在该测试 case 中,搭建了如下网络拓扑:

并且在 LB 上添加了如下 LB 规则,可以看到使用 dsr 模式,负载均衡算法使用 hash

1
$dexec llb1 loxicmd create lb 20.20.20.1 --select=hash --tcp=2020:2020 --endpoints=31.31.31.1:1,32.32.32.1:1,33.33.33.1:1 --mode=dsr

我们查看在 LB 上抓到的包:

1
2
3
4
5
6
7
8
# $hexec llb1 tcpdump -e -i ellb1l3h1 -n -l
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ellb1l3h1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# 1. 收到 l3h1 的 SYN 包,源 MAC 是 l3h1 的 el3h1llb1 Mac,目的 Mac 是 llb1 的 ellb1l3h1 Mac
12:07:34.475433 26:d9:99:b2:d4:d3 > 4a:8b:6d:bb:db:fb, ethertype IPv4 (0x0800), length 74: 10.10.10.1.55001 > 20.20.20.1.2020: Flags [S], seq 676983821, win 62720, options [mss 8960,sackOK,TS val 3408869327 ecr 0,nop,wscale 7], length 0

# 4. 转发 l3ep3 回复的 SYN/ACK 包,源 MAC 是 llb1 的 ellb1l3h1 Mac,目的 Mac 是 l3h1 的 el3h1llb1 Mac
12:07:34.475481 4a:8b:6d:bb:db:fb > 26:d9:99:b2:d4:d3, ethertype IPv4 (0x0800), length 74: 20.20.20.1.2020 > 10.10.10.1.55001: Flags [S.], seq 2169622033, ack 676983822, win 62636, options [mss 8960,sackOK,TS val 2224496444 ecr 3408869327,nop,wscale 7], length 0
1
2
3
4
5
6
7
8
9
# $hexec llb1 tcpdump -e -i ellb1l3ep3 -n -l
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ellb1l3ep3, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# 2. 修改 SYN 包的目的 Mac 为 l3ep3 的 el3ep3llb1 Mac,而源 Mac 则是 llb1 的 ellb1l3ep3 Mac
# SYN 包的目的 IP/端口均没有发生变化
12:07:34.475457 96:be:34:74:c7:f3 > 56:b7:3b:3e:cd:7b, ethertype IPv4 (0x0800), length 74: 10.10.10.1.55001 > 20.20.20.1.2020: Flags [S], seq 676983821, win 62720, options [mss 8960,sackOK,TS val 3408869327 ecr 0,nop,wscale 7], length 0

# 3. 收到 l3ep3 的 SYN/ACK 包,源 MAC 是 l3ep3 的 el3ep3llb1 Mac,目的 Mac 是 llb1 的 ellb1l3ep3 Mac
12:07:34.475472 56:b7:3b:3e:cd:7b > 96:be:34:74:c7:f3, ethertype IPv4 (0x0800), length 74: 20.20.20.1.2020 > 10.10.10.1.55001: Flags [S.], seq 2169622033, ack 676983822, win 62636, options [mss 8960,sackOK,TS val 2224496444 ecr 3408869327,nop,wscale 7], length 0

可以看到 DSR 模式下,Endpoint 都在 lo 口上配置了 VIP 20.20.20.1,llb1 在收到报文后,直接修改报文的目的 Mac,即可将报文转发到 Endpoint 上。在这个拓扑下,Endpoint 的回包也会通过 llb1 转发,而不是直接从 endpoint 回复给 l3h1。

cicd/tcplbdsr2/

上一个 case 的 DSR 模式,Endpoint 的回包是通过 llb1 转发的。而在这个 case中,Endpoint 的回包是直接回到 l3h1 上的。本 case 搭建了如下网络拓扑:

增加了如下 LB 规则:

1
$dexec llb1 loxicmd create lb 20.20.20.1 --select=hash --tcp=2020:2020 --endpoints=31.31.31.1:1,32.32.32.1:1,33.33.33.1:1 --mode=dsr

可以先看下 l3h1 和 l3ep1 的路由配置:

1
2
3
4
5
6
7
8
9
10
11
12
# $hexec l3h1 ip route
default via 10.10.10.254 dev el3h1llb1
10.10.10.0/24 dev el3h1llb1 proto kernel scope link src 10.10.10.1
11.11.11.0/24 dev vlan11 proto kernel scope link src 11.11.11.254
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3

# $hexec l3ep1 ip route
default via 31.31.31.254 dev el3ep1llb1
10.10.10.0/24 via 11.11.11.254 dev vlan11
11.11.11.0/24 dev vlan11 proto kernel scope link src 11.11.11.1
31.31.31.0/24 dev el3ep1llb1 proto kernel scope link src 31.31.31.1
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.4

在看下整个拓扑的 vlan 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# $hexec l3h1 ip -br addr show vlan11
vlan11 UP 11.11.11.254/24
# $hexec l3h1 brctl show vlan11
bridge name bridge id STP enabled interfaces
vlan11 8000.c2decb6b4135 no el3h1sw1

# $hexec sw1 brctl show vlan11
bridge name bridge id STP enabled interfaces
vlan11 8000.46f2213b1deb no esw1l3ep1
esw1l3ep2
esw1l3ep3
esw1l3h1
# $hexec l3ep1 ip -br addr show vlan11
vlan11 UP 11.11.11.1/24
# $hexec l3ep1 brctl show vlan11
bridge name bridge id STP enabled interfaces
vlan11 8000.0a96b4aa4683 no el3ep1sw1
......

从路由可以看出,l3h1 的访问 VIP 的报文是通过 el3ep1llb1 到达 LB,LB 在 DSR 模式下修改报文的目的 Mac,并将报文转发到某个 Endpoint,而根据 Endpoint 的路由来看,发往 10.10.10.0/24 网段的报文是通过 vlan11 发送,这样经由 sw1 上的 vlan bridge 二层转发后,到达 l3h1。由于收发包路径不一致,需要对 rp_filter 做特殊设置,将其设置为 0,以关闭反向路由检测。

1
2
3
4
5
$hexec l3h1 sysctl net.ipv4.conf.all.rp_filter=0 2>&1 >> /dev/null
$hexec l3h1 sysctl net.ipv4.conf.vlan11.rp_filter=0 2>&1 >> /dev/null
$hexec l3ep1 sysctl net.ipv4.conf.el3ep1llb1.rp_filter=0 2>&1 >> /dev/null
$hexec l3ep2 sysctl net.ipv4.conf.el3ep2llb1.rp_filter=0 2>&1 >> /dev/null
$hexec l3ep3 sysctl net.ipv4.conf.el3ep3llb1.rp_filter=0 2>&1 >> /dev/null

通过抓包验证以下该流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# $hexec llb1 tcpdump -e -i ellb1l3h1  -n -l tcp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ellb1l3h1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# 1. llb1 上的 ellb1l3h1 收到 l3h1 发来的 SYN 包
16:26:58.521744 e6:d3:aa:9a:f8:a2 > 02:26:41:c3:98:15, ethertype IPv4 (0x0800), length 74: 10.10.10.1.55001 > 20.20.20.1.2020: Flags [S], seq 3347038838, win 62720, options [mss 8960,sackOK,TS val 3424433373 ecr 0,nop,wscale 7], length 0


# $hexec llb1 tcpdump -e -i ellb1l3ep3 -n -l tcp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ellb1l3ep3, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# 2. llb1 上的 ellb1l3ep3 转发该 SYN 包给 l3ep3
16:26:58.521768 5a:60:8e:64:da:33 > 82:dc:87:59:bb:fe, ethertype IPv4 (0x0800), length 74: 10.10.10.1.55001 > 20.20.20.1.2020: Flags [S], seq 3347038838, win 62720, options [mss 8960,sackOK,TS val 3424433373 ecr 0,nop,wscale 7], length 0


# $hexec sw1 tcpdump -e -i vlan11 -n -l tcp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on vlan11, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# 3. sw1 上的 vlan11 网桥负责对 l3ep3 回复的 SYN/ACK 包进行二层转发
16:26:58.521789 de:aa:21:3a:4a:f3 > c2:de:cb:6b:41:35, ethertype IPv4 (0x0800), length 74: 20.20.20.1.2020 > 10.10.10.1.55001: Flags [S.], seq 544709783, ack 3347038839, win 62636, options [mss 8960,sackOK,TS val 2240060490 ecr 3424433373,nop,wscale 7], length 0

cicd/tcplbl3dsr/

这个测试 case 在 LB 上使用 L3 ipip 隧道来实现 DSR 模式,即通过 ipip 隧道将流量转发到 Endpoint 上,其实就是一般所说的 LVS 的 IP 隧道模式。测试 case 的拓扑如下:

增加了如下 LB 规则:

1
create_lb_rule llb1 20.20.20.1 --select=hash --tcp=8080:8080 --endpoints=56.56.56.1:1,57.57.57.1:1,58.58.58.1:1 --mode=dsr

查看 LB 上的配置:

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
40
41
42
43
44
45
# $dexec llb1 ip -br link show
lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
llb0 UNKNOWN 52:68:57:6d:45:ba <BROADCAST,MULTICAST,UP,LOWER_UP>
ellb1l3h1@if2 UP 12:b9:f5:99:4e:3c <BROADCAST,MULTICAST,UP,LOWER_UP>
ellb1l3ep1@if2 UP 9e:8d:30:a8:be:82 <BROADCAST,MULTICAST,UP,LOWER_UP>
ellb1l3ep2@if2 UP be:24:7e:c4:38:51 <BROADCAST,MULTICAST,UP,LOWER_UP>
ellb1l3ep3@if2 UP 96:13:7e:39:d2:c2 <BROADCAST,MULTICAST,UP,LOWER_UP>
tunl0@NONE DOWN 0.0.0.0 <NOARP>
ipip0@NONE UNKNOWN 31.31.31.254 <POINTOPOINT,NOARP,UP,LOWER_UP>
ipip1@NONE UNKNOWN 32.32.32.254 <POINTOPOINT,NOARP,UP,LOWER_UP>
ipip2@NONE UNKNOWN 33.33.33.254 <POINTOPOINT,NOARP,UP,LOWER_UP>
eth0@if112 UP 02:42:ac:11:00:02 <BROADCAST,MULTICAST,UP,LOWER_UP>

# $dexec llb1 ip -br addr show
lo UNKNOWN 127.0.0.1/8
llb0 UNKNOWN
ellb1l3h1@if2 UP 10.10.10.254/24
ellb1l3ep1@if2 UP 31.31.31.254/24
ellb1l3ep2@if2 UP 32.32.32.254/24
ellb1l3ep3@if2 UP 33.33.33.254/24
tunl0@NONE DOWN
ipip0@NONE UNKNOWN 45.45.45.254/24
ipip1@NONE UNKNOWN 46.46.46.254/24
ipip2@NONE UNKNOWN 47.47.47.254/24
eth0@if112 UP 172.17.0.2/16

# $dexec llb1 ip route
default via 172.17.0.1 dev eth0
10.10.10.0/24 dev ellb1l3h1 proto kernel scope link src 10.10.10.254
31.31.31.0/24 dev ellb1l3ep1 proto kernel scope link src 31.31.31.254
32.32.32.0/24 dev ellb1l3ep2 proto kernel scope link src 32.32.32.254
33.33.33.0/24 dev ellb1l3ep3 proto kernel scope link src 33.33.33.254
45.45.45.0/24 dev ipip0 proto kernel scope link src 45.45.45.254
46.46.46.0/24 dev ipip1 proto kernel scope link src 46.46.46.254
47.47.47.0/24 dev ipip2 proto kernel scope link src 47.47.47.254
56.56.56.0/24 via 45.45.45.1 dev ipip0
57.57.57.0/24 via 46.46.46.1 dev ipip1
58.58.58.0/24 via 47.47.47.1 dev ipip2
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2

# $hexec llb1 ip tunnel show
tunl0: any/ip remote any local any ttl inherit nopmtudisc
ipip0: any/ip remote 31.31.31.1 local 31.31.31.254 ttl inherit
ipip1: any/ip remote 32.32.32.1 local 32.32.32.254 ttl inherit
ipip2: any/ip remote 33.33.33.1 local 33.33.33.254 ttl inherit

查看某个 endpoint 上的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# $dexec l3ep1 ip -br link show
lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
el3ep1llb1@if4 UP 32:b6:d7:fd:50:de <BROADCAST,MULTICAST,UP,LOWER_UP>
tunl0@NONE DOWN 0.0.0.0 <NOARP>
ipip0@NONE UNKNOWN 31.31.31.1 <POINTOPOINT,NOARP,UP,LOWER_UP>
eth0@if116 UP 02:42:ac:11:00:04 <BROADCAST,MULTICAST,UP,LOWER_UP>

# $dexec l3ep1 ip -br addr show
lo UNKNOWN 127.0.0.1/8 56.56.56.1/32 20.20.20.1/32
el3ep1llb1@if4 UP 31.31.31.1/24
tunl0@NONE DOWN
ipip0@NONE UNKNOWN 45.45.45.1/24
eth0@if116 UP 172.17.0.4/16

# $dexec l3ep1 ip route
default via 31.31.31.254 dev el3ep1llb1
31.31.31.0/24 dev el3ep1llb1 proto kernel scope link src 31.31.31.1
45.45.45.0/24 dev ipip0 proto kernel scope link src 45.45.45.1
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.4

看下抓的报文:

1
2
3
4
5
# $hexec llb1 tcpdump -i ellb1l3h1 -n -l
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ellb1l3h1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
# llb1 上的 ellb1l3h1 收到 SYN 包
19:05:10.487705 IP 10.10.10.1.55001 > 20.20.20.1.8080: Flags [S], seq 3053784621, win 63680, options [mss 7960,sackOK,TS val 3433925339 ecr 0,nop,wscale 7], length 0

而从 LB ellb1l3ep3 接口上的抓包可以看到,该 SYN 包通过 IPIP 隧道协议发送:

Reference