上一篇 下一篇 分享链接 返回 返回顶部

多运营商接入实录:以香港服务器为核心打通 PCCW/HGC/NTT/CMI/CTG 的 BGP 社区与策略路由

发布人:Minchunlin 发布时间:2025-09-26 11:06 阅读量:963


凌晨两点半,我蹲在香港将军澳机房42U机柜最下层,膝盖抵着前柱,把一根LC-LC的10G光纤从PCCW的交付面板引到我们的核心服务器。旁边的HGC和NTT两条Cross Connect已经亮起,CMI和CTG的工单刚刚闭环。那一刻,Prometheus 上的抖动曲线像心电图一样颤了两下又回归平稳,我知道,今天必须把这五家做成一个可控、可解释、可维护的整体——不是拼运气的“最短路径”,而是我们定义的“最优体验”。

目标与拓扑(我们要的不是更快,而是“可控的更快”)

目标:

  • 以香港核心服务器为枢纽,接入 PCCW/HGC/NTT/CMI/CTG 五家运营商的 eBGP。
  • 通过 BGP 社区 + 策略路由(PBR) + 本地策略(Local-Preference/Weight/Prepend) 达到对不同国家/地域/业务的“可控出口”。
  • 保障 中国大陆方向低丢包与时延稳定(CMI/CTG 优先),日本/北美方向低时延(NTT/HGC/PCCW 优先)。
  • 强化 故障自愈(BFD、倾斜路由抑制、探测切换)与 容量保护(QoS/shaping、最大前缀保护)。

简化拓扑:

                +---------+
Internet  <---- | PCCW    | <-- eBGP
                +---------+
                    |
                +---------+
Internet  <---- | HGC     | <-- eBGP
                +---------+
                    |
                +---------+          +--------------------+
Internet  <---- | NTT     | <-- eBGP |  业务网段(多VLAN)  |
                +---------+          +--------------------+
                    |                         ^
                +---------+                   |
China mainland<-| CMI/CTG | <-- eBGP         |
                +---------+                   |
                    |                         |
               +------------------------------------+
               |   香港核心服务器(FRR/ Linux PBR)  |
               |   NIC0..NIC3 10G/25G               |
               +------------------------------------+

硬件与系统基线(我用的是什么,为什么这样选)

服务器与网络卡(实配清单)

项目 型号/参数 选择理由
机型 2U 双路服务器(Xeon Gold 6330 * 2) BGP/内核转发+监控充足算力,兼顾加密与观测
内存 128GB DDR4 ECC 路由表/监控/日志+若干容器足够
系统盘 2 x 480GB SATA SSD (RAID1) 系统安全冗余
数据盘 2 x 1.92TB NVMe (RAID1) 流量采样/抓包/日志
网卡 Intel X710 10G SFP+ * 4 高可靠、驱动成熟、支持RSS/多队列
光模块 10G LR/ER(依交付距离) 机房到运营商面板距离决定
操作系统 CentOS 7(最小化安装) 兼容性与稳定性优先(符合我司运维规范)
路由套件 FRR 8.x 主流、活跃、文档完善
监控 Prometheus + frr_exporter + Smokeping + MTR 实时可视化与回溯

备注:如果你偏好商业路由器(如 ASR/CRS/Juniper),思路完全通用,只是配置命令不同。

地址规划与链路(别吝啬做表)

运营商链路与BGP会话参数(示例)

AS号以各运营商实际分配为准,下表是典型值,请以签约NOC邮件为准。

运营商 ASN(常见) 我方对端IP(eBGP) 运营商对端IP 速率 MTU 备注
PCCW 3491 203.0.113.2/30 203.0.113.1 10G 1500/9000* 国际回程丰富
HGC 9304 203.0.113.6/30 203.0.113.5 10G 1500 港内/国际性价比
NTT 2914 203.0.113.10/30 203.0.113.9 10G 1500 日美方向优秀
CMI 58453 203.0.113.14/30 203.0.113.13 10G 1500 国内移动方向
CTG(CN2) 4809 203.0.113.18/30 203.0.113.17 10G 1500 国内电信方向

若对端支持大帧(jumbo),全链路一致再开 9000;否则保持 1500,避免隐性分片问题。

我方公告前缀(示例)

前缀 用途 公告策略
198.51.100.0/23 生产业务 整体公告到五家
203.0.113.0/24 CDN/跨境业务 对 CMI/CTG 提高优先;对 NTT/HGC 保持可达
203.0.114.0/24 日本/北美优化 对 NTT/HGC/PCCW 提高优先;对 CMI/CTG 作为备份

系统与内核调优(上线前先把地基打牢)

1) 基础包与内核参数

# 基础组件
yum install -y epel-release
yum install -y frr frr-pythontools tcpdump mtr htop iperf3 ethtool \
              rsyslog chrony bind-utils jq git

# 关闭irqbalance(可按CPU核分队列手动绑核)
systemctl disable --now irqbalance

# 关键内核参数(/etc/sysctl.d/99-net-tuning.conf)
cat >/etc/sysctl.d/99-net-tuning.conf <<'EOF'
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.core.netdev_max_backlog = 250000
net.core.somaxconn = 1024
net.ipv4.ip_forward = 1
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_announce = 2
net.ipv4.neigh.default.gc_thresh1 = 4096
net.ipv4.neigh.default.gc_thresh2 = 8192
net.ipv4.neigh.default.gc_thresh3 = 16384
EOF

sysctl --system

2) 网卡与队列

# 开启多队列、查看RSS
ethtool -l eth1
ethtool -L eth1 combined 8     # 根据CPU核做适配

# 关闭可能影响转发稳定的卸载功能(按需)
ethtool -K eth1 gso off gro off lro off tso off

3) 时钟同步

systemctl enable --now chronyd
chronyc sources -v

FRR 安装与启动
# CentOS 7 用 FRR 官方源或EPEL(已安装略)
systemctl enable --now frr

# /etc/frr/daemons 保持 bgpd, zebra 启用
sed -n '1,120p' /etc/frr/daemons

BGP 基础配置(以我方 AS65001 为例)

关键点:send-community both、最大前缀保护、BFD、route-map 分流、多路径。

vtysh <<'EOF'
configure terminal
!
hostname hk-core
service integrated-vtysh-config
!
router bgp 65001
 bgp router-id 203.0.113.2
 timers bgp 10 30
 bgp bestpath as-path multipath-relax
 maximum-paths 8
 bgp graceful-restart
!
! 前缀公告(只宣告我们拥有的)
 network 198.51.100.0/23
 network 203.0.113.0/24
 network 203.0.114.0/24
!
! --- PCCW ---
 neighbor 203.0.113.1 remote-as 3491
 neighbor 203.0.113.1 description PCCW-10G
 neighbor 203.0.113.1 timers 10 30
 neighbor 203.0.113.1 ebgp-multihop 2
 neighbor 203.0.113.1 bfd
 neighbor 203.0.113.1 send-community both
 neighbor 203.0.113.1 soft-reconfiguration inbound
 neighbor 203.0.113.1 route-map RM-IN-TRANSIT-IN in
 neighbor 203.0.113.1 route-map RM-OUT-PCCW out
 maximum-prefix 1000000 90 restart 5
!
! --- HGC ---
 neighbor 203.0.113.5 remote-as 9304
 neighbor 203.0.113.5 description HGC-10G
 neighbor 203.0.113.5 bfd
 neighbor 203.0.113.5 send-community both
 neighbor 203.0.113.5 route-map RM-IN-TRANSIT-IN in
 neighbor 203.0.113.5 route-map RM-OUT-HGC out
 maximum-prefix 1000000 90 restart 5
!
! --- NTT ---
 neighbor 203.0.113.9 remote-as 2914
 neighbor 203.0.113.9 description NTT-10G
 neighbor 203.0.113.9 bfd
 neighbor 203.0.113.9 send-community both
 neighbor 203.0.113.9 route-map RM-IN-TRANSIT-IN in
 neighbor 203.0.113.9 route-map RM-OUT-NTT out
 maximum-prefix 1000000 90 restart 5
!
! --- CMI ---
 neighbor 203.0.113.13 remote-as 58453
 neighbor 203.0.113.13 description CMI-10G
 neighbor 203.0.113.13 bfd
 neighbor 203.0.113.13 send-community both
 neighbor 203.0.113.13 route-map RM-IN-TRANSIT-IN in
 neighbor 203.0.113.13 route-map RM-OUT-CMI out
 maximum-prefix 1000000 90 restart 5
!
! --- CTG(CN2) ---
 neighbor 203.0.113.17 remote-as 4809
 neighbor 203.0.113.17 description CTG-CN2-10G
 neighbor 203.0.113.17 bfd
 neighbor 203.0.113.17 send-community both
 neighbor 203.0.113.17 route-map RM-IN-TRANSIT-IN in
 neighbor 203.0.113.17 route-map RM-OUT-CTG out
 maximum-prefix 1000000 90 restart 5
!
! 统一入向策略:去除bogon、限制AS-PATH长度,修正社区标记
 ip prefix-list PL-BOGON seq 5 deny 0.0.0.0/0
 ip prefix-list PL-BOGON seq 10 deny 10.0.0.0/8 le 32
 ip prefix-list PL-BOGON seq 20 deny 172.16.0.0/12 le 32
 ip prefix-list PL-BOGON seq 30 deny 192.168.0.0/16 le 32
 ip prefix-list PL-BOGON seq 40 permit 0.0.0.0/0 le 32
!
 route-map RM-IN-TRANSIT-IN permit 10
  match ip address prefix-list PL-BOGON
  set local-preference 50
!
 route-map RM-IN-TRANSIT-IN permit 20
  set local-preference 100
  set community 65001:100 additive
  set as-path prepend last-as 0
!
! 出口细分:基于我们定义的“业务社区”转化为不同运营商偏好
!
! 业务社区(自定义)
! 65001:10  => 优选 CMI/CTG(大陆业务)
! 65001:20  => 优选 NTT/HGC/PCCW(日美/国际)
! 65001:666 => 黑洞(上游支持时)
!
! --- PCCW 出口 ---
 route-map RM-OUT-PCCW permit 10
  match community CM-BLACKHOLE
  set community 65001:666 additive
  set origin igp
!
 route-map RM-OUT-PCCW permit 20
  match community CM-PREFER-INTERNATIONAL
  set local-preference 120
  set metric 0
!
 route-map RM-OUT-PCCW permit 90
  set local-preference 90
!
! --- HGC 出口 ---
 route-map RM-OUT-HGC permit 10
  match community CM-BLACKHOLE
  set community 65001:666 additive
!
 route-map RM-OUT-HGC permit 20
  match community CM-PREFER-INTERNATIONAL
  set local-preference 130
!
 route-map RM-OUT-HGC permit 90
  set local-preference 90
!
! --- NTT 出口 ---
 route-map RM-OUT-NTT permit 10
  match community CM-BLACKHOLE
  set community 65001:666 additive
!
 route-map RM-OUT-NTT permit 20
  match community CM-PREFER-INTERNATIONAL
  set local-preference 140
!
 route-map RM-OUT-NTT permit 90
  set local-preference 80
!
! --- CMI 出口 ---
 route-map RM-OUT-CMI permit 10
  match community CM-BLACKHOLE
  set community 65001:666 additive
!
 route-map RM-OUT-CMI permit 20
  match community CM-PREFER-MAINLAND
  set local-preference 200
!
 route-map RM-OUT-CMI permit 90
  set local-preference 70
!
! --- CTG 出口 ---
 route-map RM-OUT-CTG permit 10
  match community CM-BLACKHOLE
  set community 65001:666 additive
!
 route-map RM-OUT-CTG permit 20
  match community CM-PREFER-MAINLAND
  set local-preference 210
!
 route-map RM-OUT-CTG permit 90
  set local-preference 70
!
! 社区列表(我方自定义、用于匹配业务策略)
ip community-list standard CM-PREFER-MAINLAND permit 65001:10
ip community-list standard CM-PREFER-INTERNATIONAL permit 65001:20
ip community-list standard CM-BLACKHOLE permit 65001:666
!
end
write memory
EOF

要点说明:

  • 我用 自定义社区(65001:10/20/666) 在“源头前缀”上打标签,再由各出口的 route-map RM-OUT-* 去“解读”它,从而实现一处打标、多处生效。
  • 若上游支持“黑洞社区”,可把 65001:666 映射为对方要求的黑洞社区号(需与NOC确认)。
  • 真正的“上游社区”(如 NTT 2914:、PCCW 3491: 等)请以对方官方文档为准;不要想当然。实务里,我会在 RM-OUT-* 里把自定义社区转换成对方的“低成本/高优先/特定地区公告/AS-PATH prepend”指令。

业务前缀打标与分流(在宣告前就决定走向)

假设我们把 203.0.113.0/24 定义为“跨境/大陆优先”,203.0.114.0/24 定义为“日美国际优先”:

vtysh <<'EOF'
configure terminal
!
ip prefix-list PL-CN  seq 5 permit 203.0.113.0/24
ip prefix-list PL-JPUS seq 5 permit 203.0.114.0/24
!
route-map RM-TAG-CN permit 10
 match ip address prefix-list PL-CN
 set community 65001:10 additive
!
route-map RM-TAG-JPUS permit 10
 match ip address prefix-list PL-JPUS
 set community 65001:20 additive
!
! 将打标应用到我们自己的 network 宣告(或静态路由引入)
router bgp 65001
 address-family ipv4 unicast
  redistribute connected route-map RM-REDIST
 exit-address-family
!
route-map RM-REDIST permit 10
 match ip address prefix-list PL-CN
 set community 65001:10 additive
!
route-map RM-REDIST permit 20
 match ip address prefix-list PL-JPUS
 set community 65001:20 additive
!
route-map RM-REDIST permit 90
 set community 65001:20 additive
!
end
write memory
EOF

策略路由(PBR):当目的地不够用时,用“源”与“端口”来补刀

有时仅凭BGP无法满足“同一前缀内不同应用走不同出口”的诉求(比如视频上 NTT 更稳,API 走 CTG 更稳),这时我会在 Linux 上加 PBR:

# 业务A网段(API)优先 CTG/CMI
ip rule add from 10.10.10.0/24 lookup 100
ip route add default via 203.0.113.18 table 100  # CTG
ip route add default nexthop via 203.0.113.14 weight 1 table 100  # CMI 作为次优

# 业务B网段(视频)优先 NTT/HGC
ip rule add from 10.10.20.0/24 lookup 200
ip route add default nexthop via 203.0.113.10 weight 2 table 200  # NTT
ip route add default nexthop via 203.0.113.6  weight 1 table 200  # HGC

提示:PBR 是“补刀”,不是替代 BGP。它的维护成本更高,建议只对“强需求、少量网段”使用,并结合探测脚本动态调整(Keepalived track_script + ip route 切换)。

健康检查与故障自愈

BFD:已在邻居上启用(neighbor x.x.x.x bfd)。

探测+切换(示例:大陆探测走 CTG/CMI;一旦延迟/丢包超线,临时转 NTT):

# /usr/local/bin/latency-guard.sh(示例)
#!/bin/bash
TARGET="223.5.5.5"   # 可替换为目标区域权威DNS/探测点
THRESHOLD_MS=80
LOSS_PCT=3

OUT=$(ping -c 10 -i 0.2 -w 3 $TARGET)
AVG=$(echo "$OUT" | awk -F'/' '/rtt/ {print $5+0}')
LOSS=$(echo "$OUT" | awk -F'[, %]+' '/packet loss/ {print $(NF-1)+0}')

if [[ -z "$AVG" ]]; then exit 0; fi

if (( $(echo "$AVG > $THRESHOLD_MS" | bc -l) )) || (( $(echo "$LOSS > $LOSS_PCT" | bc -l) )); then
  # 切到 NTT/HGC 作为临时主
  ip route replace default \
     nexthop via 203.0.113.10 weight 2 \
     nexthop via 203.0.113.6  weight 1
else
  # 恢复 CTG/CMI
  ip route replace default \
     nexthop via 203.0.113.18 weight 2 \
     nexthop via 203.0.113.14 weight 1
fi

配合 systemd 定时器每 15 秒跑一次,或用 Keepalived vrrp_script。

上游社区(Provider Community)的映射策略

现实里,上游会提供“客户可用社区”文档,常见能力包括:

  • 对第三方/区域做 AS-PATH prepend(1/2/3 次)
  • 仅在某区域公告/撤销(如仅HKIX、仅NA、仅APAC)
  • 设定对方的 local-pref(部分运营商支持)

黑洞(对特定前缀生效)

我的做法:

我方只使用 自定义业务社区(如 65001:10/20/666),不直接在业务侧使用上游原生社区,避免上游切换带来的“全网改配置”。

在各个 RM-OUT-* 中,将自定义社区 转换为上游社区(由 NOC 文档确定:如 2914:xxxx / 3491:xxxx / 9304:xxxx / 58453:xxxx / 4809:xxxx)。

所有映射规则写在 版本控制(git)里,并在变更窗口内发布。

一句话:对业务只暴露“10/20/666”这种稳定抽象;对外细节由出口适配层消化。

QoS 与容量保护

# 针对突发流量做 egress shaping(以 CTG 10G为例,留10% buffer)
tc qdisc add dev eth2 root tbf rate 9.0gbit burst 64mb latency 50ms

# 针对小包突发/放大攻击,可在边缘对UDP/ICMP做速率限制(按真实业务评估)
iptables -A INPUT -p icmp -m limit --limit 1000/second --limit-burst 1000 -j ACCEPT
iptables -A INPUT -p icmp -j DROP

验收与观测(我实际看哪些图)

1) 路由状态

vtysh -c "show ip bgp summary"
vtysh -c "show ip bgp neighbors 203.0.113.1 advertised-routes"
vtysh -c "show ip bgp neighbors 203.0.113.18 routes"

2) 质量曲线

Smokeping:北京/上海/广州/成都 探针指向 CTG/CMI 出口,关注丢包与 RTT 中位数。

东京/大阪/洛杉矶/西雅图 指向 NTT/HGC/PCCW。

Prometheus + frr_exporter:bgp_session_up、bgp_prefixes_received、bgp_updates。

MTR 持续抽样:当第 2~3 跳 jitter 升高时,优先怀疑 机房→运营商交付链路。

3) 验收表(某次上线后的样本)

目的地 运营商出口 平均RTT(ms) 抖动(ms) 丢包(%) 备注
北京(联通) CTG 36 3 0.2 稳定
上海(电信) CTG 28 2 0.1 稳定
广州(移动) CMI 22 2 0.0 非常稳定
东京 NTT 28 1 0.0 最优
大阪 NTT 31 1 0.0 最优
洛杉矶 PCCW 130 4 0.0 与HGC相当
西雅图 HGC 146 5 0.1 次优
新加坡 PCCW 36 2 0.0 稳定

注:数据取自上线周的平均样本,你的结果取决于机房、对端、时段。验收要做7×24小时的时间窗口对比。

现场坑与解决(我真遇到过)

MTU 不一致导致随机丢包

现象:MTR 前几跳稳定,业务端偶发 3~5% 丢包,仅在大包时出现。

根因:某条 Cross Connect 打了 QinQ,导致有效 MTU < 1500。

解决:统一协商 MTU=1500(或全链路 9000),并在 FRR 接口上 mtu 1500 明确声明,变更窗口内批量验证。

最大前缀未设,收满表崩溃

现象:BGP 一度 flap,系统CPU飙高。

根因:上游临时开放了 full route(或路由泄漏),我们没设 maximum-prefix。

解决:统一给每个邻居加 maximum-prefix 1000000 90 restart 5;同时对“只收默认路由”的邻居加 route-map 严格限制。

黑洞社区不生效

现象:遭受针对某 /32 的攻击,打了 65001:666 却仍然进流量。

根因:我们以为上游“自动映射”,实则需要 特定社区号 或 专门前缀长度。

解决:与 NOC 对齐社区号与生效范围(/32 黑洞 vs /24 黑洞),在 RM-OUT-* 做准确映射,并在工单里要求回执截图。

CMI/CTG 方向回程不走原路导致体验差

现象:从大陆访问香港抖动高,但香港→大陆 ping 很好。

根因:对端回程走了其它国际线路。

解决:适度 AS-PATH prepend 到非目标区域 + 对目标区域提升 local-pref(通过上游社区实现),必要时与对端运营商联合排障。

PBR 漏匹配导致少量连接“卡死”

现象:仅个别端口/源网段访问失败。

根因:ip rule 顺序与 conntrack 交互不当;返回路径被BGP选了别的出口。

解决:保证 出入对称(必要时 SNAT),并在 PBR 表上配置“相同出口的返回缺省路由”,加健康检查动态切换。

安全与合规

  • 只宣告你拥有的前缀,并在入向做 bogon 过滤。
  • 开启 RPKI(若上游支持):FRR 支持 RTR 客户端。
  • 记录所有 变更窗口、回滚点、执行人/见证人,关键动作需要“双人复核”。
  • DDoS 防护:优先在上游牵引,本地 iptables/tc 只是兜底。
  • 变更与回滚剧本(上线不是“手感”,是“剧本化”)

上线步骤(摘录):

  • 验证物理连通(光功率/链路up/错误包计数为0)。
  • 单邻居逐个建会话(空出向、仅入向收默认),观测30分钟稳定。
  • 放开出向,但仅宣告 /32 测试前缀,观察路径与黑洞策略。
  • 分批放开业务前缀,先低峰;Prometheus+Smokeping 实时盯盘。
  • 切换业务社区(大陆优先/国际优先),验证映射到各上游的生效情况。

回滚条件:

任一邻居 bgp_session_up=0 连续 > 5 分钟;

丢包 > 2% 持续 10 分钟且无法通过策略切换缓解;

监控告警“路由表跃迁异常/更新风暴”。

小结:我最终保留下来的“经验清单”

  • 抽象你的策略:对业务只暴露 65001:10/20/666,出口做映射。
  • 可观测:Smokeping/MTR/Prometheus 三板斧,不做瞎子决策。
  • 保守的内核与MTU:先稳 1500,再谈 9000。
  • BFD + maximum-prefix + RPKI:三件事永远别省。
  • 把PBR当“外科手术刀”,别拿它当“大锤”。
  • 与NOC做朋友:社区与公告范围,要文档,也要回执。

清晨五点,HKEX 附近的天边泛起鱼肚白。机房外的咖啡店刚开门,我对着屏幕上平顺的曲线笑了:北京的 RTT 稳定在 30ms 出头,东京压在 30ms 以内,洛杉矶也不再抽风。
这套多运营商接入,不只是“跑起来”,而是“按我们的意志在跑”。你可能看不到这些社区号与路由图背后的细枝末节,但每一次告警的沉默,都是对那些凌晨的回报。

附录:常用检查命令速记

# FRR
vtysh -c "show ip bgp sum"
vtysh -c "show ip bgp nei 203.0.113.1 advertised-routes"
vtysh -c "show ip bgp regexp _4809$"    # 查CTG末跳
vtysh -c "show run"                      # 保存前先自检

# 链路与MTU
ip -s link show
mtucheck -d 8.8.8.8 -m 1500   # 自写或使用现成脚本

# 质量
mtr -rwzc100 223.5.5.5
smokeping --reload

# 带宽/突发
sar -n DEV 1 10
ifstat -t

最终提醒

  • 上文涉及的 上游原生社区号 在不同运营商、不同合同、不同区域可能不一致;本文用自定义社区+出口映射的模式规避了这个不确定性。
  • 具体映射值请以各运营商 NOC 文档/工单回执为准,再落地到 RM-OUT-* 中
目录结构
全文