最近发生了一件诡异的事:跑在一台测试服务器上的一个 Tomcat 的 Docker 容器隔几天都会以 ExitCode: 137 退出。最近几天一直很平稳,没有一个容器退出过,直到今天早上 10 点左右,这台机上所有 Tomcat 容器都一起停掉了(除了两个加了 restart=always 的已经自启了)。
137 意味着什么
容器的退出码 137 等于 128 + 9,表示容器内的主进程收到了 SIGKILL 信号而被强制终止。SIGKILL 的来源通常有两类:
- 内核 OOM killer:宿主机或容器内存不足,内核选中并强制杀掉了进程,此时
docker inspect中会看到OOMKilled: true; - 人为或外部工具执行了
kill -9,或者docker stop超时后被强制终止。
排查过程
既然是 137,第一个怀疑对象自然是 OOM。先用 docker inspect 查看容器的状态:
docker inspect <container_id> --format '{{json .State}}'
输出中 ExitCode 为 137,但 OOMKilled 字段是 false,说明容器并不是被 Docker 以 OOM 为由杀掉的。
再查宿主机内核日志,确认有没有 OOM killer 的记录:
dmesg | grep -i "out of memory"
grep -i "killed process" /var/log/messages
两条命令都没有得到任何 OOM 记录。接着又检查了 Tomcat 自身的日志、系统的定时任务(crontab -l 与 /etc/cron*)以及登录记录(last),都没有发现明显的异常。
分析到这里貌似已经没辙了,如果还有其它方法请在下面留言。
真相
最后我只能想到一个可能性了:公司有一个同事因个人原因在云南远程办公,有可能是被他手动 kill 掉的。确认后果然是他干的(粗暴地把所有 java 进程都 kill -9 了:()。
小结
回过头看,这次 137 退出并不是 OOM kill:OOMKilled 为 false、内核日志里也没有 OOM 记录,而是人为的 kill -9。以后遇到容器以 137 退出,可以按下面的顺序排查:
- 先看
docker inspect的State.OOMKilled是否为true,并确认容器与宿主机的内存占用情况; - 再查内核日志(
dmesg、/var/log/messages)中有没有 OOM killer 的记录; - 如果都没有,就要考虑人为的
kill -9或其他外部强制终止,结合登录记录排查。
日常也建议给容器合理配置内存限制并加上 restart=always——这次要不是有两个容器自启了,服务中断的时间还会更长。