线上排障里最怕什么不是报错本身而是报错把你带偏。这次我遇到的问题就是一个很典型的例子容器启动后健康检查一直失败日志里反复出现curl: (7) Failed connect to 127.0.0.1:8090; Connection refused第一眼看上去像是端口没开、服务没起、或者 health check 写错了。但真正排下来之后才发现8090 没起来只是结果根因是应用在启动初始化阶段因为数据库缺表直接失败了。这类问题很适合复盘因为它体现的不是“会不会看日志”而是“会不会顺着链路找根因”。一、问题背景这个容器的启动流程里健康检查脚本会访问http://127.0.0.1:8090/ali/api-gateway/checkHealth但实际执行时返回的是curl: (7) Failed connect to 127.0.0.1:8090; Connection refused从网络现象上看这意味着非常明确的一点目标端口没有进程监听。也就是说这不是接口 500也不是返回内容不符合预期而是服务本身没有真正跑起来。二、排查过程1先确认是不是 8090 根本没监听我先在容器里看端口状态ss -lntp | grep 8090输出为空说明 8090 没有任何监听进程。随后我又看了另一个端口ss -lntp | grep 45678发现有进程staragent-gatew在监听 45689。这一步让我确认不是容器完全没服务而是目标服务没有按预期起在 8090 上。2确认 8090 是谁负责拉起的接着我看容器启动信息docker inspect 9c20ae7da9d6 | grep -i -E 8090|port|health|start这里看到两个关键点容器入口是/data/bin/start环境变量里有ali_mgr_server_port8090这说明 8090 不是我猜出来的而是系统设计上就应该由这套服务监听。所以问题不是 health check 脚本写错了而是负责启动 8090 的主服务没有成功起来。3顺着启动脚本往下追我继续查看/data/bin/start发现它并不是单纯启动一个进程而是先做一系列初始化动作然后再启动 Java 应用cd /data nohup java ${SERVICE_OPTS} -jar /data/manage_new-1.0-SNAPSHOT.jar --spring.profiles.active${apsara_stack_version} /data/manage_new.log 21 也就是说真正提供 8090 服务的是这个 Java 包manage_new-1.0-SNAPSHOT.jar排查思路到这里已经很清楚了不是端口配置问题而是 Java 服务启动失败。4最终在应用日志里找到根因我继续看/data/manage_new.log发现了关键异常java.sql.SQLSyntaxErrorException: Table scm.task_registration doesnt exist这条日志把整条故障链路彻底串起来了容器启动执行/data/bin/start脚本拉起manage_new-1.0-SNAPSHOT.jarJava 应用在初始化阶段访问数据库它尝试操作scm.task_registration但这张表不存在Spring 初始化失败Java 服务没有启动成功8090 没有监听health check 报Connection refused三、根因分析这次故障的根因不是健康检查脚本也不是端口配置而是数据库表缺失导致应用启动初始化失败。更具体一点服务启动时会执行初始化逻辑初始化逻辑依赖scm.task_registration数据库中这张表不存在导致 Spring 容器初始化失败Java 主服务没有启动成功8090 自然也不会监听这类问题很容易被“表象”误导。如果只看健康检查失败容易以为是脚本问题如果只看端口没监听容易以为是服务没拉起来但真正的根因要回到应用日志和数据库依赖上去找。四、排障思维图 / 流程图可以把这次排查过程总结成下面这个思路图现象health check 失败 | v curl 报错Connection refused | v 确认 8090 是否监听 | v ss -lntp 发现 8090 无进程 | v 反查容器启动配置 | v docker inspect 发现入口 /data/bin/start | v 查看启动脚本 | v 发现由 manage_new-1.0-SNAPSHOT.jar 提供服务 | v 查看 /data/manage_new.log | v 发现数据库异常task_registration 表不存在 | v 根因应用启动初始化失败 - 服务未启动 - 8090 未监听五、排障方法论从表象到根因的四步法这次排查里我总结出一套很实用的方法尤其适合线上故障、容器启动失败、健康检查异常这类场景。1先定义现象不急着下结论看到报错后先判断它属于哪一类问题是连接失败、超时、接口错误还是业务异常。像这次的Connection refused本质上先说明的是端口没有监听而不是接口返回内容有问题。2先确认链路中的关键节点排障不要一上来改代码而是先看最关键的几个节点服务有没有启动端口有没有监听容器入口是什么启动脚本干了什么应用日志里有没有异常这一步的目的是把问题从“可能很多”缩小到“少数几个高概率点”。3顺着调用链往后追不要只盯着最外层报错要一直往后追到真正执行出错的地方。这次就是从health check 失败 → 端口未监听 → 启动脚本 → Java 应用 → 数据库异常一路追到了task_registration表不存在。4最后回到根因而不是停留在修表象很多人排障容易停在第一层比如改 health check、改端口、重启服务。但如果根因是数据库缺表不解决表结构问题一定会反复出现。所以最后一定要回答三个问题它为什么会失败是哪一层出了问题怎么避免下次再发生如果再把它压缩成一句话就是先看现象再查链路最后找根因不要修结果要修原因。