精通 Docker 容器生命周期:启动、停止、重启与自定义命令实践
Docker 作为当今最流行的容器化技术之一,深刻改变了软件的开发、交付和运行方式。熟练掌握 Docker 容器的生命周期管理,是每一位开发者和运维工程师的必备技能。本文将结合实际操作示例,深入探讨如何启动、停止、重启 Docker 容器,以及如何在启动时覆盖默认命令,并阐述容器生命周期与其主进程的关键关系。
前提条件
- 已在您的系统上安装并运行 Docker 服务。
- 对 Docker 的基本概念(如镜像、容器)有初步了解。
1. 查看容器状态 (docker ps
)
在管理容器之前,首先需要了解它们的当前状态。docker ps
命令是您的核心工具。
docker ps
: 列出当前正在运行的容器。docker ps -a
(或--all
): 列出所有容器,包括已停止的容器。
示例:
root@docker102:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
93b29258821f docker.elastic.co/kibana/kibana:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Up 2 seconds 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp kibana
d346a298551e docker.elastic.co/elasticsearch/elasticsearch:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Up 5 seconds 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp es01
deddaf31973d mysql:8.3.0-oracle "docker-entrypoint.s…" 2 days ago Exited (255) About an hour ago 33060/tcp, 0.0.0.0:13306->3306/tcp, :::13306->3306/tcp mysql-server02
这里的 STATUS
列是关键信息:
Up ...
: 表示容器正在运行,并显示已运行时间。Exited (code) ...
: 表示容器已停止。括号内的退出码 (Exit Code
) 提供了容器停止原因的线索(例如,0
通常表示正常退出,非零值表示异常或接收到特定信号)。
2. 停止容器 (docker stop
)
docker stop
命令用于优雅地停止一个或多个正在运行的容器。
工作机制:
- 该命令首先向容器内的主进程(PID 1)发送
SIGTERM
信号,请求其自行关闭。 - 容器内的应用程序应该捕获此信号并执行清理操作(如保存数据、关闭连接)。
- 如果在默认的超时时间(10 秒)内,主进程没有退出,Docker 会接着发送
SIGKILL
信号,强制终止该进程。
语法:
docker stop [OPTIONS] CONTAINER [CONTAINER...]
- 可以通过容器的 Name 或 ID (或其唯一前缀) 来指定目标容器。
OPTIONS
中常用的有-t
或--time
:指定发送SIGTERM
后等待的秒数,超过该时间再发送SIGKILL
。例如,-t 0
表示几乎立即发送SIGKILL
(在SIGTERM
之后)。
示例:
# 停止名为 'es01' 的容器,设置超时为 0 秒
root@docker102:~# docker stop -t 0 es01
es01
# 通过容器 ID 停止 'kibana' 容器 (使用默认 10 秒超时)
root@docker102:~# docker stop 93b29258821f
93b29258821f
# 验证容器状态,STATUS 变为 Exited
root@docker102:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
93b29258821f docker.elastic.co/kibana/kibana:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Exited (0) 9 seconds ago kibana # 正常退出
d346a298551e docker.elastic.co/elasticsearch/elasticsearch:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Exited (137) 26 seconds ago es01 # 可能被 SIGKILL (9) 终止,退出码 137 = 128 + 9
deddaf31973d mysql:8.3.0-oracle "docker-entrypoint.s…" 2 days ago Exited (255) About an hour ago mysql-server02
注意: 退出码
137
通常表示进程是被SIGKILL
(信号编号 9) 杀死的,因为 Shell 的约定是将退出码设置为128 + 信号编号
。
3. 启动已停止的容器 (docker start
)
对于处于 Exited
状态的容器,可以使用 docker start
命令将其重新启动。
关键特性:
docker start
会保留容器创建时的所有配置(包括命令、环境变量、挂载卷、端口映射等)。- 它只是将容器的主进程重新拉起。
语法:
docker start [OPTIONS] CONTAINER [CONTAINER...]
- 同样可以通过容器的 Name 或 ID 指定。
- 可以一次启动多个容器。
示例:
# 查看当前状态 (es01 和 kibana 已停止)
root@docker102:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
93b29258821f docker.elastic.co/kibana/kibana:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Exited (255) About an hour ago 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp kibana
d346a298551e docker.elastic.co/elasticsearch/elasticsearch:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Exited (130) 2 days ago es01
deddaf31973d mysql:8.3.0-oracle "docker-entrypoint.s…" 2 days ago Exited (255) About an hour ago 33060/tcp, 0.0.0.0:13306->3306/tcp, :::13306->3306/tcp mysql-server02
# 通过名称启动 'es01'
root@docker102:~# docker start es01
es01
# 通过 ID 启动 'kibana'
root@docker102:~# docker start 93b29258821f
93b29258821f
# 验证容器状态,STATUS 变为 Up,运行时间重新开始计数
root@docker102:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
93b29258821f docker.elastic.co/kibana/kibana:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Up 4 seconds 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp kibana
d346a298551e docker.elastic.co/elasticsearch/elasticsearch:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Up 26 seconds 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp es01
deddaf31973d mysql:8.3.0-oracle "docker-entrypoint.s…" 2 days ago Exited (255) About an hour ago 33060/tcp, 0.0.0.0:13306->3306/tcp, :::13306->3306/tcp mysql-server02
4. 重启容器 (docker restart
)
docker restart
命令是一个便捷操作,它相当于对指定的容器执行 docker stop
,然后再执行 docker start
。
特性:
- 如果容器正在运行,它会先停止再启动。
- 如果容器已停止,它会直接启动。
- 同样支持
-t
(或--time
) 选项,用于控制停止阶段的超时时间。 - 重启后,容器的
STATUS
中的运行时间会重新开始计数。
语法:
docker restart [OPTIONS] CONTAINER [CONTAINER...]
示例:
# 查看 es01 状态 (已运行 25 秒)
root@docker102:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
...
d346a298551e docker.elastic.co/elasticsearch/elasticsearch:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Up 25 seconds 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp es01
...
# 重启 es01
root@docker102:~# docker restart es01
es01
# 再次查看状态,es01 仍在运行,但 Up 时间已重置
root@docker102:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
...
d346a298551e docker.elastic.co/elasticsearch/elasticsearch:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Up 2 seconds 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp es01
...
5. 强制终止容器 (docker kill
)
与 docker stop
不同,docker kill
命令直接向容器的主进程发送 SIGKILL
信号(或其他指定的信号),强制终止容器,不给应用程序优雅关闭的机会。
使用场景:
- 当
docker stop
无法停止容器时。 - 需要立即停止容器,不关心数据是否丢失或状态是否一致时(生产环境慎用)。
语法:
docker kill [OPTIONS] CONTAINER [CONTAINER...]
- 默认发送
SIGKILL
。 - 可以通过
-s
或--signal
选项指定发送其他信号。
示例:
# 启动 kibana 和 es01
root@docker102:~# docker start kibana es01
kibana
es01
# 验证它们正在运行
root@docker102:~# docker ps -a | grep -E 'kibana|es01'
93b29258821f docker.elastic.co/kibana/kibana:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Up 7 seconds 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp kibana
d346a298551e docker.elastic.co/elasticsearch/elasticsearch:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Up 5 seconds 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp es01
# 强制终止 kibana
root@docker102:~# docker kill kibana
kibana
# 验证 kibana 状态变为 Exited (通常退出码为 137)
root@docker102:~# docker ps -a | grep kibana
93b29258821f docker.elastic.co/kibana/kibana:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Exited (137) 2 seconds ago kibana
重要提示:
docker stop
本质上是尝试优雅关闭(SIGTERM
),超时后再强制关闭(SIGKILL
)。而docker kill
默认直接强制关闭(SIGKILL
)。在生产环境中,应优先使用docker stop
,以允许应用程序完成必要的清理工作。
6. 容器与宿主机进程的关系
理解容器的本质非常重要:一个运行中的 Docker 容器,其核心是宿主机上的一个或多个进程,这些进程被 Linux 内核的 Namespace 和 Cgroups 技术隔离和限制资源。
- 容器启动:相当于在宿主机上启动了容器的主进程(以及可能的子进程)。
- 容器停止/终止:意味着宿主机上对应的进程被终止。
示例:查找并终止 Elasticsearch 容器的宿主机进程
# 确认 es01 容器正在运行
root@docker102:~# docker ps | grep es01
d346a298551e docker.elastic.co/elasticsearch/elasticsearch:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Up 55 seconds 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp es01
# 在宿主机上查找与 Elasticsearch 相关的 Java 进程
# (注意:Elasticsearch 是 Java 应用,进程名通常包含 'java' 或 'elasticsearch')
root@docker102:~# ps -ef | grep java
# 输出可能包含多个进程,找到与 es01 容器相关的 (通常由特定用户如 'elasticsearch' 或 'yinzhen+' 运行,且路径包含 /usr/share/elasticsearch)
yinzhen+ 9280 9253 5 09:44 ? 00:00:03 /usr/share/elasticsearch/jdk/bin/java ... # 这是 Elasticsearch 的启动器/父进程
yinzhen+ 9341 9280 77 09:44 ? 00:00:50 /usr/share/elasticsearch/jdk/bin/java ... # 这是 Elasticsearch 的主服务进程
# 在宿主机上直接杀死容器的主进程或其父进程 (这里以父进程 9280 为例)
root@docker102:~# kill 9280
# 等待片刻,再次查看容器状态
root@docker102:~# docker ps -a | grep es01
d346a298551e docker.elastic.co/elasticsearch/elasticsearch:8.14.3 "/bin/tini -- /usr/l…" 2 days ago Exited (143) 6 seconds ago es01
可以看到,当宿主机上的对应进程被杀死后,Docker 检测到主进程退出,容器的状态也随之变为 Exited
。退出码 143
通常表示进程是被 SIGTERM
(信号编号 15) 终止的 (128 + 15 = 143
),kill
命令默认发送 SIGTERM
。
7. 容器启动时指定自定义命令
使用 docker run
创建并启动容器时,可以在镜像名称后面指定要运行的命令。这个指定的命令会覆盖镜像 Dockerfile 中定义的默认 CMD
或 ENTRYPOINT
。
1. 查看容器的默认启动命令:
使用 docker ps
查看正在运行的容器时,COMMAND
列会显示容器启动时执行的命令。有时命令过长会被截断,可以使用 --no-trunc
选项查看完整命令。
# 运行一个基于 nginx 的示例容器 c1
root@docker102:~# docker run -d --name c1 -p 81:80 registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1
9f0419692262...
# 查看 c1 的启动命令 (可能会被截断)
root@docker102:~# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f0419692262 registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1 "/docker-entrypoint.…" 3 seconds ago Up 1 second 0.0.0.0:81->80/tcp, :::81->80/tcp c1
# 查看 c1 完整的启动命令
root@docker102:~# docker ps -l --no-trunc
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f04196922621ea548d4cd9058f4fa77674ef380ac62156663bd066018f9e66a registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1 "/docker-entrypoint.sh nginx -g 'daemon off;'" 11 seconds ago Up 10 seconds 0.0.0.0:81->80/tcp, :::81->80/tcp c1
默认情况下,这个镜像启动了 Nginx 服务。
2. 启动容器并指定自定义命令:
在 docker run
命令的最后,直接跟上你想要执行的命令及其参数。
# 启动一个名为 c2 的容器,使用相同的镜像,但覆盖默认命令,让它执行 'sleep 120'
root@docker102:~# docker run -d --name c2 -p 82:80 registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1 sleep 120
a266625de6d9...
# 查看 c2 的完整启动命令,确认它执行的是 sleep 命令
root@docker102:~# docker ps -l --no-trunc
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a266625de6d9fa010c90f4a3af175b0d4e90eaa5be95ea3292c7b2f1dc7d547e registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1 "/docker-entrypoint.sh sleep 120" 8 seconds ago Up 7 seconds 0.0.0.0:82->80/tcp, :::82->80/tcp c2
这个 c2
容器现在的主进程是 sleep 120
,它不会启动 Nginx 服务。
8. 连接到运行中的容器 (docker exec
)
docker exec
命令允许你在一个已经运行的容器内部执行新的命令。这对于调试、检查容器状态或执行临时任务非常有用。
常用选项:
-i
(interactive): 保持标准输入 (STDIN) 打开。-t
(tty): 分配一个伪终端。-it
: 通常一起使用,提供一个交互式的 Shell 环境。
示例:进入 c2
容器并查看进程
# 进入名为 c2 的容器,并启动一个 shell (/bin/sh 或 sh,取决于基础镜像)
root@docker102:~# docker exec -it c2 sh
# 现在你位于容器 c2 内部的 shell 环境
/ #
# 查看容器内的进程,可以看到 PID 1 是 sleep 120
/ # ps -ef
PID USER TIME COMMAND
1 root 0:00 sleep 120 # 这是容器的主进程 (PID 1)
6 root 0:00 sh # 这是 docker exec 启动的 shell 进程
12 root 0:00 ps -ef # ps 命令本身
# 可以在容器内手动启动其他服务,比如 nginx (注意:这不会成为主进程)
/ # nginx
... (nginx 启动日志) ...
# 再次查看进程,nginx 进程已启动,但 PID 1 仍然是 sleep
/ # ps -ef
PID USER TIME COMMAND
1 root 0:00 sleep 120
6 root 0:00 sh
20 root 0:00 nginx: master process nginx
21 nginx 0:00 nginx: worker process
22 nginx 0:00 nginx: worker process
23 root 0:00 ps -ef
# 退出容器的 shell 环境
/ # exit
root@docker102:~#
9. 容器生命周期与其主进程的绑定关系
这是理解容器运行机制的核心要点:Docker 容器的生命周期与其内部的主进程(PID 1)紧密绑定。当容器的主进程退出时,无论是什么原因(正常完成、出错、被杀死),容器自身也会停止。
回顾上面的 c2
容器示例:
- 我们使用
docker run ... sleep 120
启动了c2
,所以sleep 120
是它的主进程 (PID 1)。 - 虽然我们通过
docker exec
进入容器并手动启动了nginx
,但这只是容器内部的附加进程,并非主进程。 sleep 120
命令会在 120 秒后正常退出。
结果:
# 等待大约 2 分钟后,再次查看 c2 的状态
root@docker102:~# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a266625de6d9 registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1 "/docker-entrypoint.…" 2 minutes ago Exited (0) 38 seconds ago c2
可以看到,c2
容器的状态变成了 Exited (0)
。这是因为它的主进程 sleep 120
在 120 秒后执行完毕并正常退出(退出码 0),即使我们在容器内手动启动的 nginx
可能还在运行,但由于主进程已结束,Docker 判定该容器的生命周期结束,因此将其停止。
总结与关键要点
docker ps -a
: 查看所有容器状态。docker stop
: 优雅停止容器(SIGTERM
->SIGKILL
),是首选的停止方式。docker start
: 启动已停止的容器,保留原有配置。docker restart
: 方便地停止并重新启动容器。docker kill
: 强制终止容器(SIGKILL
),用于stop
无效或紧急情况。- 容器是进程: 运行的容器对应宿主机上的一个或多个进程。
- 自定义命令:
docker run [image] [command]
可以覆盖镜像的默认启动命令。 docker exec
: 在运行的容器内执行命令,用于调试和交互。- 生命周期核心: 容器与其主进程(PID 1)共存亡。主进程退出,容器即停止。
掌握这些命令和概念,将使您在日常的 Docker 使用中更加得心应手,能够有效地管理和维护您的容器化应用。
来源链接:https://www.cnblogs.com/leojazz/p/18807707
没有回复内容