当前位置: 首页 > news >正文

生产网络故障复盘:网络分割与灰度发布事故

文章目录

    • 一、背景:灰度发布前的例行变更
    • 二、完整时间线:从变更到全站网络分割
      • 2.1 时间线(均为北京时间)
      • 2.2 每一步发生了什么
    • 三、根因分析:不是一条路由的问题,是三层网络的联动失效
      • 3.1 根因链
      • 3.2 为什么申请单上写着"预计影响:无"
      • 3.3 三个技术层面的失效点
    • 四、诊断命令汇总:事故中用到的每一条命令
      • 4.1 路由层诊断
      • 4.2 DNS 层诊断
      • 4.3 连接状态层诊断
      • 4.4 服务发现层诊断
      • 4.5 连接池层诊断
    • 五、反思:网络变更应该怎么做
      • 5.1 路由变更的完整检查清单
      • 5.2 变更窗口的灰度策略
      • 5.3 为什么网络变更特别需要"变更隔离"
    • 六、预防措施:从这次事故中学到的
      • 6.1 技术层面的改进
      • 6.2 组织层面的改进
    • 七、尾声:从故障复盘到防御体系

核心问题:一次不谨慎的 VPC CIDR 调整如何引发了全站网络分割?路由变更的连锁反应是如何一步步传导的?


一、背景:灰度发布前的例行变更

某中型互联网公司,Kubernetes 集群规模约 200 个节点,业务覆盖华东和华南两个区域,采用阿里云 VPC 网络架构。

2026 年 6 月 19 日,网络团队发起了一项"例行变更":为即将上线的新微服务网段10.20.0.0/16)添加 VPC 路由表条目,允许集群节点访问新网段的 API Gateway。

变更申请单内容:

变更类型:网络路由新增 涉及范围:VPC 主路由表 变更内容:新增路由 10.20.0.0/16 → NATGW-xxxx(NAT 网关) 变更时间:2026-06-19 14:00 ~ 14:30 预计影响:无 审批人:网络团队负责人 抄送:应用运维

申请单上写着"预计影响:无"——这是整个事故的起点。


二、完整时间线:从变更到全站网络分割

2.1 时间线(均为北京时间)

14:00
变更执行:添加 10.20.0.0/16 路由

14:03
部分 Pod 无法解析内网 DNS(CoreDNS 超时)

14:05
华东区 API Gateway 报错:上游服务全部不可达

14:08
应用运维接到告警,尝试重启 CoreDNS Pod

14:12
重启后短暂恢复,14:14 再次崩溃

14:15
数据库连接池异常(连接超时)

14:18
灰度发布被紧急中止

14:22
网络团队确认 NAT 网关路由异常

14:35
回滚 10.20.0.0/16 路由

14:38
服务完全恢复

2.2 每一步发生了什么

14:00 —— 变更执行

网络工程师在阿里云控制台为主路由表新增了一条路由:

目标网段:10.20.0.0/16 下一跳类型:NAT 网关(NATGW-xxxx)

这看似是一条无害的路由添加——只是告诉 VPC"去 10.20.0.0/16 的包走 NAT 网关"。

然而,阿里云 VPC 的路由表中已经存在一条默认路由

目标网段:0.0.0.0/0 下一跳类型:NAT 网关(NATGW-xxxx)

14:03 —— CoreDNS 解析超时

CoreDNS Pod 尝试解析一个内网服务的域名inventory.prod.svc.cluster.local。解析路径是:

应用 Pod → kubedns(本地 DNS 缓存)→ CoreDNS(集群 DNS 服务)→ 上游 DNS(VPC DNS 168.63.129.16)

CoreDNS 向 VPC 内网 DNS(168.63.129.16)发起递归查询时,包需要经过 NAT 网关出公网再回来——而 NAT 网关此时正同时处理新网段的路由和原有的 DNS 递归返回流量,出现了路由优先级冲突。

根因:VPC DNS 的回程路由经过 NAT 网关,而 NAT 网关同时处理了新的10.20.0.0/16路由。阿里云路由表中存在双重 default 路由的隐性冲突(新网段路由和 NAT 网关 default 路由都指向同一个 NAT 网关),导致 NAT 网关的 SNAT 表(源地址转换表)溢出。

这一步的诊断关键命令:

# 在节点上查看 DNS 解析路径的每一步延迟dig+trace +all inventory.prod.svc.cluster.local @168.63.129.16# 查看 NAT 网关的 SNAT 连接表使用量(阿里云控制台 → NAT 网关 → 监控)# 如果 SNAT 连接数接近规格上限,说明 SNAT 表溢出了

14:05 —— 上游服务不可达

当 CoreDNS 解析超时后,应用 Pod 的 Kubernetes Service 域名无法解析。Kubernetes Service 的 DNS 解析是延迟绑定的——解析失败时,Envoy/Istio Sidecar 无法获取目标 endpoint,只能返回 503。

应用 Pod → Envoy Sidecar(15001)→ DNS 解析 inventory.prod.svc.cluster.local ↓ 超时(3s 默认) ↓ 无 endpoint 返回 ↓ HTTP 503

14:08 ~ 14:14 —— 短暂恢复后再次崩溃

应用运维尝试重启 CoreDNS Pod 以刷新 DNS 缓存。CoreDNS Pod 重启后短暂恢复了 DNS 解析(因为新的 CoreDNS Pod 重新建立了到 VPC DNS 的连接),但由于 NAT 网关的 SNAT 资源仍未释放,30 秒后再次触发连接超时。

14:15 —— 数据库连接池异常

上游服务(API Gateway)长期调用超时,连接池中的 HTTP 连接被长时间占用无法释放。当数据库连接请求到达时,连接池已经没有可用连接——连接池满触发了"快速失败"(fail-fast)策略,直接拒绝新请求。

数据库连接池(max=100) ├─ 业务请求占用 85 个(等待上游响应) ├─ 空闲连接 15 个 └─ 新请求 20 个 → 15 个成功,5 个被拒绝(pool exhausted)

14:18 —— 灰度发布紧急中止

由于所有区域的服务健康检查都在失败,灰度发布流水线自动触发了中止条件——这是 CI/CD 流水线中"服务健康检查不通过自动回滚"机制的正确触发,说明该机制在这次事故中没有让故障进一步扩大。

14:22 —— 根因定位

网络团队在阿里云控制台发现:主路由表中除了原有的0.0.0.0/0default 路由外,新增的10.20.0.0/16路由覆盖了原有 NAT 网关的流量路径——新网段路由与 NAT 网关 default 路由存在隐性竞争关系,导致 NAT 网关的 SNAT 表资源异常消耗。

14:35 —— 回滚

删除10.20.0.0/16路由条目,服务开始逐步恢复。

14:38 —— 完全恢复


三、根因分析:不是一条路由的问题,是三层网络的联动失效

3.1 根因链

根因:VPC 路由表变更
(新增 10.20.0.0/16 → NAT 网关)

第一层:NAT 网关 SNAT 表溢出
(默认路由 + 新网段路由同时竞争
同一个 NAT 网关的 SNAT 资源)

第二层:VPC DNS 递归查询失败
(CoreDNS 的上游查询无法返回)

第三层:Kubernetes Service DNS 解析失败
(域名无法解析为 Pod IP,endpoint 为空)

第四层:Envoy/Istio Sidecar 无法路由
(无 endpoint → HTTP 503)

第五层:数据库连接池耗尽
(上游超时 → 连接占用 → 新请求被拒)

第六层:全站服务降级
(API Gateway、支付、订单等依赖链全线崩溃)

3.2 为什么申请单上写着"预计影响:无"

这是整个事故中组织层面最值得反思的点

网络团队的变更申请单只评估了"新网段能不能通",没有评估"这条路由会不会影响 NAT 网关的既有流量"。具体来说:

  • 路由优先级评估缺失。阿里云 VPC 路由表中,最长前缀匹配(LPM)原则决定路由选路——10.20.0.0/160.0.0.0/0更精确,理论上应该优先走新路由。但 NAT 网关的 SNAT 表资源是共享的,新旧流量同时到达同一个 NAT 网关,造成了 SNAT 连接数超出规格上限。

  • DNS 依赖链未纳入评估。变更申请没有评估 CoreDNS 对 VPC DNS 的依赖路径——DNS 解析失败是这次事故的第一个可见症状,但申请单中完全没有提到 CoreDNS 或 DNS 相关的服务。

  • 跨团队通知流于形式。变更抄送了"应用运维",但应用运维只看到了抄送,没有理解这条变更会如何影响 CoreDNS 和 Service 间的 DNS 解析。

3.3 三个技术层面的失效点

失效点一:路由变更与 DNS 递归的隐式耦合

VPC DNS(168.63.129.16)的响应包返回路径依赖 NAT 网关。当 NAT 网关 SNAT 表溢出时,响应包无法正确返回,CoreDNS 的上游查询一直等待,最终超时。

大多数工程师知道"VPC DNS 在内网,不需要 NAT 网关"——但这个认知是基于 SNAT 资源充足的前提的。一旦 NAT 网关的 SNAT 连接数接近上限,所有经过该 NAT 网关的出站流量都会受影响。

失效点二:健康检查未能提前发现

Kubernetes ReadinessProbe 和 livenessProbe 都是针对 Pod 自身的检查(HTTP 探测端口),不检查 Pod 到核心依赖(CoreDNS、数据库)的连通性。这意味着即使 CoreDNS 已经解析超时,Pod 自身的健康检查仍然通过,Kubernetes 不会主动驱逐这个 Pod。

失效点三:灰度发布流水线的健康检查阈值

灰度发布流水线的健康检查默认等待 3 分钟(initialDelaySeconds+periodSeconds × failureThreshold)。在这 3 分钟内,CoreDNS 已经反复重启了一次,但流水线仍在等待。如果健康检查阈值更严格(例如 60 秒内失败 2 次立即回滚),可以在第一轮 CoreDNS 崩溃时就中止灰度发布。


四、诊断命令汇总:事故中用到的每一条命令

4.1 路由层诊断

# 确认节点的路由表(验证变更是否已生效)iprouteiproute show table all# 验证到目标网段的路由路径iproute get10.20.0.1# 检查是否有重复的 default 路由iproute show|grep-E"default|^0\.0\.0\.0"# 阿里云:查看 VPC 路由表(OpenAPI)aliyun vpc DescribeRouteTables--VpcIdvpc-xxxx

4.2 DNS 层诊断

# DNS 递归查询的完整路径(+trace 输出每一跳)dig+trace +all inventory.prod.svc.cluster.local# 查看 CoreDNS 的实时查询延迟(进入 CoreDNS Pod 执行)kubectlexec-nkube-system deploy/coredns -- coredns-dig metrics# 或直接看 CoreDNS 日志kubectl logs-nkube-system deploy/coredns-f|greptimeout# 验证 VPC DNS 是否可达dig@168.63.129.16 kubernetes.default.svc.cluster.local

4.3 连接状态层诊断

# 查看当前节点的 SNAT 连接数(阿里云 NAT 网关监控)# 控制台路径:NAT 网关 → 监控 → SNAT 连接数# 查看节点上的 NAT 转发状态cat/proc/net/nf_conntrack|grepNAT# 查看 NAT 表溢出时的内核日志dmesg-T|grep"nf_conntrack"|tail-20# 如果看到 "nf_conntrack: table full, dropping packet",# 说明连接跟踪表已满,这是 NAT 网关 SNAT 溢出的内核层表现

4.4 服务发现层诊断

# 查看某 Service 的 endpoint 是否存在kubectl get endpoints inventory-nprod# 如果 endpoint 为空,说明没有 Pod 被 Kubernetes 选中(可能是 DNS 解析失败)# 对比:有 endpoint 时的状态kubectl get endpoints nginx-ndefault# 查看 Consul(如果使用 Consul 做服务注册)的健康检查状态consul services health-namespace=prod# 如果显示 "critical",说明 Consul 健康检查也在失败

4.5 连接池层诊断

# 查看数据库连接池状态(假设使用 PgBouncer)psql-hlocalhost-p5432-Upgbouncer-c"show pools"# 查看应用层的连接等待时间(应用 metrics)curl-slocalhost:9090/metrics|grepconnection_pool_wait_time# 如果该指标持续上升,说明连接池正在积压等待请求

五、反思:网络变更应该怎么做

5.1 路由变更的完整检查清单

在执行任何 VPC 路由表变更之前,应该逐项检查:

检查项

1. 新路由是否与
现有路由重叠?

2. 新路由的下一跳
NAT 网关/网关的规格是否
支撑总流量?

3. NAT 网关的
SNAT/DNAT 连接数
是否有余量?

4. 核心依赖
(CoreDNS/数据库/缓存)
的出站路径是否经过
该 NAT 网关?

5. DNS 递归查询
的返回路径是否会
受到变更影响?

检查项一:路由重叠。用最长前缀匹配原则验证新路由不会意外覆盖重要流量。

检查项二:NAT 网关规格。SNAT 连接数有规格上限(阿里云 NAT 网关 Basic 版:默认每秒新建连接数 10 万,最大并发连接数 100 万)。如果新流量预计超过规格的 50%,应该提前扩容或分流。

检查项三:核心依赖的出站路径。这一步最容易被忽略——CoreDNS、数据库、Redis 的健康检查请求都经过 NAT 网关,如果 NAT 网关 SNAT 表满,这些健康检查全部超时。

5.2 变更窗口的灰度策略

对于涉及网络路由的变更,建议采用以下灰度策略:

第一步:先在测试 VPC 验证(不连接生产网络)

在独立的测试 VPC 中添加相同的路由规则,验证 NAT 网关规格是否足够。

第二步:在非核心命名空间先执行(影响范围最小)

先在测试命名空间执行变更,观察 CoreDNS 日志是否有异常(kubectl logs -n test deploy/coredns | grep timeout)。

第三步:生产环境分区域执行

先在单个可用区(AZ)的节点上执行变更,观察 5 分钟后确认无异常,再推广到全量节点。

第四步:回滚预案必须提前验证

变更前必须验证回滚命令能在 2 分钟内完成执行。这次事故的回滚耗时 3 分钟(14:22 确认根因到 14:35 完成回滚),大部分时间花在"确认哪条路由是罪魁祸首"上——如果申请单中记录了每条路由的上线时间和审批人,确认根因只需要 2 分钟

5.3 为什么网络变更特别需要"变更隔离"

网络层与业务层的本质区别在于:网络是所有业务的前置依赖。Linux 内核参数调错,影响的是当前节点上的进程;网络路由配置错误,影响的是所有跨节点通信——包括 DNS 解析、服务注册、健康检查、数据库连接等。

这意味着网络变更的错误成本远高于普通应用变更。应对策略不是"更加谨慎",而是"隔离执行":每次网络变更影响的节点数量要严格控制,变更窗口要有硬性的"观察期",回滚时间要纳入 SLO 计算。


六、预防措施:从这次事故中学到的

6.1 技术层面的改进

改进项具体措施验证方式
NAT 网关 SNAT 监控告警当 SNAT 连接数超过规格 70% 时触发 P2 告警,超过 90% 时触发 P1 告警模拟 NAT 网关高负载场景,验证告警是否及时触发
CoreDNS 健康检查增强在 CoreDNS 的 health check 中增加对168.63.129.16:53的探测,不仅仅是 HTTP 探测kubectl exec deploy/coredns -- wget -q -O- http://168.63.129.16:53
DNS 解析的熔断机制应用侧设置 DNS 解析超时(建议 ≤ 2s)和重试策略(最多 2 次)在 DNS 解析超时的场景下验证重试逻辑是否生效
灰度发布健康检查收紧健康检查失败 2 次(间隔 30s)即触发自动回滚,而非等待 3 分钟修改流水线配置后执行一次模拟演练

6.2 组织层面的改进

一、变更申请需要包含"影响路径分析"

路由变更申请必须附上:

  • 受影响的 NAT 网关及其 SNAT 连接数当前使用量
  • CoreDNS 到 VPC DNS 的路径依赖说明
  • 需要通知的团队(不能只抄送,要确认对方已阅读并理解)

二、网络团队和应用团队的联合 Review

涉及 NAT 网关或 VPC 路由表的所有变更,必须由网络团队和应用团队的 SRE 共同 Review。特别是 NAT 网关规格的变化(SNAT 连接数),这是大多数变更申请中最容易被忽视的指标。

三、变更文档化

每条上线的路由都必须记录:上线时间、审批人、变更原因、回滚命令。事故发生时,3 分钟的排查时间有一半浪费在确认’哪条路由是罪魁祸首’上——这个成本本可以通过文档化完全消除。


七、尾声:从故障复盘到防御体系

这次事故的根因不是"一条路由配置错误",而是网络层与 DNS 层、服务发现层之间的隐式依赖从未被显式管理。当任何一个前置依赖出现问题时,级联影响从故障发生到被发现之间存在 3 分钟以上的延迟窗口——这段时间足够让一个原本局部的故障演变成全站网络分割。

整个网络与安全系列始终围绕一条核心叙事线展开:排查即线索。TCP 连接失败不是"连接不上",是队列溢出还是端口耗尽;DNS 解析失败不是"域名解析不到",是上游 DNS 超时还是本机 DNS 缓存被污染;网络路由变更也不只是"加一条路由",是 NAT 网关 SNAT 资源、CoreDNS 依赖路径、连接池状态的联动。

掌握这些细节,就是在掌握网络故障的主动权。

http://www.jsqmd.com/news/665409/

相关文章:

  • 如何完全掌控你的微信数据:WeChatMsg终极指南与数字记忆管理实践
  • 选降AI率工具总踩坑?看懂这份排行榜背后的评测标准 - 我要发一区
  • WebPlotDigitizer完全指南:如何从图表图片中快速提取数据
  • 有实力的气动元件一站式服务公司推荐,诚信经营 - 工业设备
  • Windows 10终极精简指南:用Win10BloatRemover让你的旧电脑飞起来!
  • 从电赛到毕设:如何用OpenMV+STM32 HAL库复刻一辆智能送药小车(附完整代码与PCB)
  • ComfyUI-Impact-Pack:解决SAM模型加载失败的3步快速指南
  • 20253901 2025-2026-2 《网络攻防实践》实践5报告
  • Wan2.2-I2V-A14B安全实践:模型API的鉴权、限流与防滥用设计
  • 游戏Mod与安全测试:深入浅出用MinHook实现函数热替换(以修改游戏内存和监控API为例)
  • 抖音下载器:从内容收藏到批量管理的全能解决方案
  • N_m3u8DL-CLI-SimpleG:告别命令行,三步完成M3U8视频下载
  • 分享充电电源车按需定制经验,正规厂家哪家口碑好 - 工业推荐榜
  • 2026年大庆GEO优化公司推荐top5:专业服务商选型参考与核心能力解析 - 商业小白条
  • 探寻通风管道制造商哪家好,玻璃钢、镀锌通风管道厂合作案例多的推荐 - 工业品牌热点
  • 从无人机避障到机器人抓取:深入聊聊双目视觉中‘视差与深度成反比’到底意味着什么
  • Steam成就管理器:3步解锁Steam游戏成就的完整指南
  • 如何一键搞定Android驱动安装:Windows平台终极解决方案
  • HEIF Utility:打破Windows平台HEIF格式壁垒的得力助手
  • Taskbar11完整使用指南:解锁Windows 11任务栏个性化设置
  • MusicFree插件完全解决方案:打造跨平台音乐聚合生态
  • 用Qwen-Image-2512-SDNQ做设计:快速生成粒子特效与流体艺术图
  • 终极指南:如何使用applera1n免费绕过iPhone激活锁(iOS 15-16.6.1)
  • Keil MDK升级到Arm Compiler 6后,我的NO_INIT变量配置踩坑实录与修复指南
  • OpenCore Legacy Patcher:让老旧Mac焕发新生的5步完整指南
  • 网络测试命令
  • VideoDownloadHelper终极指南:解锁网页视频下载的完整解决方案
  • 如何快速配置八大网盘直链下载助手:完整操作指南与实用技巧
  • Pixel Epic部署教程:NVIDIA Jetson Orin边缘设备轻量化运行可行性验证
  • STC89C52单片机频率计DIY全攻略:从信号调理到LCD1602显示,手把手教你避开硬件坑