Docker容器优雅停止同时保留nginx定时重载的实现方案
解决方案
核心思路是调整启动脚本的执行顺序,将非核心的定时重载逻辑放到后台子进程运行,最后通过exec让uWSGI替换初始shell进程成为容器PID 1,无需额外安装组件、无需修改现有uWSGI配置即可同时满足两个诉求:
- uWSGI作为PID 1直接接收
docker stop发送的SIGTERM信号,触发你已经配置好的优雅退出逻辑 - 6小时定时重载nginx的逻辑作为后台子进程持续运行,不会被
exec逻辑中断
修改后的startapi.sh完整内容如下:
#!/bin/bash # 启动nginx反向代理 service nginx start # 将定时重载nginx的循环逻辑放入后台执行,末尾加&标记为后台任务 while :; do nginx -s reload; sleep 6h; done & # 最后通过exec启动uWSGI,替换当前bash进程成为容器PID 1 exec uwsgi --ini uwsgi.ini
原理说明
你之前的写法存在三个逻辑冲突点:
- 若将
exec uwsgi放在前面且不加&,当前shell进程会直接被uWSGI替换,后续的while循环代码永远不会被执行 - 若将while循环放在前面且不放入后台,shell会一直阻塞在循环逻辑中,永远走不到启动uWSGI的步骤
- 若将uWSGI加
&放入后台,while循环占用主shell进程成为PID 1,SIGTERM信号无法直接传递给uWSGI,导致容器停止时超时强杀
调整顺序后,while循环作为后台任务被fork到子进程执行,主shell进程顺利走到最后一行exec uwsgi,此时原bash进程被uWSGI替换成为PID 1,后台的循环子进程会被uWSGI(新的PID 1)收养,持续按间隔执行nginx重载操作。
注意:你已经在uWSGI中配置了进程级优雅退出钩子,容器停止时uWSGI收到SIGTERM后会自动回收所有子进程(包括nginx进程、定时重载的bash进程),不会产生僵尸进程。
备选方案(不推荐,改动量更大)
如果不想把定时逻辑写在启动脚本里,也可以通过以下方式实现,但会增加镜像复杂度:
- 在容器内安装cron服务,添加6小时执行一次
nginx -s reload的定时任务,启动脚本最后直接exec uwsgi --ini uwsgi.ini - 使用supervisor等进程管理工具托管nginx、uWSGI和定时重载脚本,但此时PID 1是supervisor,需要额外配置supervisor的信号转发逻辑,无法满足uWSGI直接作为PID 1的诉求
内容的提问来源于stack exchange,提问作者gregCubed
相关产品推荐
相关产品推荐

