在 Docker 的奇幻世界里,我们每天都在乐此不疲地docker run,召唤出一个个容器精灵来为我们服务。但俗话说得好,请神容易送神难。如何优雅地、或者说有效地让这些“精灵”功成身退,却是一门值得深究的学问。今天,我们就来聚焦那个看似简单,却内藏玄机的命令——docker stop。它可不是一个单纯的“关机键”,而是一套融合了“绅士风度”与“最终手段”的完整哲学。
一、为什么不是所有的“停止”都叫 Stop?
很多新手会混淆 docker stop 和 docker kill,甚至觉得它们没什么区别,不都是让容器停止运行吗?如果你也这么想,那就大错特错了。它们的区别,就像是“礼貌地请客人离开”和“直接把客人轰出门外”的本质区别。
docker stop: 优雅的绅士
它的工作流程是“先礼后兵”。当你执行docker stop时,Docker daemon 会先向容器内的主进程(PID 1)发送一个 SIGTERM 信号。这个信号的意思是:“您好,麻烦您处理一下手头的工作,我们准备要关闭了。” 进程收到这个信号后,可以执行一些预定义的清理操作,比如关闭数据库连接、将内存数据持久化到磁盘、完成正在处理的网络请求等。这给了容器一个“体面退出”的机会。
只有在等待一段时间(默认10秒)后,如果容器依然没有自行停止,Docker 才会失去耐心,发出终极杀手锏——SIGKILL 信号。这个信号是强制性的,操作系统会立即终止该进程,并且进程无法捕获或忽略它。这是一种强制手段。
docker kill: 粗暴的霸王
相比之下,docker kill就直接多了。它默认发送的就是 SIGKILL 信号(也可通过-s参数指定其他信号),相当于不由分说,直接拔电源。容器进程没有机会做任何清理工作,可能会导致数据丢失或状态不一致。
所以,首选永远是 docker stop! 除非容器已经完全无响应,你确认它无法处理 SIGTERM 信号,这时才考虑使用 docker kill 作为最后的补救措施。
二、实战示例:从入门到精通
光说不练假把式,下面我们通过一系列示例来真切感受 docker stop 的魅力。
示例 1:基础操作——停止一个容器
启动一个测试容器:
我们用一个最简单的 Nginx 容器来演示。它会以后台模式运行。
docker run -d --name my-nginx nginx:alpine
使用 docker ps 查看,确认容器正在运行(STATUS 为 Up)。
优雅地停止它:
docker stop my-nginx
再次运行 docker ps,你会发现容器不见了。运行 docker ps -a 可以看到容器状态变为 Exited (0),其中的 0 表示正常退出码。
示例 2:处理“顽固”容器——修改超时时间
假设我们有一个自定义的容器,它收到 SIGTERM 后需要花费 25 秒进行复杂的数据清理工作。默认的10秒显然不够。
启动一个模拟“慢”容器:
我们可以写一个简单的脚本模拟此行为。
# 创建一个名为 slow-stop.sh 的脚本
cat > slow-stop.sh << 'EOF'
#!/bin/sh
# 模拟收到SIGTERM后的清理工作
trap "echo 'Received SIGTERM, cleaning up...'; sleep 25; echo 'Cleanup done!'; exit 0" SIGTERM
echo "Container is running..."
# 保持脚本运行
while true; do sleep 1; done
EOF
# 构建一个自定义镜像
cat > Dockerfile << 'EOF'
FROM alpine:latest
COPY slow-stop.sh /
RUN chmod +x /slow-stop.sh
CMD ["/slow-stop.sh"]
EOF
docker build -t my-slow-app .
docker run -d --name slow-container my-slow-app
使用默认超时停止(预计会失败):
time docker stop slow-container # 使用 time 命令计算耗时
你会发现大约10秒后,命令执行完毕。但使用 docker logs slow-container 查看日志,你会发现只打印了 "Received SIGTERM, cleaning up...",还没来得及打印 "Cleanup done!" 就被 SIGKILL 强制杀死了。它的退出码不会是 0。
使用自定义超时时间:
docker stop 命令支持 -t 或 --time 参数来设置等待时间(单位:秒)。
# 重新启动容器
docker start slow-container
# 给予30秒的超时时间
time docker stop -t 30 slow-container
这次,命令大约会在30秒后完成。查看日志 docker logs slow-container,你会看到完整的 "Cleanup done!" 输出,并且退出码是 0。这证明了优雅关机成功了!
示例 3:与 Docker Kill 的对比
启动容器:
docker run -d --name quick-nginx nginx:alpine
使用 docker kill:
docker kill quick-nginx
这个过程几乎是瞬间完成的。对于 Nginx 这种设计良好的服务,它也能处理 SIGKILL 之外的信号,我们可以通过给 docker kill 指定信号来模拟 stop。
让 docker kill 发送 SIGTERM 信号:
docker run -d --name another-nginx nginx:alpine
docker kill -s SIGTERM another-nginx # 使用 kill 发送 stop 的信号
效果和 docker stop 是一样的。这反过来证明了,docker stop 本质上就是 docker kill -s SIGTERM 加上一个超时等待后自动升级为 SIGKILL 的包装。
示例 4:一键停止所有运行中的容器
这是一个非常实用的运维技巧,可以优雅地停止所有正在运行的容器,而不是粗暴地杀死它们。
docker stop $(docker ps -q)
docker ps -q:-q参数只输出容器的 ID。docker stop ...: 然后将这些 ID 作为参数传递给stop命令。
三、最佳实践与常见问题(FAQ)
- 我的容器应用如何优雅地处理 SIGTERM?
这是开发者的责任。在你的应用程序(如 Python、Java、Node.js、Golang 等)中,需要编写信号处理代码来监听 SIGTERM 信号,并触发清理逻辑。这是构建云原生应用的重要一环。 docker stop之后容器去哪了?
容器只是停止了,其文件系统(数据层)和配置(容器名、端口映射等)仍然存在。你可以通过docker start再次启动它。只有执行docker rm后,容器才会被彻底删除。- 为什么我的容器用
stop停不掉?
通常有两个原因:
-
- 容器内的 主进程(PID 1) 非常特殊,它默认不会转发或处理任何信号。如果你的主进程是一个简单的 Shell 脚本,它可能无法将收到的 SIGTERM 传递给实际工作的子进程。解决方法是使用
exec命令或在更高级的工具中包装进程。 - 容器本身已经处于“死锁”或“无响应”状态,无法处理任何信号。这时就只能求助于
docker kill了。
- 容器内的 主进程(PID 1) 非常特殊,它默认不会转发或处理任何信号。如果你的主进程是一个简单的 Shell 脚本,它可能无法将收到的 SIGTERM 传递给实际工作的子进程。解决方法是使用
docker restart是什么?
docker restart=docker stop+docker start。它同样支持-t参数,用于控制停止阶段的超时时间。
结论
docker stop 远不止是一个命令,它体现了 Docker 设计中对应用生命周期的尊重。理解其 SIGTERM 和 SIGKILL 的两阶段机制,学会灵活运用 -t 参数,并根据不同场景选择 stop 或 kill,是你从 Docker 新手走向熟练玩家的标志。
下次在停止容器时,不妨多一份耐心,给它一个体面退出的机会,毕竟它曾辛勤地为你工作过。记住,优秀的船长不仅要知道如何启航,更要懂得如何平稳入港。

被折叠的 条评论
为什么被折叠?



