主机发送关机信号时Docker容器的处理机制及优化方案咨询
首先,咱们先把主机关机时Docker的处理逻辑理清楚:
当Ubuntu Server(由systemd管理服务)触发关机流程时,systemd会先向Docker daemon发送SIGTERM信号。Docker daemon收到信号后,会依次给所有运行中的容器发送SIGTERM信号,并等待默认10秒的优雅停止窗口。如果容器在这段时间内没有主动退出,Docker就会发送SIGKILL信号强制终止容器进程。
那为什么你的MySQL容器会出现恢复模式启动甚至文件损坏的情况?核心原因是:MySQL的优雅关闭需要足够时间完成缓冲数据落盘、关闭客户端连接、同步binlog等操作,默认10秒的等待时间往往不够用。如果容器被强制终止,MySQL的磁盘数据会处于不一致状态,下次启动就会触发自动恢复,极端情况下会导致文件损坏。
接下来是具体的解决办法,按优先级排序:
1. 给MySQL容器设置更长的优雅停止超时时间
Docker允许为单个容器或docker-compose项目自定义停止等待时间:
- 用
docker run启动容器时,添加--stop-timeout 30参数(这里设置30秒,你可以根据实际情况调整到60秒甚至更久):docker run -d --stop-timeout 30 mysql:latest - 如果用docker-compose,在服务配置中添加
stop_grace_period: 30s:services: mysql: image: mysql:latest stop_grace_period: 30s
这个设置会让Docker在发送SIGTERM后等待30秒再强制终止,给MySQL足够的时间完成关机流程。
2. 确保容器内的MySQL进程能正确接收信号
很多时候,容器里的进程不是PID 1,或者启动脚本吃掉了SIGTERM信号,导致MySQL收不到关机指令。解决办法:
- 用
exec命令启动MySQL,让它成为PID 1进程,直接接收信号。比如修改Dockerfile的ENTRYPOINT:ENTRYPOINT ["exec", "mysqld"] - 或者在启动容器时使用
tini作为init进程,它会负责把信号传递给子进程。比如用docker run时添加--init参数:docker run -d --init --stop-timeout 30 mysql:latest
3. 验证容器的优雅停止逻辑
先手动测试docker stop <mysql容器名>,然后查看容器日志:
docker logs <mysql容器名>
如果日志里能看到类似Shutdown complete的字样,说明MySQL是正常优雅关闭的。如果手动停止都有问题,那主机关机时肯定也会出问题,得先排查容器的启动配置。
4. 可选:添加主机关机前的容器停止脚本
虽然Docker默认会处理容器停止,但如果系统关机的整体超时时间(systemd默认的关机超时一般是90秒)比你设置的容器停止时间还短,或者Docker daemon本身出现异常,还是可能导致强制终止。这种情况下,可以添加一个自定义的systemd服务,在关机前先主动停止所有容器:
- 创建脚本
/usr/local/bin/stop-docker-containers.sh,内容如下:
#!/bin/bash # 停止所有运行中的容器 docker stop $(docker ps -q) # 如果用docker-compose,也可以加上对应的停止命令 # docker-compose -f /path/to/your/docker-compose.yml stop
- 给脚本添加执行权限:
chmod +x /usr/local/bin/stop-docker-containers.sh
- 创建systemd服务文件
/etc/systemd/system/stop-docker-containers.service:
[Unit] Description=Stop Docker containers before shutdown Before=shutdown.target reboot.target halt.target [Service] Type=oneshot ExecStart=/usr/local/bin/stop-docker-containers.sh TimeoutStartSec=0 [Install] WantedBy=multi-user.target
- 启用并启动服务:
systemctl daemon-reload systemctl enable stop-docker-containers.service
这样系统关机时会先执行这个脚本,主动停止所有容器,再继续关机流程。
总结一下:优先调整容器的停止超时时间和信号传递逻辑,这是最根本的解决办法;如果还不放心,再添加关机前的脚本做双重保障。
内容的提问来源于stack exchange,提问作者sonikro

