容器起来就退出,docker ps 空空如也,别急着重跑

新同事在测试机上部署我们的 order-api, 一条 run 下去没报错,他以为成了。

$ docker run -d --name order-api -p 8080:8080 order-api:1.0
a91c0d2e4f7b

接着 docker ps 一敲,傻眼了:

$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

表头一个字段都没配上,等于零个容器在跑。

他第一反应是镜像坏了,准备重新 pull 一个 1.7G 的。 我拦住了,这机器上跑不起来基本跟镜像没关系。 测试机是 CentOS 7.9,Docker 20.10.7,同一个镜像在同事笔记本上是能起来的。

排障第一步是 docker ps -a

docker ps 只列在跑的,退出过的它根本不说。 加个 -a 就全出来了:

$ docker ps -a
CONTAINER ID   IMAGE           COMMAND               CREATED              STATUS                      PORTS     NAMES
a91c0d2e4f7b   order-api:1.0   "java -jar app.jar"   5 seconds ago        Exited (1) 4 seconds ago              order-api

STATUS 那列已经告诉我们两件事: 它起来过,四秒后就死了,退出码 1。

退出码 1 是「应用自己抛异常退的」,不是 docker 的问题。 要是那行显示的是 126、127,方向完全不一样,得先去查启动命令本身,那是另一篇的事。

确认是应用自己挂的,就不用再折腾 docker 了,直接看它的日志:

$ docker logs order-api

2026-09-17 14:31:02.470  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2026-09-17 14:31:03.512 ERROR 1 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Exception during pool initialization.
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago.
The driver has not received any packets from the server.
Caused by: java.net.ConnectException: Connection refused (Connection refused)

中间那四十多行 at 我删了,抄过来也没人看,留头尾就够了。

这一段是 Spring Boot 连不上 MySQL 的标准长相。 HikariPool 初始化炸了,底下 Cause 一层层套到 ConnectException。

看到 ConnectException 就说明应用起来了、配置也读到了,是连不出去, 方向立刻从代码转到网络和环境上。

docker logs 后面加 -f 可以跟着看,加 --tail 200 只看最后两百行。 build 的日志不在这里,那得回 docker build 的输出里翻。

这次的根因是 127.0.0.1

日志里那行 url 是 jdbc:mysql://127.0.0.1:3306/order, 配置是打在 jar 包里带进来的。

他本地一直用 127.0.0.1 连自己的 MySQL, 打成镜像上了容器,这个地址就变味了。

容器里的 127.0.0.1 指的是容器自己,不是宿主机。 MySQL 跑在宿主机的 3306 上,容器里连 127.0.0.1, 等于连了一个自己家里根本没开门的进程,所以是 refused。

这两个报错别混。 refused 是「到是到了,没人听」; timeout 大概率是网络不通,或者被防火墙拦了。 我那台报的是 refused,所以压根没去查安全组。

改法有两条路,我当时给他的是先让服务起来:

$ docker run -d --name order-api -p 8080:8080 \
    -e SPRING_DATASOURCE_URL="jdbc:mysql://10.18.18.21:3306/order" \
    order-api:1.0

Spring Boot 认环境变量,同名 key 会盖掉 application.yml 里的值,不用重新打镜像。 内网 IP 那台就是宿主机自己,走局域网地址,容器摸得到。

再确认一次它这次是真活着,别只看有没有返回容器 id:

$ docker ps
CONTAINER ID   IMAGE           COMMAND               CREATED          STATUS         PORTS                                       NAMES
b41f8e19c207   order-api:1.0   "java -jar app.jar"   12 seconds ago   Up 8 seconds   0.0.0.0:8080->8080/tcp                      order-api

STATUS 是 Up 8 seconds,等了 8 秒还没退,才算起来。 我一般多等十几秒再看一次, 有些应用是跑到一半连不上 Redis 才退的,头五秒看着一切正常。

另一种「起来就退」跟这个不一样

上面这种日志里有满屏报错,好查。 麻烦的是 CMD 里那个脚本自己后台化了:

#!/bin/bash
nohup java -jar /app/app.jar &

容器的 1 号进程就是这个脚本,它把 java 扔到后台,自己立刻退出。 脚本一退,容器认为「主进程结束了」,直接收摊, 而 java 进程可能才刚起来,还没打印任何东西。

这种 docker logs 里干干净净,一行错都没有,最容易把人耗住。 要么脚本最后加一行 wait,要么干脆把 CMD 写成前台执行的 java 命令。

我这台的 8080 起来了,接口也通了,就先这么交付。 但 docker ps -a 里那一堆 Exited 的僵尸容器我还得清, 而且退出码不止 1 这一种,125、126、127、137 分别指什么, 我拿几个容器一个个试出来了。

posted @ 2026-10-17 09:02  kaka的程序人生  阅读(11)  评论(0)    收藏  举报