K8s部署Django时Docker容器中Celery Worker无法自动启动问题
问题原因与解决方案
核心问题:Dockerfile多CMD指令被覆盖
Docker明确规定一个Dockerfile仅会执行最后一条CMD指令,你写的四条CMD里,只有最后启动gunicorn的命令会被执行,前面创建日志目录、启动Celery Worker、执行数据库迁移的命令全部被忽略——这就是Worker没启动的根本原因。
另外,你用--detach让Celery Worker后台运行的方式也不适合容器环境:后台进程的输出不会被容器捕获,排查问题困难;且容器只会跟踪主进程(这里是gunicorn),如果Celery Worker异常退出,容器不会感知或自动重启它。
解决方案:用Shell脚本作为容器入口
把所有需要执行的命令整合到一个shell脚本里,让gunicorn作为最后一个前台运行的主进程,确保容器能正确跟踪生命周期。
步骤1:创建启动脚本start.sh
#!/bin/bash # 创建日志目录(-p参数避免目录已存在时报错) mkdir -p logs # 初始化日志文件 touch logs/error.log logs/access.log # 执行数据库迁移 python manage.py migrate # 启动Celery Worker(放到后台运行,不阻塞主进程) celery -A app.celery worker -l info & # 启动gunicorn,用exec让它成为容器PID 1进程,确保能正确接收停止信号 exec gunicorn --bind :8000 --workers 2 --timeout 300 --error-logfile logs/error.log --access-logfile logs/access.log --capture-output --log-level debug app.wsgi
步骤2:修改Dockerfile
在原Dockerfile末尾替换所有CMD指令,改为复制并执行启动脚本:
# ... 保留前面所有构建步骤 # 复制启动脚本到容器内 COPY start.sh /app/start.sh # 给脚本添加执行权限 RUN chmod +x /app/start.sh # 设置容器入口为启动脚本 CMD ["/app/start.sh"]
为什么手动执行有效?
手动进入Pod后,是在交互式shell环境下依次执行每个命令,每条指令都会被执行;而Dockerfile的多CMD是覆盖关系,只有最后一条生效,所以两种场景结果不同。
内容的提问来源于stack exchange,提问作者Kabiljan Tanaguzov
相关产品推荐
相关产品推荐

