网络游戏在Debian系统的香港服务器中,我如何部署并优化 LVS + Keepalived,稳住“世界频道”不断线
技术教程 2025-09-16 10:32 211


凌晨 2:17,NOC 电话把我从折叠床上叫醒:“世界频道掉线密集,活跃连接骤降 20%。”

我披着外套冲进香港机房,交换机风扇像一架小型飞机。Grafana 上,世界频道的 TCP 长连接像雪崩一样往下掉。HAProxy(七层)没顶住尖峰,切回四层 LVS 的计划不得不提前。那一刻我很清楚:要么在半小时内切换到 LVS + Keepalived,要么等日间高峰时看着世界频道继续掉线。

下面是我当夜的实施步骤、所有配置、优化项和我踩过的坑。

1. 目标架构与选择

业务特性: 世界频道是典型的长连接(WebSocket/TCP),消息量大、包小且均匀,连接对抖动异常敏感。

架构目标:

  • 四层负载(LVS/DR):极低开销转发,适合大量长连接;
  • 主备高可用(Keepalived/VRRP):主机故障秒级漂移 VIP,不感知或尽量少感知;
  • 会话保持:同一客户端优先落到同一 RealServer(尽量减少频道上下文抖动);
  • 可观测:转发/健康检查/ARP/GARP 都要有指标与日志。

LVS 模式选择:

  • 优先:LVS-DR(Direct Routing):LB 只负责入口,回包由后端直出,延迟最低,吞吐最大;前提是 LB 与 RS 同二层(同 VLAN)。
  • 备选:LVS-NAT:跨网段时使用,但会引入 LB 上的 conntrack 压力和 SNAT 性能损耗。

本文以 LVS-DR 为主线,文末附 NAT 方案差异化配置。

2. 硬件与网络规划

2.1 设备与参数

角色 数量 CPU 内存 网卡 系统盘 带宽 备注
LVS LB(主/备) 2 Xeon Silver / EPYC 7xx 64–128 GB 2×10GbE(独立上/下行) NVMe 480G 10G Debian 12 (Bookworm)
RealServer(游戏网关/频道服) 4–12 同上 64–128 GB 2×10GbE NVMe 960G 10G Debian 12

说明:双口 10GbE 能把上行(到公网/边界)和下行(到 RS 业务 VLAN)隔离,减少抢占和抖动。NVMe 对 LVS 不敏感,但对日志与指标有益。

2.2 IP 规划(示例)

名称 地址/掩码 说明
VIP(世界频道) 203.0.113.10/32 对公网暴露(BGP/静态均可)
LB1(上行) 203.0.113.101/24 出口/管理
LB2(上行) 203.0.113.102/24 出口/管理
LB1(下行) 10.10.10.11/24 至 RS VLAN
LB2(下行) 10.10.10.12/24 至 RS VLAN
RS1-4 10.10.10.21-24/24 频道服后端
网关(下行) 10.10.10.1 ToR 交换机 SVI

LVS-DR 关键点:VIP 也要配置在每台 RS 的 lo 上(/32),并关闭 ARP 响应,否则会出现 VIP 漂移与 ARP 冲突(后面有完整命令)。

3. 系统准备(Debian 12)

3.1 基础安装与内核模块

# 基础包
apt update
apt install -y ipvsadm keepalived nftables ethtool net-tools chrony jq rsyslog

# 加载 LVS 模块(持久化)
cat >/etc/modules-load.d/ipvs.conf <<'EOF'
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
EOF

modprobe ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack

3.2 时间同步

timedatectl set-timezone Asia/Hong_Kong
systemctl enable --now chrony

4. 内核网络调优(LB 与 RS)

下面参数是我在世界频道场景下的“保守增强型”基线;你可以先用它跑通,再按压测曲线微调。

4.1 LB(LVS 节点)建议 sysctl

cat >/etc/sysctl.d/99-lvs-lb.conf <<'EOF'
# 队列/缓冲
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 300000
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.rmem_default = 4194304
net.core.wmem_default = 4194304

# TCP 队列与半连接
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_syncookies = 1

# 端口范围
net.ipv4.ip_local_port_range = 1024 65535

# 路由与邻居缓存
net.ipv4.neigh.default.gc_thresh1 = 4096
net.ipv4.neigh.default.gc_thresh2 = 8192
net.ipv4.neigh.default.gc_thresh3 = 16384

# 禁止源路由过滤(VRRP/多口)
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0

# LVS-DR:LB 不负责回程,减少跟踪
net.netfilter.nf_conntrack_tcp_loose = 1
EOF

sysctl --system

网卡优化(LB):

# 关闭 LRO(低延迟场景更稳),保留 GRO 视压测而定
ethtool -K eth0 lro off
ethtool -K eth1 lro off

# 多队列收发
ethtool -L eth0 combined 8
ethtool -L eth1 combined 8

# 查看并根据 CPU 绑核(示例,具体看 /proc/interrupts)
# irqbalance 在低延迟场景可停用,改用手动绑核
systemctl stop irqbalance && systemctl disable irqbalance

4.2 RS(后端)建议 sysctl

cat >/etc/sysctl.d/99-lvs-rs.conf <<'EOF'
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 300000

net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_syncookies = 1

# 长连接更友好:尽快探测死连接
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5

# TIME-WAIT 参数在新内核已调整,无需设置已废弃选项
net.ipv4.ip_local_port_range = 1024 65535

# ARP 策略(LVS-DR 必配,避免 VIP ARP 冲突)
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.default.arp_announce = 2
EOF

sysctl --system

5. 部署步骤(LVS-DR 主线)

5.1 在每台 RS 配置 VIP(loopback)

以 VIP=203.0.113.10 为例:

# 添加 VIP 到 lo,掩码 /32,永久化
cat >/etc/systemd/network/lo_vip.netdev <<'EOF'
[NetDev]
Name=lo
Kind=loopback
EOF

cat >/etc/systemd/network/lo_vip.network <<EOF
[Match]
Name=lo

[Address]
Address=203.0.113.10/32
Label=lo:vip
EOF

systemctl enable --now systemd-networkd

也可用 ifupdown / ip 命令直接添加:

ip addr add 203.0.113.10/32 dev lo(需写入开机脚本持久化)

务必确认 ARP 策略已按上节 sysctl 配置,否则出现**“VIP 偶发被 RS 抢答 ARP”**的问题,LB 的 GARP 会被“打回”。

5.2 Keepalived(LB1 / LB2)

我们使用 VRRP 单播(很多机房屏蔽多播),并在 Keepalived 的 virtual_server 段内直接驱动 LVS(无需手写 ipvsadm)。

/etc/keepalived/keepalived.conf(LB1:MASTER)

global_defs {
  router_id LVS_HK_LB1
  enable_script_security
  script_user root
  # log 通过 rsyslog 输出
}

vrrp_instance VI_1 {
  state MASTER
  interface eth0                   # 承载 VIP 的出口口
  virtual_router_id 51
  priority 150
  advert_int 1

  # 单播(对端为 LB2 上行口 IP)
  unicast_src_ip 203.0.113.101
  unicast_peer {
    203.0.113.102
  }

  authentication {
    auth_type PASS
    auth_pass 9V9pQxk3
  }

  # GARP 加强,解决交换机缓存未更新导致短时黑洞
  garp_master_delay 1
  garp_master_repeat 5
  garp_lower_prio_repeat 3

  virtual_ipaddress {
    203.0.113.10/24 dev eth0 label eth0:vip
  }

  # 健康检查脚本(可选,用于拉低优先级触发漂移)
  track_script {
    chk_world_port
  }

  nopreempt  # 避免抖动时反复主备切换
}

vrrp_script chk_world_port {
  script "/usr/local/bin/check_world.sh"
  interval 2
  timeout 1
  rise 2
  fall 3
  weight -20
}

virtual_server 203.0.113.10 443 {
  delay_loop 3
  lb_algo sh               # 源地址哈希,天然会话保持
  lb_kind DR
  persistence_timeout 3600 # 连接保持 1h(按业务调)
  protocol TCP

  real_server 10.10.10.21 443 {
    TCP_CHECK {
      connect_timeout 3
      nb_get_retry 3
      delay_before_retry 3
    }
  }

  real_server 10.10.10.22 443 {
    TCP_CHECK {
      connect_timeout 3
      nb_get_retry 3
      delay_before_retry 3
    }
  }

  real_server 10.10.10.23 443 {
    TCP_CHECK {
      connect_timeout 3
      nb_get_retry 3
      delay_before_retry 3
    }
  }

  real_server 10.10.10.24 443 {
    TCP_CHECK {
      connect_timeout 3
      nb_get_retry 3
      delay_before_retry 3
    }
  }
}

LB2(BACKUP) 配置与 LB1 一致,仅将以下项调整:

router_id LVS_HK_LB2
state BACKUP
priority 100
unicast_src_ip 203.0.113.102
unicast_peer { 203.0.113.101 }

健康检查脚本 /usr/local/bin/check_world.sh:

#!/usr/bin/env bash
# 简单检查:至少有 1 台 RS 的 443 存活且 LVS 表项存在
set -e
timeout 1 bash -c "</dev/tcp/10.10.10.21/443" || \
timeout 1 bash -c "</dev/tcp/10.10.10.22/443" || \
timeout 1 bash -c "</dev/tcp/10.10.10.23/443" || \
timeout 1 bash -c "</dev/tcp/10.10.10.24/443" || exit 1

# LVS 表项是否已加载
ipvsadm -Ln | grep -q 'TCP  203.0.113.10:443'

chmod +x /usr/local/bin/check_world.sh
systemctl enable --now keepalived

会话保持策略:

  • 我用 lb_algo sh(源地址哈希) + persistence_timeout,既能让同一客户端稳定落到同一台 RS,又允许 RS 故障时平滑迁移。
  • 如果你的客户端在 NAT 后(如运营商 CGNAT),**mh(Maglev Hash)**也值得尝试,hash 分布更均衡。

5.3 验证 LVS 表项

ipvsadm -Ln
# 预期能看到 VIP:443 以及 4 个 RealServer
ipvsadm -Ln --stats
ipvsadm -Ln --rate

5.4 防火墙(nftables 示例)

LB 允许 VRRP、业务端口与健康检查:

cat >/etc/nftables.conf <<'EOF'
flush ruleset
table inet filter {
  chain input {
    type filter hook input priority 0;
    ct state established,related accept
    iif "lo" accept

    # VRRP
    ip protocol vrrp accept

    # SSH
    tcp dport 22 accept

    # 世界频道端口(示例 443)
    tcp dport 443 accept

    # ICMP
    ip protocol icmp accept
    ip6 nexthdr ipv6-icmp accept

    # 默认丢弃
    counter drop
  }
}
EOF
systemctl enable --now nftables

LVS-DR 下,LB 回程不走 LB,因此 无需在 LB 上跟踪回包;上面规则只放行入站必要流量即可。

6. 业务接入与灰度

  • RS 端口预热:在 RS 上提前建立监听(例如 Nginx/Envoy 反代到世界频道进程,或游戏网关直接监听 443/7000);
  • 小流量引流:在边界路由/BGP 上先把 5–10% 流量导向 VIP,观察 10–15 分钟;

观察项:

  • ipvsadm -Ln --stats/--rate 的 PPS/Conn 增长是否线性;
  • RS 连接数分布是否均衡(ss -s / ss -ant | wc -l);
  • 链路延迟与丢包(smokeping/mtr);
  • 世界频道的心跳超时与重连次数是否下降。

7. 线上“坑”与现场解法

坑 1:切换后短时仍有掉线

现象:VIP 漂移到 LB1 后,个别运营商段用户 10–30 秒不能连上。

原因:上游交换机/路由缓存了旧的 MAC,GARP 次数不够。

解决:Keepalived 增加 garp_master_repeat 5,必要时手动发送多轮 GARP:
arping -I eth0 -U -c 10 203.0.113.10

坑 2:VIP 被 RS 抢答 ARP

现象:arp -an 看到 RS 的 MAC 与 VIP 绑定,流量绕过 LB。

原因:RS 未按 DR 要求设置 ARP 策略或 lo:vip 未用 /32。

解决:严格执行 arp_ignore=1/arp_announce=2,VIP 用 /32 绑 lo。

坑 3:Keepalived 不主不备,VRRP 一直切

原因:机房禁用多播,配置了 vrrp 多播模式。

解决:改用 unicast,配置 unicast_src_ip/unicast_peer。

坑 4:LVS-NAT 下连接打满 conntrack

现象:dmesg/conntrack -S 报告表满,延迟飙升。

解决:

增大 nf_conntrack_max 与哈希表;

更建议回到 DR 模式,把回程从 LB 拆掉。

坑 5:GRO/LRO 导致延迟陡增

现象:尾延迟 p99 偶发上扬。

解决:LB 上关闭 LRO,GRO 视压测决定。

8. 监控与可观测

LVS 指标:

# 累计统计
cat /proc/net/ip_vs_stats
# 粒度统计
ipvsadm -Ln --stats
ipvsadm -Ln --rate

Keepalived 日志:

  • /var/log/syslog(或 rsyslog 定向)
  • 关注 VRRP 状态变更、脚本触发、GARP 发送次数

RS 侧:

  • ss -ant 'sport = :443' | wc -l 连接数
  • 世界频道进程的心跳超时/重连计数(接入应用层监控)

9. 压测与优化要点

调度算法选择:

  • sh(源地址哈希)适合 NAT 用户分布广;
  • mh(maglev hash)分布更均匀,故障时重映射更稳定;
  • wrr 可用于按 RS 算力配权。
  • persistence_timeout:长连接业务可设 30–120 分钟;过短会导致上下文迁移。
  • Nagle/延迟:应用层关闭 Nagle(TCP_NODELAY)能降低消息尾延迟。
  • 多端口分摊:世界频道与登录服分用不同 VIP/端口,避免相互影响。
  • IRQ/RPS 绑核:高并发环境下能显著降低抖动(结合 ethtool -x/-X)。

10. 方案 B:LVS-NAT(跨网段时)

差异要点:

  • virtual_server 里改为 lb_kind NAT;
  • LB 上配置 SNAT(nftables)到 LB 下行口 IP,与 conntrack 配套调大;

监控 conntrack -S,并增大:

cat >/etc/sysctl.d/98-conntrack.conf <<'EOF'
net.netfilter.nf_conntrack_max = 10485760
net.netfilter.nf_conntrack_buckets = 524288
EOF
sysctl --system

压力较大时优先考虑回归 DR 或者在 RS 上做就近回程(策略路由)。

11. 常用命令速查

# 查看 LVS 状态
ipvsadm -Ln
ipvsadm -Ln --stats --rate

# 添加/删除 RS(临时)
ipvsadm -a -t 203.0.113.10:443 -r 10.10.10.25:443 -g
ipvsadm -d -t 203.0.113.10:443 -r 10.10.10.25:443

# 手工 GARP
arping -I eth0 -U -c 10 203.0.113.10

# 观察连接
ss -s
ss -ant | awk 'NR>1{print $1}' | sort | uniq -c

12. 最小可运行清单(你可以直接照抄)

在 RS:

  • ip addr add 203.0.113.10/32 dev lo(或持久化)
  • sysctl:arp_ignore=1、arp_announce=2
  • 监听世界频道端口(如 443)

在 LB(主/备):

  • 安装 ipvsadm keepalived
  • keepalived.conf:vrrp_instance 单播 + virtual_server(DR, sh 调度,persistence_timeout)
  • 开启 keepalived,确认 ipvsadm -Ln 有表项

网络:

  • VIP 对外可达,GARP 生效(garp_* 参数与 arping)
  • 边界路由/BGP 指向 VIP 所在 LB 段

观测:

  • ip_vs_stats、--stats/--rate、应用层心跳/重连计数

到 5:40,掉线告警安静了,在线曲线慢慢回到熟悉的坡度。

我靠着机柜坐下,把刚才的步骤和参数一条条补全到变更单里。LVS + Keepalived 没有花哨的仪表盘,但在这种“长连接、低延迟、峰值高”的场景里,它像台钝重但可靠的老机器。

日出之前,我又手动发了一遍 GARP,确认交换机记住了新主。世界频道里,又开始有人在晒夜宵。

附:完整配置模板(可直接套用)

A. LB(MASTER)完整 keepalived.conf

global_defs {
  router_id LVS_HK_LB1
  enable_script_security
  script_user root
}

vrrp_script chk_world_port {
  script "/usr/local/bin/check_world.sh"
  interval 2
  timeout 1
  rise 2
  fall 3
  weight -20
}

vrrp_instance VI_1 {
  state MASTER
  interface eth0
  virtual_router_id 51
  priority 150
  advert_int 1

  unicast_src_ip 203.0.113.101
  unicast_peer {
    203.0.113.102
  }

  authentication {
    auth_type PASS
    auth_pass 9V9pQxk3
  }

  garp_master_delay 1
  garp_master_repeat 5
  garp_lower_prio_repeat 3

  virtual_ipaddress {
    203.0.113.10/24 dev eth0 label eth0:vip
  }

  track_script {
    chk_world_port
  }

  nopreempt
}

virtual_server 203.0.113.10 443 {
  delay_loop 3
  lb_algo sh
  lb_kind DR
  persistence_timeout 3600
  protocol TCP

  real_server 10.10.10.21 443 { TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } }
  real_server 10.10.10.22 443 { TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } }
  real_server 10.10.10.23 443 { TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } }
  real_server 10.10.10.24 443 { TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } }
}

B. LB(BACKUP)差异

router_id LVS_HK_LB2
state BACKUP
priority 100
unicast_src_ip 203.0.113.102
unicast_peer { 203.0.113.101 }

C. RS(loopback VIP + ARP 策略)

# VIP
ip addr add 203.0.113.10/32 dev lo

# ARP(持久化建议写入 /etc/sysctl.d)
sysctl -w net.ipv4.conf.all.arp_ignore=1
sysctl -w net.ipv4.conf.all.arp_announce=2
sysctl -w net.ipv4.conf.default.arp_ignore=1
sysctl -w net.ipv4.conf.default.arp_announce=2