一、痛点:传统下线为何总“伤筋动骨”?
作为一名LVS老兵,你必定经历过这样的深夜告警:
# 场景1:直接移除RS导致流量中断
ipvsadm -d -t 192.168.1.100:80 -r 10.0.0.1:80
# 瞬间该服务器的所有连接被强制断开
# 场景2:粗暴摘流引发业务抖动
# 运维执行:
echo 1 > /proc/sys/net/ipv4/vs/expire_nodest_conn
# 结果:用户会话突然失效,购物车清空,支付失败...
传统方案三大罪:
- 硬切断:直接删除RS,活跃连接立即中断
- 零过渡:缺乏流量递减过程,业务监控突现毛刺
- 手动风险:依赖人工操作,易误伤健康实例
二、weight原理:LVS的“流量调节阀”
核心机制揭秘
// LVS调度器核心逻辑简化版
struct ip_vs_dest {
int weight; // 权重值:0-255
int activeconns; // 当前活跃连接数
int weight_r; // 运行时权重(动态计算)
};
// 加权最少连接算法(wlc)关键计算
static inline int ip_vs_dest_conn_overhead(struct ip_vs_dest *dest)
{
// 核心公式:连接负载率 = 活动连接数 / 权重
return (dest->activeconns << 8) / dest->weight;
}
权重工作流程:
流量分配比例 = 当前RS权重 / 所有RS权重总和
示例:
RS1 weight=100, RS2 weight=100, RS3 weight=50
则:
RS1接收流量 = 100/(100+100+50) = 40%
RS2接收流量 = 40%
RS3接收流量 = 20%
三、四步实现优雅下线实战
场景:三台Web服务器,需下线RS3(10.0.0.3)
初始状态配置:
# 查看当前配置
ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.1.100:80 wlc
-> 10.0.0.1:80 Route 100 350 1200
-> 10.0.0.2:80 Route 100 320 1150
-> 10.0.0.3:80 Route 100 340 1180
第一步:预热通知(提前5分钟)
# 通知监控系统开始摘流
curl -X POST http://monitor/api/drain-start
-d '{"host":"10.0.0.3","type":"graceful"}'
# 标记服务器进入维护状态
etcdctl put /lvs/rs/10.0.0.3/status '{"weight":100,"draining":true}'
第二步:逐步降权(关键步骤)
# 1. 首次降权至50(立即生效)
ipvsadm -e -t 192.168.1.100:80 -r 10.0.0.3:80 -w 50
# 等待90秒,让调度器感知新权重
sleep 90
# 2. 再次降权至20
ipvsadm -e -t 192.168.1.100:80 -r 10.0.0.3:80 -w 20
sleep 120
# 3. 最终降权至1(最小流量)
ipvsadm -e -t 192.168.1.100:80 -r 10.0.0.3:80 -w 1
sleep 180
# 实时监控流量变化
watch -n 1 'ipvsadm -ln | grep 10.0.0.3'
监控指标验证:
# 确认新连接数趋近于0
while true; do
NEW_CONN=$(netstat -an | grep ':80 ' | grep ESTAB | wc -l)
ACTIVE_CONN=$(ipvsadm -ln | grep 10.0.0.3 | awk '{print $5}')
echo "新连接: $NEW_CONN, 活跃连接: $ACTIVE_CONN"
if [ $ACTIVE_CONN -lt 5 ]; then
echo "可安全下线"
break
fi
sleep 10
done
第三步:零流量确认
# 使用tcpdump验证无新请求
timeout 60 tcpdump -i eth0 host 10.0.0.3 and port 80
| tee /tmp/traffic.log
| wc -l
# 检查LVS统计(确保InActConn不再增长)
ipvsadm -ln --stats
第四步:安全移除
# 此时活跃连接已处理完毕
ipvsadm -d -t 192.168.1.100:80 -r 10.0.0.3:80
# 通知监控系统下线完成
curl -X POST http://monitor/api/drain-end
-d '{"host":"10.0.0.3","status":"completed"}'
四、自动化脚本:一键优雅下线
#!/usr/bin/env python3
# graceful_drain.py
import subprocess
import time
import json
class LVSGracefulDrain:
def __init__(self, vip, vport, rs_ip, rs_port=80):
self.vip = vip
self.vport = vport
self.rs_ip = rs_ip
self.rs_port = rs_port
def get_current_weight(self):
"""获取当前权重"""
cmd = f"ipvsadm -ln | grep {self.rs_ip}"
output = subprocess.getoutput(cmd)
if output:
return int(output.split()[3])
return None
def set_weight(self, weight):
"""动态设置权重"""
cmd = (
f"ipvsadm -e -t {self.vip}:{self.vport} "
f"-r {self.rs_ip}:{self.rs_port} -w {weight}"
)
subprocess.run(cmd, shell=True, check=True)
print(f"[{time.ctime()}] 设置 {self.rs_ip} 权重为 {weight}")
def drain(self):
"""执行优雅下线流程"""
weights = [50, 20, 5, 1] # 递减权重
wait_times = [90, 120, 180, 300] # 等待时间
for i, (weight, wait) in enumerate(zip(weights, wait_times)):
self.set_weight(weight)
# 检查活跃连接
active_conn = self.get_active_connections()
print(f"当前活跃连接数: {active_conn}")
if i == len(weights) - 1: # 最后一次等待
while active_conn > 0:
print(f"等待剩余 {active_conn} 个连接...")
time.sleep(30)
active_conn = self.get_active_connections()
time.sleep(wait)
# 移除RS
cmd = (
f"ipvsadm -d -t {self.vip}:{self.vport} "
f"-r {self.rs_ip}:{self.rs_port}"
)
subprocess.run(cmd, shell=True, check=True)
print(f"[{time.ctime()}] {self.rs_ip} 已安全移除")
def get_active_connections(self):
"""获取活跃连接数"""
cmd = f"ipvsadm -ln | grep {self.rs_ip}"
output = subprocess.getoutput(cmd)
if output:
return int(output.split()[4])
return 0
# 使用示例
if __name__ == "__main__":
drainer = LVSGracefulDrain(
vip="192.168.1.100",
vport=80,
rs_ip="10.0.0.3"
)
drainer.drain()
五、新旧方案对比实验
实验环境:
压测工具:wrk,1000并发持续压测
监控指标:QPS、错误率、响应时间P99
服务器:3台Nginx(RS1/RS2/RS3)
方案对比结果:
|
指标 |
传统方案(直接删除) |
weight递减方案 |
|
错误率峰值 |
12.8% |
0.03% |
|
P99延迟波动 |
+450ms |
+18ms |
|
完全下线时间 |
立即 |
8-12分钟 |
|
活跃连接处理 |
强制中断 |
自然结束 |
|
业务影响 |
明显感知 |
无感知 |
|
回滚难度 |
需要重启服务 |
秒级恢复权重 |
真实监控数据对比:
// 传统方案(灾难现场)
{
"timestamp": "2024-01-15T03:00:00Z",
"errors": 1280,
"error_rate": "12.8%",
"alerts": ["high_error_rate", "connection_reset"]
}
// weight方案(平稳过渡)
{
"timestamp": "2024-01-15T03:00:00Z",
"errors": 3,
"error_rate": "0.03%",
"alerts": []
}
六、高级技巧与注意事项
1. 权重计算优化公式
def calculate_optimal_weights(current_weights, rs_to_remove):
"""
智能权重调整算法
:param current_weights: 当前各RS权重列表 [100, 100, 100]
:param rs_to_remove: 待下线RS索引(从0开始)
:return: 调整后的权重列表
"""
total_weight = sum(current_weights)
removed_weight = current_weights[rs_to_remove]
# 将下线RS的权重按比例分配给其他RS
remaining_rs = [i for i in range(len(current_weights)) if i != rs_to_remove]
new_weights = current_weights.copy()
for i in remaining_rs:
proportion = current_weights[i] / (total_weight - removed_weight)
new_weights[i] = int(current_weights[i] + proportion * removed_weight)
new_weights[rs_to_remove] = 0 # 标记为待移除
return new_weights
# 示例:下线第三台服务器
print(calculate_optimal_weights([100, 100, 100], 2))
# 输出:[150, 150, 0] # 流量完美重分配
2. 健康检查集成
# Nginx upstream配置示例
upstream backend {
server 10.0.0.1:80 weight=100 max_fails=3 fail_timeout=30s;
server 10.0.0.2:80 weight=100 max_fails=3 fail_timeout=30s;
server 10.0.0.3:80 weight=1 max_fails=3 fail_timeout=30s;
# 配合LVS的优雅下线
check interval=5000 rise=2 fall=3 timeout=1000;
check_http_send "HEAD /health HTTP/1.0
";
check_http_expect_alive http_2xx http_3xx;
}
3. 容器化环境适配(K8s + LVS)
# Kubernetes ConfigMap for keepalived
apiVersion: v1
kind: ConfigMap
metadata:
name: lvs-config
data:
keepalived.conf: |
virtual_server 192.168.1.100 80 {
delay_loop 6
lb_algo wlc
lb_kind DR
protocol TCP
real_server 10.0.0.1 80 {
weight 100
TCP_CHECK {
connect_timeout 3
}
}
real_server 10.0.0.2 80 {
weight 100
TCP_CHECK {
connect_timeout 3
}
}
real_server 10.0.0.3 80 {
weight 1 # 优雅下线中
TCP_CHECK {
connect_timeout 3
}
}
}
七、专家经验总结
黄金法则:
- 永远不要直接删除RS:先降权至1,等待连接耗尽
- 监控先行:确保有完整的连接数监控告警
- 渐进式调整:权重递减需有合理时间间隔
- 自动化一切:手动操作是故障的根源
排错指南:
# 1. 查看实时流量分布
watch -n 1 'ipvsadm -ln --rate'
# 2. 跟踪连接状态变化
ipvsadm -ln --timeout
# 输出:TCP 01:20 FIN_WAIT 00:30
# 3. 紧急恢复命令(发现异常时)
# 立即恢复权重并暂停下线
ipvsadm -e -t VIP:PORT -r RS_IP:PORT -w 100
性能指标参考:
- 安全阈值:活跃连接 < 5 时方可移除
- 时间窗口:每降权一次至少等待90秒(TCP超时思考)
- 权重梯度:推荐 100 → 50 → 20 → 5 → 1 → 0
结语
巧用weight实现无损下线,看似简单的权重调整,实则是LVS运维艺术的核心体现。这套方案已在多个万级QPS生产环境中验证,实现了全年计划内维护零故障的目标。
记住:好的运维不是让系统永不宕机,而是让变更对用户透明。weight方案正是这种理念的完美实践——让每一次下线都如细雨般无声,如呼吸般自然。
致每一位LVS守护者: 我们处理的不是流量,是用户的信任;我们调整的不是权重,是服务的承诺。
本文源自数十次真实生产环境迭代经验,适用于LVS/Keepalived全版本。实践前请在测试环境验证。如有疑问,欢迎在评论区深度交流。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
真不戳💪
受益匪浅👏
向你学习👍
好棒👏