VPC对等连通故障我查了2小时17分
故障现场
周二 19:07,我在华北-北京四做小流量切库,计划让 order-service 从 VPC-A 连到 VPC-B 的 MySQL。发布窗口 30 分钟,我自己在群里写的是“预计 20:00 前收口”。应用机是 ECS c7.large.2,4 vCPU / 8 GiB,Ubuntu 22.04.3,内核 5.15.0-103-generic;数据库机是 ECS s7.xlarge.2,8 vCPU / 16 GiB,EulerOS 2.0 SP10,MySQL 8.0.34,字符集 utf8mb4,端口 3306。发布单 CHG-20260618-147,版本 order-service 2.19.4,Git 提交 7f3c9ab,JDK 17.0.10,镜像 digest sha256:8f1f...3e2a。
- VPC-A:10.20.0.0/16,应用子网 10.20.12.0/24,实例 IP 10.20.12.17
- VPC-B:10.31.0.0/16,数据库子网 10.31.6.0/24,实例 IP 10.31.6.23
- 对等连接:pcx-58c2a1,状态 已接受,创建时间 2026-06-17 14:26:11
- 灰度比例:19:09 从 5% 提到 20%,19:13 原计划提到 35% 但被我手动暂停
19:11 开始,订单写入接口错误率从 0.2% 抬到 11.6%,19:15 到 37.0%,CES 连续触发 3 次 1 分钟告警,告警 ID 分别是 a-7d11、a-7d12、a-7d13。5 分钟窗口失败 3124 次,P95 从 86ms 变成 4.7s,P99 到 8.9s,超时几乎都卡在建连阶段。
2026-06-18 19:14:08.912 ERROR [order-service,,,] o.h.e.j.s.SqlExceptionHelper
SQL Error: 0, SQLState: 08S01
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
Caused by: java.net.ConnectException: Connection timed out
HikariPool-1 - Connection is not available, request timed out after 3000ms.
这次我被自己复制模板的习惯坑了两次。
我一开始把锅甩给应用
19:19 到 19:39 这段我一直盯应用侧,怀疑是 Spring Boot 2.6 升 2.7.18 后配置没热更新。我先核对配置中心,再查本地落盘,再看 Actuator,三个地方的 JDBC URL 都是 10.31.6.23:3306,没有串环境。
date
# Tue Jun 18 19:19:42 CST 2026
grep datasource.url /opt/order-service/config/application-prod.yml
# spring.datasource.url=jdbc:mysql://10.31.6.23:3306/order?connectTimeout=2000&socketTimeout=3000
curl -s http://127.0.0.1:18080/actuator/env | grep -A2 datasource.url
# propertySource: applicationConfig
# value: jdbc:mysql://10.31.6.23:3306/order?connectTimeout=2000&socketTimeout=3000
nc -vz -w 2 10.31.6.23 3306
# nc: connect to 10.31.6.23 port 3306 (tcp) timed out: Operation now in progress
traceroute -n 10.31.6.23
# 1 10.20.12.1 0.41 ms
# 2 * * *
这 20 分钟里我重启了两次服务,19:23 一次、19:27 一次,systemd 记录的启动耗时分别是 41 秒和 44 秒,完全没改善。
19:31 我做了个失败的止血尝试,把 Hikari 参数从 connection-timeout=3000 改到 8000,maximum-pool-size=32 改到 24,还把 minimum-idle 从 8 降到 4,想的是减少同时建连数量,别把业务线程一下子全堵死。改完重启后,应用侧等待线程从 73 个涨到 119 个,错误率 37.0% 变 38.4%,RT 更难看,这一步纯属自我安慰。
# 19:31 临时改动(20:42 已回滚)
spring.datasource.hikari.connection-timeout=8000
spring.datasource.hikari.maximum-pool-size=24
spring.datasource.hikari.minimum-idle=4
jstack 22173 | grep -n 'java.net.PlainSocketImpl.socketConnect' | head -3
# 417: at java.net.PlainSocketImpl.socketConnect(Native Method)
# 893: at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:399)
# 1204: at java.net.Socket.connect(Socket.java:648)
tcpdump -nn -i eth0 host 10.31.6.23 and port 3306 -c 5
# 5 packets captured
# 5 SYN sent, 0 SYN-ACK received
我那时已经开始怀疑自己眼睛出了问题。
直到看到 SYN 发出去没有回包,我才承认方向错了,把排查点切到 VPC 对等链路。
路由表里抄错了网段
对等连接状态一直是“已接受”,我先查了 A 侧路由,目标 10.31.0.0/16 指向 pcx-58c2a1,没问题。B 侧回程路由里我写成了旧网段 10.20.10.0/24,新应用子网其实是 10.20.12.0/24,这个错误来自 5 月份旧工单模板,我只改了资源名,CIDR 漏改。
# VPC-B 路由表 rtb-b9f3(19:46 导出)
10.31.0.0/16 local
10.20.10.0/24 pcx-58c2a1
0.0.0.0/0 nat-3f91d7
# 20:03 修改后
10.31.0.0/16 local
10.20.12.0/24 pcx-58c2a1
0.0.0.0/0 nat-3f91d7
我用 API 改的,不是控制台点点点,调用记录里 requestId 是 6b2d9f7a-1c44-4a6b-9d2f-8e7d41d5a1bf。提交时间 20:03:12,控制台显示 20:03:40 生效,28 秒。生效后错误率从 37% 掉到 31%,说明路径通了一段,但还没完全通。
我当时以为快收工了。
安全组又挡了一次
20:11 我登数据库机核对服务,MySQL 监听正常,系统防火墙是 inactive。应用侧还在超时,我去看安全组才发现第二个坑,sg-db-prod 入方向来源也是旧网段 10.20.10.0/24。路由修完后,包能到 VPC-B,但被安全组丢掉。
ss -lntp | grep 3306
# LISTEN 0 70 0.0.0.0:3306 0.0.0.0:* users:((mysqld,pid=1246,fd=36))
systemctl is-active firewalld
# inactive
# sg-db-prod 入方向(20:16)
# rule-id sgr-91aa3c TCP 3306 Source: 10.20.10.0/24
# 流日志片段(20:18)
# src=10.20.12.17 dst=10.31.6.23 dport=3306 action=REJECT bytes=60
# 修正后(20:24)
# rule-id sgr-91aa3c TCP 3306 Source: 10.20.12.0/24
我没有直接放宽到 10.20.0.0/16,这个我当时确实犹豫过 1 分钟。内网基线要求数据库来源按业务子网收敛,放成 /16 第二天巡检会出高危项,还要补风险说明和审批记录,时间上更慢。
20:24:19 改完安全组,20:24:41 在应用机用 mysql -h 10.31.6.23 -u order_app -p 成功握手,20:27 错误率回到 0.6%,20:35 回到 0.3%。从 19:07 报警到恢复一共 2 小时 17 分。
我加的防呆和还没做完的事
这次额外云上费用几乎可以忽略。对等连接账单是 0 元,我临时开了 2 小时 VPC 流日志,写入 LTS 0.42GB,账单 0.18 元。真正的损失是排班人力和发布窗口,原定 20:30 的营销活动发布推到第二天 10:00,业务同学在群里等了 13 小时。
故障后我把手工步骤落进代码,Terraform 模块固定网段变量,pre-commit 里加网段一致性检查,CodeArts 在灰度前加 15 秒连通性探测。这个改动 6 月 21 日上线,跑了 7 次,拦住过 1 次测试环境的错误网段提交。
# variables.tf
variable 'app_subnet_cidr' { default = '10.20.12.0/24' }
variable 'db_subnet_cidr' { default = '10.31.6.0/24' }
# pre-commit
python3 check_net.py --src-vpc 10.20.0.0/16 --dst-vpc 10.31.0.0/16
# route_ok: true
# sg_source_match: false (sg-db-prod expects 10.20.10.0/24)
# CodeArts step timeout=15
nc -vz -w 2 10.31.6.23 3306
nc -vz -w 2 10.31.8.14 6379
curl -m 2 -sSf http://10.31.9.21:8080/health
还没做完的是自动巡检,我现在每周五 17:00 手工跑一次 python3 audit_pcx_sg.py,单次 43 秒;下一步是把它塞进 CodeArts 定时任务,每 15 分钟执行一次,命中不一致就自动提工单并 @ 当班人。
- 点赞
- 收藏
- 关注作者
评论(0)