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

凌晨两点半,我蹲在香港将军澳机房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-* 中