Hotdry.

Article

Railway 被 GCP 阻断后的实时流量迁移:从 DNS 秒级切换到跨云负载均衡的工程实践

基于 Railway 2024 年 12 月 GCP 网络阻断事故,拆解 Terraform 配置陷阱、DNS 秒级切换策略与多云负载均衡的工程实现参数。

2026-05-20systems

2024 年 12 月 16 日,Railway 的一次常规配置变更意外演变为全网入站流量阻断事故。工程师在清理历史遗留的静态 IP 封禁列表时,将空数组传入 Terraform,导致 GCP 防火墙规则从 "阻断特定 IP" 变成 "阻断所有 IP"。这场持续约 10 分钟的事故,以及 Railway 长期以来与 GCP 的摩擦,最终推动其构建了一套从 DNS 秒级切换到跨云负载均衡的韧性架构。

Terraform 配置陷阱:空数组与 nil 的语义鸿沟

事故的直接诱因是一个看似无害的代码变更。Railway 过去维护着静态和动态两套 IP 封禁机制,随着 DDoS 防护能力的提升,他们决定完全迁移到动态方案,并清理遗留的静态封禁列表。

在 Terraform 配置中,工程师将 source_ranges 设置为空数组 [],意图表示 "没有 IP 需要封禁"。然而 GCP Terraform Provider 的实现逻辑是:当 source_ranges 未提供或为空时,默认将规则应用于所有 IP。由于 Terraform 使用 Go 语言编写,在 Go 的类型系统中,空数组与 nil 没有本质区别,这一语义差异导致防火墙规则从 "阻断空列表" 被解释为 "阻断一切"。

这一陷阱的隐蔽性在于,它违背了开发者的直觉预期。在大多数编程语境中,空集合意味着 "无操作",但 GCP 的防火墙规则语义恰恰相反 —— 未指定源范围即表示 "所有源"。Railway 在事后审计中发现,类似的宽松规则还存在于其他遗留配置中,这促使他们建立了更严格的防火墙规则审查机制。

实时流量迁移:DNS 秒级切换的工程参数

面对全网阻断,Railway 的应急响应遵循 "先回滚,后调查" 的原则。从变更合并到完全恢复,整个过程用时约 12 分钟,其中关键的时间消耗在于两个环节:告警通知路由到错误的值班人员,以及 Terraform 在回滚时执行了不必要的 plan 阶段。

这次事故催生了 Railway 对流量迁移能力的重新设计。在理想的多云架构中,当某一云提供商出现网络层阻断时,应当能够在秒级完成流量切换。实现这一目标需要以下工程参数:

DNS TTL 设置:将关键域名的 TTL 设置为 30-60 秒,确保 DNS 变更能够在全球范围内快速传播。过短的 TTL 会增加 DNS 服务器负载,但在故障场景下这是可接受的权衡。

健康检查频率:负载均衡器的健康检查间隔应设置为 5-10 秒,连续失败 2-3 次即判定为不健康。这能够在 15-30 秒内检测到后端故障并触发流量切换。

多区域部署:在每个地理区域至少部署两个云提供商的节点,确保单一云故障不会导致该区域服务中断。

Railway 在事故后优化了告警路由逻辑,将配置变更相关的告警直接发送给提交变更的工程师,而非轮值的值班人员。同时,他们在 Terraform 回滚流程中增加了 "紧急模式",跳过 plan 阶段直接执行 apply,将回滚时间从 3 分钟压缩到 30 秒以内。

多云架构设计:从单云依赖到多活隔离

Railway 与 GCP 的摩擦并非始于这次事故。2023 年 12 月,他们经历了一次更为严重的事件:GCP 的虚拟化层在资源压力下触发 softlock,导致机器无法自动故障转移。当时,Railway 的工程师在串口日志中发现了 kvm_wait__pv_queued_spin_lock_slowpath 等内核级错误,这些错误与 GCP 的嵌套虚拟化实现相关。

更早之前,GCP 的 Artifact Registry 还曾无故将 Railway 的配额限制降至零,导致镜像分发严重延迟。面对这一系列问题,Railway 决定构建自主可控的基础设施:

自有网络栈:基于 eBPF 和 IPv6 Wireguard 构建了覆盖全平台的私有网络,替代了 GCP 的 VPC 网络。这一架构不仅解决了 GCP 的网络抖动问题,还为跨云通信提供了统一的抽象层。

裸金属部署:在每个区域部署自有裸金属服务器,通过 BGP 协议的 L3 网络 fabric 实现交换机级别的故障隔离。这与依赖单一云提供商的虚拟化层相比,提供了更高的可预测性和可控性。

多云负载均衡:在保留 GCP 支持的同时,引入 AWS 作为备选提供商,并在多个数据中心之间实现负载分担。这种设计使得单一云提供商的阻断不会导致服务中断,而是触发自动的流量迁移。

工程实践清单:可落地的参数与监控点

基于 Railway 的事故复盘和架构演进,以下是构建云阻断韧性系统的实践清单:

配置管理

  • 对 Terraform 等基础设施即代码工具进行空值语义审计,特别关注数组、列表类型的默认值行为
  • 建立防火墙规则的白名单审查机制,禁止无源范围限制的阻断规则
  • 实施配置变更的自动化测试,包括 dry-run 和 impact analysis

应急响应

  • 建立 "变更者告警" 机制,配置变更触发的告警直接通知提交者
  • 优化回滚流程,支持跳过 plan 阶段的紧急回滚模式
  • 定义明确的 RTO(恢复时间目标)和 RPO(恢复点目标),并定期进行演练

监控与可观测性

  • 在串口控制台、内核日志等低层级采集点设置监控,捕获虚拟化层的异常信号
  • 建立跨云的健康检查矩阵,确保每个区域至少有两个独立的健康检查源
  • 监控 DNS 传播延迟,确保 TTL 变更能够按预期生效

架构设计

  • 实施至少双云提供商策略,避免单一云锁定
  • 在每个区域部署异构基础设施(裸金属 + 多云虚拟机)
  • 设计无状态服务架构,确保实例可以在不同云之间无缝迁移

Railway 的经历表明,云提供商的阻断可能来自账号层面、网络层面或虚拟化层,而构建韧性系统的关键在于假设故障必然发生,并为此设计自动化的故障隔离和流量迁移机制。


资料来源

  • Railway, "Incident Report: December 16th, 2024", blog.railway.com
  • Railway, "Not Everything Is Google's Fault (Just Most Things)", blog.railway.com, 2023-12-01

systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com