主题
负载均衡映射
静态映射是一对一:一个公网端口对应一台内网服务器。 这一篇讲一对多 —— 同一个公网端口,按权重分流到一组后端。
它适合什么、不适合什么
先把预期摆正。这不是要取代 LVS、HAProxy 或云上的四层负载均衡器:
text
✓ 适合 ✗ 不适合
────────────────────────────────── ────────────────────────────────
两三台后端,想省一台设备 需要健康检查(后端挂了自动摘除)
后端数量稳定、很少变 后端频繁上下线
已经有 VPP 在做网关,顺手加个分流 需要按 URL / Header 分流(那是七层)
对 pps 有要求,不想多一跳 需要连接数均衡而不是哈希均衡最关键的短板是没有健康检查。 VPP 只按权重分流,不会探测后端死活。 后端宕了,落到它身上的连接就是失败 —— 这一点必须在选型时想清楚。
配置
应用配置
bash
./labctl reset base
./labctl apply base 06bash
vppctl nat44 add load-balancing static mapping \
protocol tcp external 203.0.113.1:8080 \
local 10.10.0.11:9000 probability 50 \
local 10.10.0.12:9000 probability 50所有后端必须写在同一条命令里
只给一个 local 会被直接拒绝:
text
nat44 add load-balancing static mapping: at least two local must be set看上去 VPP 还提供了一个单独加后端的子命令:
bash
nat44 add load-balancing back-end protocol tcp \
external 203.0.113.1:8080 local 10.10.0.12:9001 probability 25但在 VPP 26.06 上,无论加还是删、带不带 vrf 0,它都返回:
text
nat44 add load-balancing back-end: Mapping or back-end not exist.即使那条映射明明就在 show nat44 static mappings 里。所以实际操作时, 改后端列表的唯一可靠方式是整条映射删掉重建:
bash
vppctl nat44 add load-balancing static mapping <原参数> del
vppctl nat44 add load-balancing static mapping <新参数>这意味着调整后端会打断已有会话,做变更时要选时间窗口。
映射建好后:
bash
./labctl vppctl base show nat44 static mappingstext
NAT44 static mappings:
TCP external 203.0.113.1:8080
local 10.10.0.11:9000 vrf 0 probability 50
local 10.10.0.12:9000 vrf 0 probability 50验证分流
跑自检
bash
./labctl verify base 06自检连 20 次,靠「你是谁」服务回话里的主机名判断落到了谁身上。
text
── 2) 连 20 次,看分流 ──────────────────────
9 h1
11 h2
✓ 两个后端都收到了流量,分流生效分布不均是正常的,样本量要够
这里有个很容易误判的地方。后端是按报文五元组哈希选的,不是轮询。 样本少的时候偏差会大得吓人 —— 实测数据:
text
连接次数 h1 h2 偏差
──────── ──── ──── ──────────────
12 10 2 严重偏斜
20 9 11 接近了
20 15 5 又偏了
60 32 28 基本收敛如果你上线后连了十几次发现"全落在一台上",先别急着报 bug, 把样本量加到几十上百次再看。
权重不是精确配额
probability 50 / 50 表达的是期望,不是保证。哈希决定了每一条具体的流去哪, 只有在流足够多时才会逼近设定的比例。
想要精确的连接数均衡,需要的是有状态的负载均衡器,不是哈希分流。
affinity:让同一个客户端粘住同一个后端
很多应用有会话状态(购物车、登录态、WebSocket),要求同一个客户端的多次连接 落到同一台后端。加 affinity <秒数>:
bash
vppctl nat44 add load-balancing static mapping \
protocol tcp external 203.0.113.1:8080 \
local 10.10.0.11:9000 probability 50 \
local 10.10.0.12:9000 probability 50 \
affinity 60效果非常明显。同样连 20 次:
text
── 3) 打开 affinity,看会话保持 ──────────────
affinity 60:同一个客户端在 60 秒内粘在同一个后端上
20 h1
✓ 20 次全部落在同一个后端 —— affinity 生效
── 4) 关掉 affinity,确认能切回去 ─────────────
15 h1
5 h2text
┌──────────────┬──────────────────────────┐
│ 配置 │ 20 次连接的落点 │
├──────────────┼──────────────────────────┤
│ 无 affinity │ 9 h1 11 h2 │
│ affinity 60 │ 20 h1 │
│ 再关掉 │ 15 h1 5 h2 │
└──────────────┴──────────────────────────┘affinity 的代价
粘性和均衡是矛盾的。affinity 打开后:
- 均衡度下降 —— 大客户后面藏着几千个用户时,这几千个全压在一台后端上
- 后端故障影响面变大 —— 粘在故障后端的客户端会持续失败,直到 affinity 超时
- 超时时间要权衡 —— 太短起不到保持作用,太长则故障恢复慢
选值时想清楚"我要保持多久":Web 会话通常几分钟够了, 没必要设成一小时。
其他可用选项
负载均衡映射支持和普通静态映射一样的修饰符:
bash
nat44 add load-balancing static mapping protocol tcp|udp \
external <addr>:<port> \
local <addr>:<port> [vrf <table-id>] probability <n> \
[local ... probability ...]... \
[twice-nat|self-twice-nat] \
[out2in-only] \
[affinity <timeout-seconds>] \
[del]| 选项 | 用途 |
|---|---|
twice-nat | 源地址也改写,见下一篇。后端路由不指向 NAT 时必需 |
out2in-only | 只对入方向生效,后端主动出去时不用这条映射 |
vrf <id> | 后端在指定 VRF 里,多租户场景 |
后端权重不必相同
probability 是相对权重,不必加起来等于 100:
bash
local 10.10.0.11:9000 probability 70 \
local 10.10.0.12:9000 probability 30新机器性能强就给高一点。灰度发布时也可以先给新版本 5,观察没问题再调大 —— 但记得每次调整都要删掉重建,会断已有连接。
排错
全落在一台后端上
先加大样本量到 60 次以上。仍然全在一台,检查是不是不小心带了 affinity:
bash
./labctl vppctl base show nat44 static mappings某台后端完全收不到流量
映射里的地址端口写对了吗?后端服务真的在监听吗?先在后端本机自测:
bash
ssh chenzhong@192.168.49.182 \
'docker exec clab-natbase-h2 nc -w 2 127.0.0.1 9000'改了后端列表但不生效
back-end 子命令在 26.06 上不可用(见上文)。必须整条删掉重建。 确认一下你的 del 命令参数和当初 add 的完全一致 —— 参数对不上就删不掉。
小结
| 命令 | nat44 add load-balancing static mapping protocol tcp external A:P local B:Q probability N local ... |
| 后端数 | 至少两个,且必须写在同一条命令里 |
| 分流方式 | 五元组哈希,probability 是期望权重不是精确配额 |
| 会话保持 | affinity <秒>,代价是均衡度下降、故障影响面变大 |
| 改后端 | 只能整条删掉重建,会断已有会话 |
| 最大短板 | 没有健康检查,后端挂了不会自动摘除 |
下一篇:twice-NAT —— 源和目的一起改,以及它治不了的发夹弯。