从两个VPS位置实现BGP故障切换和多宿主
使用BGP从两个位置宣告相同前缀,实现自动故障切换。涵盖LOCAL_PREF、MED、AS-path prepending、BFD和优雅关闭,包含完整的BIRD2和FRR配置。
本教程介绍如何通过BGP从两个独立的VPS位置宣告相同的IP前缀。你将配置LOCAL_PREF和MED实现主备偏好,启用BFD实现亚秒级故障检测,并实现优雅关闭(graceful shutdown)用于计划维护。所有示例同时展示BIRD2和FRR的配置。
前提条件:
- 至少一个VPS上有可用的BGP会话(Linux VPS上配置BIRD2 BGP路由或在Linux VPS上配置FRRouting BGP)
- 你自己的ASN和至少一个/24(IPv4)或/48(IPv6)前缀
- 对BGP communities有基本了解(BGP Communities详解:Standard、Large与Extended)
什么是BGP多宿主,为什么在VPS上使用?
BGP多宿主(multihoming)是指通过eBGP从两个或更多位置宣告相同的IP前缀。每个位置与其上游提供商保持独立的BGP会话。如果一个位置故障,另一个继续宣告前缀并自动接收所有流量。收敛时间取决于hold timer(默认配置通常为180-240秒)或BFD(正确配置下可达亚秒级)。
在VPS上,多宿主提供了不依赖单个数据中心的冗余能力。你在不同位置运行两个VPS实例,都宣告你的前缀。一个作为主节点,另一个作为备份。流量工程属性(LOCAL_PREF、MED、AS-path prepending)控制正常情况下哪条路径处理流量。
如何设计跨两个位置的BGP故障切换架构?
该架构使用两个位于不同欧洲位置的Virtua VPS,每个都与本地上游路由器保持eBGP会话。两者都宣告相同的/24和/48。
Internet
/ \
Upstream A Upstream B
(Frankfurt) (Amsterdam)
| |
eBGP session eBGP session
| |
+-----------+ +-----------+
| VPS-PRI | | VPS-BKP |
| AS 64500 | | AS 64500 |
| BIRD2/FRR | | BIRD2/FRR |
+-----------+ +-----------+
announces announces
198.51.100.0/24 198.51.100.0/24
2001:db8::/48 2001:db8::/48
两个节点都属于你的AS(示例中为AS 64500)。请将ASN、前缀和对等IP替换为你的实际值。
两个节点的防火墙规则:
BGP使用TCP端口179。BFD使用UDP端口3784和3785。在继续之前,请在你的VPS和上游对等端之间开放这些端口。
# nftables example - adjust PEER_IP to your upstream
nft add rule inet filter input ip saddr PEER_IP tcp dport 179 accept
nft add rule inet filter input ip saddr PEER_IP udp dport { 3784, 3785 } accept
如何控制BGP路径偏好?
三个属性可以影响流量走向,每个在不同范围内起作用。
| 属性 | 方向 | 范围 | 是否发送给对等端? | 使用场景 |
|---|---|---|---|---|
| LOCAL_PREF | 出站(你的出口) | AS内部 | 否(仅iBGP) | 控制哪个节点发送出站流量 |
| MED | 入站(来自上游) | 你和一个上游AS之间 | 是(发给直接邻居) | 告诉单个上游优先选择哪个入口点 |
| AS-path prepending | 入站(全局) | 路径中的所有AS | 是(会传播) | 让一条路径在整个互联网上看起来更长 |
LOCAL_PREF和MED是精确的工具。AS-path prepending比较粗糙,但当你的两个位置连接不同上游时很有效。
如何配置LOCAL_PREF实现主备路径?
LOCAL_PREF决定你的AS优先选择哪条出站路径。值越高越优先,默认值为100。在主节点设置200,在备份节点保持100。这只影响离开你网络的流量。
BIRD2 LOCAL_PREF配置
在主节点(VPS-PRI),创建或修改import过滤器:
# /etc/bird/bird.conf - Primary node
filter upstream_import_primary {
bgp_local_pref = 200;
accept;
}
protocol bgp upstream_v4 {
local 192.0.2.2 as 64500;
neighbor 192.0.2.1 as 64496;
ipv4 {
import filter upstream_import_primary;
export where net = 198.51.100.0/24;
};
}
protocol bgp upstream_v6 {
local 2001:db8:1::2 as 64500;
neighbor 2001:db8:1::1 as 64496;
ipv6 {
import filter upstream_import_primary;
export where net = 2001:db8::/48;
};
}
在备份节点(VPS-BKP),保持默认LOCAL_PREF:
# /etc/bird/bird.conf - Backup node
filter upstream_import_backup {
bgp_local_pref = 100;
accept;
}
protocol bgp upstream_v4 {
local 203.0.113.2 as 64500;
neighbor 203.0.113.1 as 64497;
ipv4 {
import filter upstream_import_backup;
export where net = 198.51.100.0/24;
};
}
重新加载BIRD2并检查路由:
birdc configure
birdc show route for 0.0.0.0/0 all
0.0.0.0/0 unicast [upstream_v4 12:00:00] * (100/?) [AS64496i]
via 192.0.2.1 on eth0
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 64496
BGP.local_pref: 200
主节点上的BGP.local_pref: 200表示它将被优先用于出站流量。
FRR LOCAL_PREF配置
在主节点:
vtysh -c "configure terminal
route-map UPSTREAM-IN permit 10
set local-preference 200
exit
router bgp 64500
neighbor 192.0.2.1 remote-as 64496
address-family ipv4 unicast
neighbor 192.0.2.1 route-map UPSTREAM-IN in
network 198.51.100.0/24
exit-address-family
neighbor 2001:db8:1::1 remote-as 64496
address-family ipv6 unicast
neighbor 2001:db8:1::1 route-map UPSTREAM-IN in
network 2001:db8::/48
exit-address-family
exit
exit"
在备份节点,设置set local-preference 100(或省略route-map,因为100是默认值)。
检查路由表:
vtysh -c "show ip bgp"
Network Next Hop Metric LocPrf Weight Path
*> 0.0.0.0/0 192.0.2.1 200 0 64496 i
如何使用MED控制入站流量?
MED(Multi-Exit Discriminator)告诉上游优先选择哪个入口点。值越低越优先。在主节点设置MED 0,在备份节点设置MED 100。MED只在来自同一邻居AS的路径之间比较,因此当两个位置与同一上游提供商对等时效果最好。
BIRD2 MED配置
在主节点,在export过滤器中设置MED:
filter upstream_export_primary {
if net = 198.51.100.0/24 || net = 2001:db8::/48 then {
bgp_med = 0;
accept;
}
reject;
}
protocol bgp upstream_v4 {
local 192.0.2.2 as 64500;
neighbor 192.0.2.1 as 64496;
ipv4 {
import filter upstream_import_primary;
export filter upstream_export_primary;
};
}
在备份节点:
filter upstream_export_backup {
if net = 198.51.100.0/24 || net = 2001:db8::/48 then {
bgp_med = 100;
accept;
}
reject;
}
FRR MED配置
在主节点:
vtysh -c "configure terminal
route-map UPSTREAM-OUT permit 10
set metric 0
exit
router bgp 64500
address-family ipv4 unicast
neighbor 192.0.2.1 route-map UPSTREAM-OUT out
exit-address-family
exit
exit"
在备份节点,使用set metric 100。
检查导出的路由:
vtysh -c "show ip bgp neighbors 192.0.2.1 advertised-routes"
Network Next Hop Metric LocPrf Weight Path
*> 198.51.100.0/24 0.0.0.0 0 32768 i
Metric列在主节点上显示0,备份节点将显示100。
什么时候应该用AS-path prepending替代MED?
当你的两个位置连接到不同的上游提供商时,使用AS-path prepending。MED只在来自同一AS的路径之间比较,如果上游是不同AS则没有效果。Prepending使备份路径看起来更长,将全局路由决策推向主节点。
在备份节点上将你自己的ASN添加1-3次。超过3次prepend很少改变路由决策,只是增加噪音。
BIRD2(备份节点export过滤器):
filter upstream_export_backup_prepend {
if net = 198.51.100.0/24 || net = 2001:db8::/48 then {
bgp_path.prepend(64500);
bgp_path.prepend(64500);
accept;
}
reject;
}
FRR(备份节点):
vtysh -c "configure terminal
route-map UPSTREAM-OUT permit 10
set as-path prepend 64500 64500
exit
exit"
应用后,从looking glass或远程主机检查AS path:
# From an external machine
traceroute -A 198.51.100.1
备份路径现在显示64500 64500 64500(你的ASN出现三次:一次真实,两次prepend),而主路径只显示一次64500。
如何启用BFD实现快速故障检测?
没有BFD时,BGP依赖hold timer检测对等端故障。BIRD2默认hold time为240秒,FRR为180秒。有了BFD,在低延迟链路上检测时间降至亚秒级。
| 参数 | 默认值 | VPS推荐值 |
|---|---|---|
| 发送间隔 | 300 ms | 300 ms |
| 接收间隔 | 300 ms | 300 ms |
| 检测倍数 | 3 | 3 |
| 有效检测时间 | 900 ms | 900 ms |
对于同一提供商骨干网上的VPS环境,300 ms间隔配合倍数3可以提供可靠的亚秒级检测而不会误报。不要在VPS实例上将间隔设置低于100 ms,虚拟化抖动可能导致路由震荡。
BIRD2 BFD配置
添加BFD协议并在BGP会话上启用:
protocol bfd {
interface "*" {
min rx interval 300 ms;
min tx interval 300 ms;
multiplier 3;
};
}
protocol bgp upstream_v4 {
local 192.0.2.2 as 64500;
neighbor 192.0.2.1 as 64496;
bfd graceful;
ipv4 {
import filter upstream_import_primary;
export filter upstream_export_primary;
};
}
bfd graceful选项意味着当BFD检测到故障时,BIRD2将触发优雅重启(保留过期路由)而不是硬重置会话。如果对等端不运行BFD,会话仍然正常建立。
重新加载后,检查BFD状态:
birdc show bfd sessions
BFD sessions:
IP address Interface State Since Interval Timeout
192.0.2.1 eth0 Up 12:00:00 300 ms 900 ms
FRR BFD配置
vtysh -c "configure terminal
bfd
profile vps-detect
receive-interval 300
transmit-interval 300
detect-multiplier 3
exit
exit
router bgp 64500
neighbor 192.0.2.1 bfd profile vps-detect
neighbor 2001:db8:1::1 bfd profile vps-detect
exit
exit"
检查BFD对等端状态:
vtysh -c "show bfd peers"
BFD Peers:
peer 192.0.2.1 vrf default
ID: 1
Remote ID: 2
Status: up
Uptime: 5 minute(s)
Diagnostics: ok
Remote diagnostics: ok
Peer Type: configured
Local timers:
Receive interval: 300ms
Transmission interval: 300ms
Echo receive interval: disabled
Echo transmission interval: disabled
Peer timers:
Receive interval: 300ms
Transmission interval: 300ms
Echo receive interval: disabled
BFD需要对等端之间开放UDP端口3784和3785。如果之前跳过了防火墙步骤,BFD会话将保持Down状态。
如何执行优雅关闭进行维护?
RFC 8326定义了GRACEFUL_SHUTDOWN知名community(65535:0)。在计划维护之前,用这个community标记所有路由。尊重该community的对等端会将这些路由的local-preference设置为0,使流量在你关闭会话之前转移到备用路径。这避免了正常BGP收敛期间出现的流量黑洞。
优雅关闭流程:
- 标记路由:在要关闭的节点上用GRACEFUL_SHUTDOWN community标记所有路由
- 等待收敛(30-60秒让互联网重新路由)
- 确认流量已转移:通过looking glass或流量计数器检查
- 关闭BGP会话
- 执行维护
- 恢复会话并移除community标记
- 确认重新收敛
BIRD2优雅关闭
在维护前,要在主节点上发起优雅关闭,修改export过滤器:
# Temporary export filter for graceful shutdown
filter upstream_export_shutdown {
if net = 198.51.100.0/24 || net = 2001:db8::/48 then {
bgp_community.add((65535, 0));
bgp_med = 65535;
accept;
}
reject;
}
通过修改BGP协议中的export过滤器并重新加载来应用:
# Edit bird.conf: change export filter to upstream_export_shutdown
# Then reload
birdc configure
要尊重来自对等端的优雅关闭(在两个节点上应用),在import过滤器中添加检查。顺序很重要:优雅关闭检查必须在if块内调用accept,否则后续的bgp_local_pref赋值会覆盖它。
filter upstream_import_backup {
if (65535, 0) ~ bgp_community then {
bgp_local_pref = 0;
accept;
}
bgp_local_pref = 100;
accept;
}
FRR优雅关闭
FRR提供一个命令自动处理标记:
vtysh -c "configure terminal
router bgp 64500
bgp graceful-shutdown
exit
exit"
这会将GRACEFUL_SHUTDOWN community(65535:0)添加到所有路由并将local-preference设置为0。会向所有对等端触发路由刷新。
确认community正在发送:
vtysh -c "show ip bgp neighbors 192.0.2.1 advertised-routes"
Network Next Hop Metric LocPrf Weight Path
*> 198.51.100.0/24 0.0.0.0 0 0 32768 i
Community: graceful-shutdown
维护后移除:
vtysh -c "configure terminal
router bgp 64500
no bgp graceful-shutdown
exit
exit"
要让FRR尊重来自对等端的优雅关闭,配置入站route-map:
vtysh -c "configure terminal
bgp community-list standard GRACEFUL_SHUTDOWN permit graceful-shutdown
route-map UPSTREAM-IN permit 5
match community GRACEFUL_SHUTDOWN
set local-preference 0
exit
route-map UPSTREAM-IN permit 10
set local-preference 200
exit
exit"
序列5匹配携带该community的路由并将local-preference降至0。序列10正常处理所有其他路由。
如何测试BGP故障切换?
通过关闭主BGP会话,从备份节点和外部视角观察来测试故障切换。
步骤1:检查两个节点上当前的路由状态。
BIRD2:
birdc show route for 198.51.100.0/24 all
FRR:
vtysh -c "show ip bgp 198.51.100.0/24"
步骤2:关闭主BGP会话。
BIRD2(在VPS-PRI上):
birdc disable upstream_v4
birdc disable upstream_v6
FRR(在VPS-PRI上):
vtysh -c "configure terminal
router bgp 64500
neighbor 192.0.2.1 shutdown
neighbor 2001:db8:1::1 shutdown
exit
exit"
步骤3:观察备份节点。
在VPS-BKP上,路由现在应该显示为唯一路径:
# BIRD2
birdc show route for 198.51.100.0/24
# FRR
vtysh -c "show ip bgp summary"
步骤4:从外部测试。
从你的本地机器或looking glass,对你的前缀执行traceroute:
traceroute -A 198.51.100.1
流量现在应该通过备份位置进入。启用BFD后,切换在1秒内完成。没有BFD,需要等待完整的hold timer时间才能收敛。
| 检测方法 | 典型故障切换时间 |
|---|---|
| 仅BGP hold timer(BIRD2默认240 s) | 160-240 s |
| 仅BGP hold timer(FRR默认180 s) | 120-180 s |
| 缩短的hold timer(如30 s) | 20-30 s |
| BFD(300 ms间隔,倍数3) | < 1 s |
使用NLNOG Looking Glass或bgp.tools确认全局路由收敛。
故障切换后如何恢复?
恢复主会话并确认流量回到首选路径。
BIRD2:
birdc enable upstream_v4
birdc enable upstream_v6
FRR:
vtysh -c "configure terminal
router bgp 64500
no neighbor 192.0.2.1 shutdown
no neighbor 2001:db8:1::1 shutdown
exit
exit"
几秒后,确认主路径再次被优先选择:
# BIRD2
birdc show route for 0.0.0.0/0 all | grep local_pref
BGP.local_pref: 200
# FRR
vtysh -c "show ip bgp"
Network Next Hop Metric LocPrf Weight Path
*> 0.0.0.0/0 192.0.2.1 200 0 64496 i
再次从外部主机执行traceroute,确认流量重新通过主位置进入。
BIRD2与FRR配置对比
| 功能 | BIRD2 | FRR |
|---|---|---|
| LOCAL_PREF | import过滤器中bgp_local_pref = 200; |
route-map中set local-preference 200 |
| MED | export过滤器中bgp_med = 0; |
route-map中set metric 0 |
| AS-path prepend | export过滤器中bgp_path.prepend(64500); |
route-map中set as-path prepend 64500 |
| BFD | protocol bfd {} + BGP中bfd graceful; |
bfd段 + neighbor X bfd profile Y |
| 优雅关闭(发起) | export过滤器中添加(65535, 0)到bgp_community |
router bgp下bgp graceful-shutdown |
| 优雅关闭(尊重) | import过滤器中检查(65535, 0) ~ bgp_community,设置bgp_local_pref = 0 |
route-map中match community GRACEFUL_SHUTDOWN,set local-preference 0 |
| 禁用会话 | birdc disable <protocol> |
neighbor X shutdown |
| 重载配置 | birdc configure |
write memory然后clear ip bgp *或重启 |
监控故障切换事件
设置监控以在故障切换发生时收到告警。在Linux上使用BGPalerter监控BGP公告介绍了BGPalerter的路由监控。至少应监控:
- BGP会话状态变化:
journalctl -u bird或journalctl -u frr - BFD会话震荡:
birdc show bfd sessions/vtysh -c "show bfd peers" - 路由数量变化:如果导出前缀数量降为零则告警
故障排除
BGP会话卡在Active/Connect状态:
- 检查TCP 179的防火墙规则
- 确认对等端IP和ASN与上游期望的一致
- 查看
journalctl -u bird -f或journalctl -u frr -f的错误信息
BFD会话卡在Down状态:
- UDP端口3784和3785必须双向开放
- 确认对等端支持BFD并已配置
- 检查路径上的MTU问题
MED不影响入站流量:
- MED只在来自同一AS的路径之间比较。如果上游是不同AS,请改用AS-path prepending
- 一些上游按策略忽略MED。请咨询你的提供商
优雅关闭community未被尊重:
- 对等端必须明确支持RFC 8326。并非所有上游都支持
- 向你的提供商确认是否尊重GRACEFUL_SHUTDOWN community
- 一些实现需要显式配置才能尊重该community
流量未切换:
- 确认两个节点都在宣告相同前缀:
birdc show route export upstream_v4或vtysh -c "show ip bgp neighbors X advertised-routes" - 从外部looking glass检查,不要从节点本身检查
- 如果你在anycast前缀上使用按位置分配的IP提供服务,DNS TTL可能使客户端继续指向旧IP