凌晨两点,下单接口开始返回500,客服电话接连打进来,运营在群里一条接一条地催。登上云服务器一看,应用进程还在,CPU和内存都不算高,可页面就是打不开。遇到这种情况,多半是几处问题叠在一起。云服务器应用程序错误的排查,最好从层级入手,按顺序看系统、应用、网络和配置。

先判断错误出在哪一层
用户看到的报错页面,未必来自应用本身。请求可能先经过负载均衡,再到Web服务器,最后才进入应用进程,途中还可能查询数据库或调用第三方接口。不同状态码指向的位置差别很大,可以先对照下面几类表现做初步判断:
- HTTP 502、504:反向代理连不上后端,或者后端处理太慢,超过了代理的等待时间。
- HTTP 500:应用代码抛出了没有捕获的异常,要去应用日志里找堆栈信息。
- 进程退出或反复重启:多见于内存不够、依赖缺失或启动参数写错。
- 连接被拒绝或超时:先查安全组、端口监听和服务器防火墙,应用内部通常没问题。
三个真实场景里的线索
内存不足,进程被系统直接杀掉
某内容社区把服务部署在2核4G的云服务器上,每到晚高峰就断一次。应用日志里看不出什么异常,查系统日志才发现,内核的OOM Killer多次终止了Java进程。JVM堆内存设成了3G,加上系统和其他服务的占用,总量早已超过物理内存。把堆上限调低到合理范围,同时给缓存加上淘汰策略,问题就没再出现。这类故障只会在系统日志里留下痕迹,应用日志通常是安静的。
部署后环境变量丢了
一个团队用脚本部署Node.js应用,本地跑得好好的,上线后却连不上数据库,日志里只有一行”Connection refused”。排查后发现,部署脚本通过新的SSH会话启动服务,而数据库地址和密码只写在用户的shell配置文件里,由systemd启动的服务读取不到。后来改用服务单元文件中的EnvironmentFile统一管理配置,问题就解决了。遇到”连不上”的报错时,先确认进程实际拿到的环境变量是什么,再去怀疑数据库。
应用在监听,外面却访问不了
某团队的API服务日志显示已经在8080端口正常监听,外部请求却一直超时。在服务器本机用curl访问,结果是正常的,说明应用本身没有问题。最后检查云平台的安全组,发现只放行了22和80端口。补上8080的入站规则后恢复正常。应用在本机能响应,只说明服务进程没问题,外部能不能访问要单独确认。
排查顺序
处理云服务器应用程序错误时,建议按下面的顺序走,避免没有依据地反复重启:
- 用systemctl status或docker ps确认进程是否还活着,退出码和最近的启动时间都要记下来。
- 读应用日志的最后几百行,但重点找最早出现异常的那一条。连锁故障里,最后一条报错往往只是结果。
- 查系统日志,看有没有OOM、磁盘写满或网络中断的记录。可以用journalctl按时间段过滤,缩小范围。
- 分别在服务器本机和外部访问服务。本机通、外部不通,问题多半在安全组或网络;两边都不通,再回到服务本身。
- 检查依赖的数据库、缓存和第三方接口,确认地址、账号和凭证仍然有效,特别是密码轮换之后。
- 对比最近一次变更,包括代码发布、配置修改和依赖升级。故障往往紧跟在某次改动之后出现。
找到原因再动手修。临时性的故障重启一次就能恢复,但如果根因没查清,重启只是把问题暂时藏起来,过段时间还会再来。
减少出错的机会
排查得快很重要,但更省事的办法是让问题早点暴露,并且把影响控制在小范围内。下面几件事投入不大,回报比较明显:
- 用systemd或Supervisor托管进程,并设置合理的重启策略,避免进程挂掉后没人察觉。重启次数过多时,要设上限并触发告警,而不是无限重启。
- 配置和代码分开管理,数据库密码、密钥这类敏感信息交给密钥管理服务注入,不要硬编码进仓库。
- 给错误率、响应时间、内存和磁盘使用率设置告警。阈值可以先宽一点,跑一段时间后再根据真实流量收紧。
- 日志统一采集到集中平台,保留时间要够长,至少覆盖一次完整的业务周期,方便回溯。
- 发布前先在预发布环境完整跑一遍主要流程,并保留上一个可用版本,出问题时能在几分钟内回滚。