目录
Docker 容器生命周期教程:创建、启动、停止和删除
容器从创建到删除会经历几种状态。了解这些状态,就能明白为什么容器有时会“运行一下就停止”、为什么已停止的容器仍占用名称,以及为什么 docker stop 有时要等 10 秒。
容器的状态
| 状态 | 含义 | docker ps -a 的 STATUS |
|---|---|---|
created | 已创建但从未启动 | Created |
running | 主进程正在运行 | Up 3 minutes |
paused | 所有进程都被冻结 | Up 3 minutes (Paused) |
exited | 主进程已结束,容器仍然存在 | Exited (0) 10 seconds ago |
restarting | 正按重启策略重新启动 | Restarting (1) 2 seconds ago |
Exited 后面括号中的数字是主进程的退出代码,0 表示正常结束,下面会详细说明。
容器何时会停止
容器的生命周期 = 主进程(PID 1)的生命周期。 主进程结束,容器就会停止。
这解释了一个很常见的疑问:
$ docker run ubuntu:26.04
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66e6f85c66e0 ubuntu:26.04 "/bin/bash" Less than a second ago Exited (0) Less than a second ago dreamy_solomon
docker run ubuntu:26.04 没有任何输出就结束了,docker ps 也看不到它。使用 docker ps -a 才能看到:它的主进程是 /bin/bash,但没有加 -it,bash 没有可读取的输入,启动后立即结束,容器也就停止了(Exited (0))。
加上 -it 为它提供一个交互式终端,bash 才会持续等待你的输入:
docker run -it ubuntu:26.04
nginx、PostgreSQL 这类服务镜像的主进程会一直在前台运行,因此容器会持续运行。
创建与启动
docker run 实际上相当于 docker create(创建)加上 docker start(启动)。也可以分开执行:
$ docker create --name web nginx:1.30
ad99713ea5acddf70d854365e882fd0a3b6d4508d50da25993b94c9878f1c3bc
$ docker ps -a --filter name=web
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad99713ea5ac nginx:1.30 "/docker-entrypoint.…" Less than a second ago Created web
$ docker start web
web
$ docker ps --filter name=web
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad99713ea5ac nginx:1.30 "/docker-entrypoint.…" Less than a second ago Up Less than a second 80/tcp web
创建后状态为 Created,启动后变为 Up。--filter name=web 只会列出名称包含 web 的容器。
名称不能重复
docker run 每次都会创建一个新容器。如果容器名称已被占用,即使那个容器已经停止,也会报错:
$ docker run -d --name web nginx:1.30
docker: Error response from daemon: Conflict. The container name "/web" is already in use by container "ad99713ea5acddf70d854365e882fd0a3b6d4508d50da25993b94c9878f1c3bc". You have to remove (or rename) that container to be able to reuse that name.
Run 'docker run --help' for more information
要重新启动之前的容器,使用 docker start web;要用新配置重新创建,先执行 docker rm -f web,再运行 docker run。
停止:stop 与 kill 的区别
| 命令 | 行为 |
|---|---|
docker stop | 先发送 SIGTERM,让程序自行收尾;等待 10 秒后如果仍未结束,再发送 SIGKILL 强制终止 |
docker kill | 直接发送 SIGKILL,立即终止 |
stop 会给程序机会“妥善收尾”,例如让数据库将数据写回硬盘、让 Web 服务器处理完正在进行的请求。通常都使用 stop,只有程序卡住时才使用 kill。
$ docker start web
web
$ docker kill web
web
$ docker ps -a --filter name=web
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad99713ea5ac nginx:1.30 "/docker-entrypoint.…" 14 seconds ago Exited (137) Less than a second ago web
被 kill 的容器退出代码是 137(128 + 9,9 是 SIGKILL 的编号)。
为什么 docker stop 有时要等 10 秒?
下面实际比较三个容器停止所需的时间:
$ time docker stop web
web
real 0m 0.10s
user 0m 0.01s
sys 0m 0.00s
$ docker run -d --name slow alpine:3.23 sh -c "sleep 300"
Unable to find image 'alpine:3.23' locally
3.23: Pulling from library/alpine
d0c1d894c237: Pull complete
Digest: sha256:85fe1e81d6758c208f3e1eed4338a1997e19d4be002d4dd32d3100c9a8c010a0
Status: Downloaded newer image for alpine:3.23
170d08a64bb8ec1519d183f8c20508d669358eea4d5a978fa4f4675ffcec41ab
$ time docker stop slow
slow
real 0m 10.17s
user 0m 0.00s
sys 0m 0.01s
$ docker run -d --name slow2 --init alpine:3.23 sh -c "sleep 300"
d1854be4790b5fc9375004e05708d27a27d56c2c540a8159424351985db20ef9
$ time docker stop slow2
slow2
real 0m 0.13s
user 0m 0.00s
sys 0m 0.01s
| 容器 | 停止时间 | 原因 |
|---|---|---|
| nginx | 0.10 秒 | nginx 收到 SIGTERM 后会立即正常关闭 |
sh -c "sleep 300" | 10.17 秒 | 主进程是 sh,作为 PID 1 运行时会忽略 SIGTERM,Docker 等足 10 秒后才用 SIGKILL 强制终止 |
同上,加上 --init | 0.13 秒 | --init 会用一个小型 init 进程作为 PID 1,并由它将信号转发给子进程 |
Linux 对 PID 1 有一条特殊规则:如果 PID 1 没有自行处理 SIGTERM,就会忽略这个信号。因此,如果你自己编写的镜像每次执行 docker stop 都要等 10 秒,通常是因为主进程以 PID 1 运行,而程序没有处理 SIGTERM。例如,直接以 node server.js 作为主进程的 Node.js 程序,经实测也要等 10 秒,详见Dockerfile 指令大全中的实际对比。解决办法是在程序中处理 SIGTERM,或加上 --init。
退出代码
$ docker run --name exit3 alpine:3.23 sh -c "exit 3"
$ docker ps -a --filter name=exit3
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
84996cca5ec5 alpine:3.23 "sh -c 'exit 3'" 1 second ago Exited (3) Less than a second ago exit3
$ docker inspect --format '{{.State.ExitCode}}' exit3
3
主进程以什么代码结束,Exited (...) 就会显示什么。常见代码如下:
| 代码 | 常见原因 |
|---|---|
0 | 正常结束 |
1 等其他数字 | 程序自身报告的错误,查看 docker logs |
137 | 被 SIGKILL 终止:执行了 docker kill、docker stop 等待时间过长,或因内存不足被系统终止(OOM) |
143 | 收到 SIGTERM 后正常结束(128 + 15) |
暂停与恢复
$ docker start web
web
$ docker pause web
web
$ docker ps --filter name=web
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad99713ea5ac nginx:1.30 "/docker-entrypoint.…" 15 seconds ago Up Less than a second (Paused) 80/tcp web
$ docker unpause web
web
pause 会冻结容器内的所有进程(不会结束它们),状态会显示 (Paused)。这个功能很少用,适合临时释放 CPU,同时保留内存中的状态。
删除容器
$ docker rm web
Error response from daemon: cannot remove container "web": container is running: stop the container before removing or force remove
$ docker rm -f web
web
运行中的容器不能直接删除,需要先执行 stop,或使用 -f 强制删除(相当于先 kill 再 rm)。
一次性清理所有已停止的容器
练习一段时间后,docker ps -a 中就会积累很多已停止的容器:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
75ee00dc4081 nginx:1.30 "/docker-entrypoint.…" 1 second ago Up Less than a second 80/tcp web
5dfb843c0b40 alpine:3.23 "sh -c 'echo startin…" 26 seconds ago Exited (1) 24 seconds ago flaky
84996cca5ec5 alpine:3.23 "sh -c 'exit 3'" 27 seconds ago Exited (3) 26 seconds ago exit3
d1854be4790b alpine:3.23 "sh -c 'sleep 300'" 29 seconds ago Exited (143) 28 seconds ago slow2
170d08a64bb8 alpine:3.23 "sh -c 'sleep 300'" 39 seconds ago Exited (137) 29 seconds ago slow
66e6f85c66e0 ubuntu:26.04 "/bin/bash" 43 seconds ago Exited (0) 42 seconds ago dreamy_solomon
$ docker container prune -f
Deleted Containers:
5dfb843c0b400146827fe138890ee4d62a24104cad5531aff4100fc471b0727f
84996cca5ec5bfe7ca51c6365b60ef89e49055cf06a3f0dd3a49f9e50e08d6f4
d1854be4790b5fc9375004e05708d27a27d56c2c540a8159424351985db20ef9
170d08a64bb8ec1519d183f8c20508d669358eea4d5a978fa4f4675ffcec41ab
66e6f85c66e0f2b586c5919a900a0e16d6f0fc49535d35fa7d30f77747f1ce0e
Total reclaimed space: 24.58kB
container prune 只会删除已停止的容器,正在运行的 web 不受影响。也可以对照上面的退出代码:slow 被强制终止,退出代码为 137;加了 --init 的 slow2 正常收到 SIGTERM,退出代码为 143。
如果希望一次性容器结束后自动删除,可以在运行时加上 --rm:
docker run --rm alpine:3.23 echo "用完就丢"
重启策略:--restart
服务器重启或程序崩溃时,是否要自动重新启动容器?使用 --restart 进行设置:
| 取值 | 行为 |
|---|---|
no | 默认值,不自动重启 |
on-failure[:次数] | 只有退出代码不为 0 时才重启,可以限制次数 |
always | 始终重启;手动 stop 后,如果 Docker 本身重新启动,容器仍会再次启动 |
unless-stopped | 类似 always,但手动 stop 过的容器不会再自动启动 |
实际看看 on-failure
下面这个容器每次启动都会打印 starting,然后以代码 1 结束,用来模拟不断崩溃的程序:
$ docker run -d --name flaky --restart on-failure:3 alpine:3.23 sh -c "echo starting; exit 1"
5dfb843c0b400146827fe138890ee4d62a24104cad5531aff4100fc471b0727f
$ docker ps -a --filter name=flaky
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5dfb843c0b40 alpine:3.23 "sh -c 'echo startin…" 25 seconds ago Exited (1) 23 seconds ago flaky
$ docker inspect --format '{{.RestartCount}} {{.State.Status}} {{.State.ExitCode}}' flaky
3 exited 1
$ docker logs flaky
starting
starting
starting
starting
日志中有 4 次 starting:第一次启动,加上 3 次重启(RestartCount 为 3),达到上限后容器就停在 Exited (1)。Docker 会逐渐延长每次重启之间的等待时间,避免频繁重启拖垮主机。
修改已有容器的重启策略
长期运行的服务一般建议使用 unless-stopped。也可以修改已在运行的容器:
$ docker run -d --name web --restart unless-stopped nginx:1.30
75ee00dc4081f4c2849562fbd89edfdd9386839a161ea9d66956eaa41257d4cf
$ docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' web
unless-stopped
$ docker update --restart always web
web
$ docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' web
always
在 Linux 上,要让容器随系统启动,Docker 本身也必须设置为开机自动启动:
sudo systemctl enable docker。在 Docker Desktop 中,则要在设置里启用“Start Docker Desktop when you sign in”。
命令汇总
| 命令 | 作用 |
|---|---|
docker run | 创建并启动新容器 |
docker create | 只创建容器 |
docker start / stop / restart | 启动 / 停止 / 重启已有容器 |
docker kill | 强制终止(SIGKILL) |
docker pause / unpause | 暂停 / 恢复 |
docker rm [-f] | 删除容器 |
docker container prune | 删除所有已停止的容器 |
docker ps [-a] | 列出容器(-a 包括已停止的容器) |
docker update --restart | 修改重启策略 |
下一节将完整介绍 docker run 的常用参数。
重点回顾
- 容器的状态:created、running、paused、exited、restarting。
- 主进程结束时容器会停止;
docker stop会先发送 SIGTERM,10 秒后再发送 SIGKILL。 - 如果主进程是 PID 1 且没有处理 SIGTERM,stop 会等待整整 10 秒;可以用
--init解决。 - 长期运行的服务应加上
--restart unless-stopped。
动手练习
练习 1:使用 docker run -d --name t1 alpine:3.23 sh -c "exit 3" 启动容器,它的状态和退出代码是什么?
答案
docker ps -a 会显示 Exited (3)。也可以使用 docker inspect -f "{{.State.ExitCode}}" t1 获取退出代码 3。
练习 2:启动 docker run -d --name t2 alpine:3.23 sleep 300,分别使用 time docker stop t2(记得先重新创建容器)和加上 --init 的版本,比较停止所需的时间。
提示
不加 --init 时,sleep 是 PID 1,会忽略 SIGTERM,约 10 秒后才被强制终止;加上 --init 后,init 进程会把信号转发给 sleep,容器几乎立即停止。
常见问题
Exited (137) 是什么意思?
137 = 128 + 9,表示容器被 SIGKILL 强制终止,可能是执行了 docker kill、docker stop 等待时间过长,或因内存不足被系统终止。
服务器重启后,容器会自动启动吗?
需要设置重启策略(例如 --restart unless-stopped),而且 Docker 本身也要设置为开机自动启动(Linux 上执行 sudo systemctl enable docker)。
暂停(pause)和停止(stop)有什么区别?
pause 会冻结容器内的所有进程,保留内存中的内容,恢复后从原状态继续运行;stop 会结束进程,内存中的状态也会消失。
更新于 2026年10月14日