香港服务器如何用多线 BGP 优化海外用户访问 CRM 平台 API 的时延
技术教程 2025-09-19 09:43 181


凌晨 2:40,香港机房 19 楼,美国东区的客诉刚刚堆了一屏——/api/v2/contacts/bulk 的 p95 抬到了 1.8s。我端着还没来得及喝的咖啡,盯着 NOC 电视墙上抖动的 RTT 曲线,忽然意识到:我们再不把多线 BGP 做到位,就会被跨洋链路的抖动拖着走。”

那一晚,我把之前只够“能跑”的网络,重做成“能打”的网络:在香港同一机柜里拉起三家国际上联+HKIX,对 CRM API 的海外访问做了系统级调优。下面是我复盘这次改造的全部细节:拓扑、参数、配置、策略、压测表、踩坑与止血。

一、现状与目标

  • 业务形态:CRM 平台,API 以 HTTPS/HTTP2(部分地区启用 HTTP/3/QUIC)对外;长连接、批量导入、Webhook 推送并存。
  • 原始网络:单线(传统国际带宽),出口偶发抖动,跨太平洋和去欧洲的路径经常“漂”。

问题画像(改造前 7 天均值):

区域 p50 (ms) p95 (ms) 超时率 备注
新加坡 42 76 0.12% 稳定
东京 52 98 0.18% 偶发拥塞
美国西部(SFO/SEA) 148 265 1.2% 太平洋路径不稳定
美国东部(IAD/NYC) 198 335 1.9% 走错向时绕路欧洲
伦敦/法兰克福 185 310 1.1% 路由选择不可控
孟买 82 140 0.6% 峰值波动大
悉尼 118 190 0.7% 可接受
圣保罗 320 520 2.8% 抖动与丢包叠加

目标:

  • 海外主要区域(US/EU/IN/ANZ)的 p95 下降 ≥ 25%;
  • 超时率 < 0.5%;
  • 发生单上联故障时,秒级收敛。

二、机房与硬件拓扑(我用的具体货)

机柜位置:香港葵涌某 IDC,直连 HKIX。

核心设备与参数:

  • 路由器(两台,主备):基于 x86 的白牌盒子(Supermicro 1U)
  • CPU:Xeon Silver 4310 ×1
  • 内存:64 GB
  • 存储:2 × 480 GB SATA SSD(RAID1)
  • 网卡:Intel X710-DA2(10G SFP+)×2
  • OS:VyOS 1.4 + FRR 9.x(控制面)
  • BFD、RPKI、BGP 社区策略启用

上联(同一机柜 10G handoff):

  • Upstream-A:国际运营商(典型如 AS2914/AS1299 类,本文不点名),BGP 全表(只收默认+热门前缀)
  • Upstream-B:国际运营商(另一家),BGP 默认+区域前缀
  • Upstream-C:PCCW/CMI/HKT 一类本地强势+国际,BGP 默认
  • HKIX:加入交换(只通本地 Peering)

业务服务器:

  • 应用:2U 机型,AMD EPYC 7xx2,128 GB RAM,2 × 1.92 TB NVMe(RAID1/ZFS Mirror),CentOS 7
  • NIC:Mellanox ConnectX-4 25G(与 ToR 交换机 25G 上行)
  • 反向代理:Nginx 1.25 + quiche(HTTP/3),gRPC 走 h2
  • TCP 拥塞控制:BBR(内核 5.4 backport)

逻辑拓扑(简化):

[App Nodes] -- iBGP/OSPF --> [VyOS-R1] <--> [VyOS-R2] --VRRP VIP--> eBGP
                                |   \        /   |
                               A     B      C   HKIX

三、设计思路(为什么用多线 BGP,而不是“玄学”加速)

  • 出站(Egress):我们通过 Local-Pref + Policy Routing 指定不同区域优先走不同上联(例如:US 西优先 A,EU 优先 B,印度优先 C)。
  • 入站(Ingress):通过 BGP 社区 + 适度 AS-Path Prepend + 精细通告粒度 引导主要流量从更“近”的运营商进来(不滥用 /24 细分,减少路由污染)。
  • 快速收敛:BFD + BGP Graceful Restart/Shutdown。
  • 稳定性:RPKI 校验,Max-Prefix 防炸表,GTSM(TTL Security)。
  • 业务层面:开启 HTTP/3/QUIC 提升弱网抗丢包;TLS1.3,开启 0-RTT(只对白名单 API);MSS Clamping 应对跨域 MTU 差异。

四、实施步骤(从接入到生效)

4.1 线路接入与基础 BGP

VyOS 侧 BGP 骨架(两台设备类似;以下示例为 R1):

configure

# 本端 AS(示例 65001,如果你没有自有 AS,用托管 AS 也可)
set protocols bgp 65001 parameters router-id '203.0.113.1'

# RPKI 验证(可接 Routinator/OctoRPKI)
set protocols rpki cache 10.20.30.10 port 3323
set protocols rpki rpki polling-interval 60
set protocols rpki rpki enable

# eBGP 邻居:Upstream-A
set protocols bgp 65001 neighbor 198.51.100.1 remote-as 'XXXX' 
set protocols bgp 65001 neighbor 198.51.100.1 description 'Upstream-A'
set protocols bgp 65001 neighbor 198.51.100.1 password '***'
set protocols bgp 65001 neighbor 198.51.100.1 timers holdtime '9'
set protocols bgp 65001 neighbor 198.51.100.1 timers keepalive '3'
set protocols bgp 65001 neighbor 198.51.100.1 bfd
set protocols bgp 65001 neighbor 198.51.100.1 ebgp-multihop 2
set protocols bgp 65001 neighbor 198.51.100.1 maximum-prefix 400000 restart 60 warning-only

# Upstream-B
set protocols bgp 65001 neighbor 203.0.113.9 remote-as 'YYYY'
set protocols bgp 65001 neighbor 203.0.113.9 description 'Upstream-B'
set protocols bgp 65001 neighbor 203.0.113.9 password '***'
set protocols bgp 65001 neighbor 203.0.113.9 bfd

# Upstream-C
set protocols bgp 65001 neighbor 203.0.113.17 remote-as 'ZZZZ'
set protocols bgp 65001 neighbor 203.0.113.17 description 'Upstream-C'
set protocols bgp 65001 neighbor 203.0.113.17 password '***'
set protocols bgp 65001 neighbor 203.0.113.17 bfd

# HKIX(IX 对等,示例只收默认/选定前缀)
set protocols bgp 65001 neighbor 218.100.18.1 remote-as 'HKIX-AS'
set protocols bgp 65001 neighbor 218.100.18.1 description 'HKIX'
set protocols bgp 65001 neighbor 218.100.18.1 soft-reconfiguration inbound

# 出口宣告(业务段,示例:203.0.113.0/23)
set policy route-map EXPORT permit 10
set policy route-map EXPORT rule 10 match ip address prefix-list OUR-PFX
set policy route-map EXPORT rule 10 set community 'no-export additive'  # 示例,根据上游要求调整
set policy prefix-list OUR-PFX rule 10 action 'permit'
set policy prefix-list OUR-PFX rule 10 prefix '203.0.113.0/23'

set protocols bgp 65001 neighbor 198.51.100.1 route-map export 'EXPORT'
set protocols bgp 65001 neighbor 203.0.113.9  route-map export 'EXPORT'
set protocols bgp 65001 neighbor 203.0.113.17 route-map export 'EXPORT'
set protocols bgp 65001 neighbor 218.100.18.1 route-map export 'EXPORT'

commit; save

备注:社区(community)数值需要找各自上游要清单,用来做入站引流、黑洞(/32)、本地优先、沿途不导出等策略。本文演示思路,不硬编码厂商私有值。

4.2 区域化的出站策略(Local-Pref + Policy Routing)

思路:

  • 业务请求“出港”时,按目的区域优先走更“顺”的上联。
  • 我保守做法是 Local-Pref 做第一层偏好,PBR 细粒度兜底。

Local-Pref 示例(VyOS/FRR 兼容思路):

# 对 Upstream-A 设置较高 local-pref,用于美西/太平洋流量
set policy route-map SET-LP-A permit 10
set policy route-map SET-LP-A rule 10 set local-preference '300'

# Upstream-B(欧洲)
set policy route-map SET-LP-B permit 10
set policy route-map SET-LP-B rule 10 set local-preference '280'

# Upstream-C(南亚/东南亚冗余)
set policy route-map SET-LP-C permit 10
set policy route-map SET-LP-C rule 10 set local-preference '260'

set protocols bgp 65001 neighbor 198.51.100.1 route-map import 'SET-LP-A'
set protocols bgp 65001 neighbor 203.0.113.9  route-map import 'SET-LP-B'
set protocols bgp 65001 neighbor 203.0.113.17 route-map import 'SET-LP-C'

PBR 细化(CentOS 7 业务机做标记,或直接在边界路由器做):

我用 ipset + ip rule:针对美国、欧洲、印度等目的前缀集合,打上 fwmark,从而走不同的 table。

# 业务服务器(CentOS 7)
# 1) 定义路由表
echo "200 us"  >> /etc/iproute2/rt_tables
echo "201 eu"  >> /etc/iproute2/rt_tables
echo "202 in"  >> /etc/iproute2/rt_tables

# 2) fwmark 绑定路由表
ip rule add fwmark 0x1 table us
ip rule add fwmark 0x2 table eu
ip rule add fwmark 0x3 table in

# 3) 每个表指定下一跳(R1/R2 的不同 egress)
ip route add default via 10.10.10.1 dev eth1 table us  # -> Upstream-A
ip route add default via 10.10.10.2 dev eth1 table eu  # -> Upstream-B
ip route add default via 10.10.10.3 dev eth1 table in  # -> Upstream-C

# 4) ipset + iptables 标记(示例:美国)
ipset create us dst hash:net
# 将美国聚合前缀(预先生成的 CIDR)导入 ipset(脚本离线维护)
# ipset restore < /etc/ipset/us.set

# 标记包
iptables -t mangle -A OUTPUT -m set --match-set us dst -p tcp --dport 443 -j MARK --set-mark 0x1
# 同理为 eu/in 建立集合与规则

生成 ipset 前缀集合我写了一个小脚本,从离线 CIDR 数据库(按国家/区域)聚合,防止集合过大影响 mangle 性能。

4.3 入站优化(社区 + 轻度 Prepend + 广告粒度)

  • 默认向三家上游都 宣告 /23;
  • 对“首选入口”的上游 不做 Prepend;对其他上游 做 1~2 次 Prepend(尽量少);
  • 启用某些上游的 “Regional Preference” 社区(各家定义不同,向运营商申请清单);
  • 严格控制更细粒度 /24 的使用(只在明确观测到大量回程绕路时,临时打开,并设定定时器自动回滚)。

VyOS/FRR Prepend 示例:

set policy route-map EXPORT-US permit 10
set policy route-map EXPORT-US rule 10 match ip address prefix-list OUR-PFX-US
set policy route-map EXPORT-US rule 10 set as-path prepend '65001 65001'

# 对 Upstream-B/Upstream-C 使用 EXPORT-US,Upstream-A 不使用
set protocols bgp 65001 neighbor 203.0.113.9  route-map export 'EXPORT-US'
set protocols bgp 65001 neighbor 203.0.113.17 route-map export 'EXPORT-US'

4.4 快速收敛与维护窗口

  • BFD:3×500ms 探测,丢 3 包即下线;
  • BGP Graceful Shutdown:维护前对邻居打 GSHUT 社区(常用 65535:0,视上游支持),先引流再断会话;
  • Max-Prefix:预防上游“泄表”;
  • GTSM:TTL Security 保护 BGP 会话。

4.5 RPKI 与 IRR

在 APNIC/RIPE Portal 创建 ROA(Origin AS=65001,前缀=203.0.113.0/23,MaxLength=23)。

路由器侧开启 RPKI 验证,invalid 拒收、unknown 接收但降权(按需)。

FRR 片段(思路一致):

rpki
 rpki cache 10.20.30.10 3323
 !
bgp rpki invalid reject

五、传输层与业务栈优化(不是只有网络)

CentOS 7 内核网络参数(BBR + FQ):

cat >> /etc/sysctl.d/99-net-tuning.conf <<'EOF'
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr

net.ipv4.tcp_rmem=4096 87380 134217728
net.ipv4.tcp_wmem=4096 65536 134217728
net.core.rmem_max=134217728
net.core.wmem_max=134217728

net.ipv4.tcp_mtu_probing=1
net.ipv4.tcp_sack=1
net.ipv4.tcp_timestamps=1
net.ipv4.tcp_fin_timeout=15
net.ipv4.tcp_tw_reuse=1
EOF

sysctl --system

Nginx(HTTP/2 + QUIC)片段:

http {
    # TLS1.3, 会话复用
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_early_data on;  # 对特定API与IP白名单开放0-RTT

    # H2
    http2_max_field_size 32k;
    http2_max_header_size 64k;
    http2_idle_timeout 30s;

    # QUIC(基于 quiche/patch 的发行版)
    quic_retry on;
    quic_gso on;
    quic_idle_timeout 30s;

    # 反向代理参数
    proxy_http_version 1.1;
    proxy_read_timeout 60s;
    keepalive_requests 10000;
    keepalive_timeout 75s;
}

MSS Clamping(避免跨洋 PMTU 坑):

# 边界路由器,对 443/TCP 出口做 MSS 限制
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -o eth_wanA -j TCPMSS --set-mss 1440
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -o eth_wanB -j TCPMSS --set-mss 1440
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -o eth_wanC -j TCPMSS --set-mss 1440

六、观测与回归(我怎么判定“生效了”)

我把 mtr、h2load、fortio、以及自研的 API 合成压测(仿真实际 payload)接到 Prometheus + Grafana,并用 BGP 会话状态、路径 AS-Path、BFD 事件 作为维度打标。下面是 “上线后一周”的对比:

6.1 时延与稳定性(核心指标)

区域 改造前 p95 改造后 p95 下降幅度 超时率(后)
新加坡 76ms 48ms -36.8% 0.05%
东京 98ms 70ms -28.6% 0.06%
美国西部 265ms 170ms -35.8% 0.28%
美国东部 335ms 220ms -34.3% 0.31%
伦敦/法兰克福 310ms 205ms -33.9% 0.22%
孟买 140ms 95ms -32.1% 0.19%
悉尼 190ms 140ms -26.3% 0.21%
圣保罗 520ms 380ms -26.9% 0.62%

API 侧:/api/v2/contacts/bulk 的 p95 从 1.8s 降到 1.18s,5xx 错误率从 0.9% 降到 0.24%。

6.2 路径样例(mtr 精简)

US-West(后)
  1. 10.10.10.1 1.0 ms
  2. Upstream-A.hk 2.0 ms
  3. * 太平洋直达 *
  7. sea01 ...
  9. sfo-cdn-edge 168 ms

EU-Frankfurt(后)
  1. 10.10.10.2 0.9 ms
  2. Upstream-B.hk 1.8 ms
  4. de-fra-core ...
  8. aws-fra 204 ms

七、我踩过的坑(以及怎么填)

上游社区表不统一:不同运营商对相同“意图”的社区值不一致,甚至同一运营商不同区域 POP 也有差异。
解法:获取最新社区文档;封装成内部“意图到社区”的映射(YAML),生成器按上游维度下发。

MTU 黑洞:开启 QUIC 后部分地区 0-RTT 时连接建不起来,追到最后是某段链路 MTU 窄,ICMP 不回。
解法:强制 MSS Clamping 1440;对 QUIC 限制 max_datagram_frame_size;关键路径强制 Don't Fragment 的探测,异常入黑名单走备线。

BGP 收敛抖动:BFD 过于激进导致抖动(假阳性 down/up)。
解法:把 BFD 探测从 300ms×3 调到 500ms×3,结合接口层 link debounce,并开启 GR 避免数据面闪断。

Max-Prefix 误触发:上游某次维护放出更多特定表项,超过我们配置的阈值。
解法:阈值从 300k 调到 400k 并设 restart 60 + 报警门限,避免长时间黑洞。

入站引流过度:一度用 /24 强引流到 Upstream-A,结果 A 的某段拥塞导致波动放大。
解法:坚守“最小必要”原则,只在问题被证据明确指向时临时启用,自动回滚(Ansible/Timer)。

八、最佳实践清单(我现在的“手感”)

  • 优先级:Local-Pref(粗粒度) > PBR(细粒度兜底) > 社区/Prepend(入站)。
  • 健康检查:BFD + 实际业务健康探针(RHI),两者都健康才宣告 VIP。
  • 观测:把 “AS-Path、Next-Hop、社区、BFD 事件” 做成一行标签,和业务 p95/超时率同屏。
  • 维护:先打 GSHUT,观测流量自然迁移后再断链;回滚脚本要一键。
  • 变更窗口:海外主流业务所在时区凌晨;我们在香港本地凌晨做灰度,确保跨区协同。
  • 文档:上游社区表、前缀白/黑名单、PBR 集合,都有生成器(别手改)。

九、完整配置片段(再给一份 FRR 版参考)

/etc/frr/frr.conf(骨架示例):

frr version 9.0
frr defaults traditional
hostname r1
service integrated-vtysh-config
!
rpki
 rpki cache 10.20.30.10 3323 preference 1
!
router bgp 65001
 bgp router-id 203.0.113.1
 bgp graceful-restart
 neighbor UP-A peer-group
 neighbor UP-A remote-as XXXX
 neighbor 198.51.100.1 peer-group UP-A
 neighbor 198.51.100.1 password ****
 neighbor 198.51.100.1 timers 3 9
 neighbor 198.51.100.1 bfd
 !
 neighbor UP-B peer-group
 neighbor UP-B remote-as YYYY
 neighbor 203.0.113.9 peer-group UP-B
 neighbor 203.0.113.9 password ****
 neighbor 203.0.113.9 bfd
 !
 neighbor UP-C peer-group
 neighbor UP-C remote-as ZZZZ
 neighbor 203.0.113.17 peer-group UP-C
 neighbor 203.0.113.17 password ****
 neighbor 203.0.113.17 bfd
 !
 address-family ipv4 unicast
  network 203.0.113.0/23
  neighbor UP-A route-map SET-LP-A in
  neighbor UP-B route-map SET-LP-B in
  neighbor UP-C route-map SET-LP-C in
  neighbor 203.0.113.9 route-map EXPORT-US out
  neighbor 203.0.113.17 route-map EXPORT-US out
 exit-address-family
!
route-map SET-LP-A permit 10
 set local-preference 300
!
route-map SET-LP-B permit 10
 set local-preference 280
!
route-map SET-LP-C permit 10
 set local-preference 260
!
ip prefix-list OUR-PFX seq 5 permit 203.0.113.0/23
!
route-map EXPORT-US permit 10
 match ip address prefix-list OUR-PFX
 set as-path prepend 65001 65001
!
line vty
!

改造后一周,我们把 CRM API 海外访问的 p95 降了 26%~36%,超时率压到 0.2x% 的量级。再也不是被动挨打:某家上游抖动时,BFD 迅速摘除,Local-Pref 和 PBR 接管,业务基本“无感”。运维群里,大家戏称这套方案是“香港三明治”:上有策略(社区/Prepend),中有路由(BGP/L-Pref),下有数据面(PBR/传输层),夹得稳、咬得动。

凌晨 4:10,我在机房门口把那杯咖啡一口闷了,天微微泛亮——电视墙上的绿色延迟曲线就像清晨的维港,终于平了。

附:落地清单(拿去按图施工)

  • 选三家 差异化上联 + HKIX;签好 SLA 和 社区/黑洞 支持条款。
  • 两台路由器(x86 + VyOS/FRR),开启 BFD/RPKI/GTSM/Max-Prefix。
  • 业务段宣告 /23;必要时临时 /24 精细引流,自动回滚。
  • Local-Pref 粗分区(US/EU/IN),ipset+PBR 兜底。
  • 业务服务器启用 BBR + FQ,Nginx 开启 HTTP/3/0-RTT(白名单)。
  • MSS Clamping 1440;对异常路径做 DF 探测与黑名单。
  • Prometheus 采集 BGP/接口/探针指标;Grafana 联动 p95/AS-Path。
  • 变更=先打 GSHUT,确认流量迁移后再断,会后复盘。
  • 每季度复核 社区表/路由策略 与 ROA/IRR。
  • 预案演练:上游故障、表溢出、RPKI 失联、BFD 假阳性。

如果你也在做香港多线 BGP 的海外加速,这篇就是我亲手走过的那条路。你可以把数值和供应商换成自己的,但策略的骨架与观测的闭环,建议一字不改地抄。