根据您的应用需求和设计,您可能需要将不同任务的工作人员分离到不同的容器中。
但是,如果资源使用率较低,并且合并多个工作人员到单个容器中是有意义的,则可以通过入口脚本实现。
编辑2019-12-05: 运行一段时间后,这对于生产使用来说并不是一个好主意。有两个注意点:
存在后台工作进程默默退出但前台没有捕获的风险。 tail -f
将继续运行,但 docker 将不知道后台工作进程已停止。根据您 celery 的调试级别设置,日志可能会显示一些指示,但当您执行 docker ps
时,docker 是不知道的。为了可靠,工作进程需要在失败后重新启动,这带来了使用 supervisord
的建议。
当容器启动和停止(但未删除)时,docker 容器状态被保留。这意味着如果您的 celery 工作进程依赖于 pidfile 进行识别,但出现非正常关闭,则 pidfile 可能会被保留,并且即使使用 docker stop; docker start
,工作进程也无法清洁地重启。这是由于 celery 启动检测到上一个不干净的关闭中剩余的 PIDfile 的存在。为了防止多个实例,重新启动的工作进程会用“PIDfile found,celery is already running?”自动停止。整个容器必须使用 docker rm
或 docker-compose down; docker-compose up
来删除。处理此问题的几种方法:
a. 必须使用 --rm
标志运行容器,以便在容器停止后删除容器。
b. 或许不包括 celery multi
或 celery worker
命令中的 --pidfile
参数会更好。
总体建议:最好使用supervisord
。
现在,进入细节:
Docker容器需要运行前台任务,否则容器将退出。下面将进一步解决这个问题。
此外,celery工作者可能会运行长时间的任务,并需要响应docker的关闭(SIGTERM)信号以优雅地关闭即在关闭或重启之前完成长时间运行的任务。
为了实现docker信号传播和处理,最好在dockerfile中声明entrypoint
,并使用docker的exec形式,也可以在docker-compose
文件中进行此操作。
此外,由于celery multi在后台工作,docker看不到任何日志。您需要能够在前台显示日志,以便让docker logs
能够查看发生了什么。我们将通过为celery多工作者设置logfile并在控制台前台显示tail -f <logfile_pattern>
来无限期地运行来实现这一点。
我们需要实现三个目标:
- 使用前台任务运行docker容器
- 接收、
trap
和处理docker关闭信号
- 优雅地关闭工作者
对于 #1,我们将运行 tail -f &
,然后将其作为前台任务 wait
。
对于 #2,这可以通过设置 trap
函数并捕获信号来实现。要使用 trap 函数接收和处理信号,必须在 #1 中实现正在运行的前台任务 wait
。
对于 #3,我们将在启动时以 celery multi start
中的其他参数参数运行 celery multi stop <number_of_workers_in_start_command>
。
以下是我编写的gist,在此处复制:
#!/bin/sh
set -o errexit
set -o nounset
teardown()
{
echo " Signal caught..."
echo "Stopping celery multi gracefully..."
celery -A config.celery_app multi stop 3 --pidfile=./celery-%n.pid --logfile=./celery-%n%I.log
echo "Stopped celery multi..."
echo "Stopping last waited process"
kill -s TERM "$child" 2> /dev/null
echo "Stopped last waited process. Exiting..."
exit 1
}
celery -A config.celery_app multi start 3 -l INFO -Q:1 queue1 -Q:2 queue1 -Q:3 queue3,celery -c:1-2 1 \
--pidfile=./celery-%n.pid \
--logfile=./celery-%n%I.log
trap teardown SIGINT SIGTERM
tail -f ./celery*.log &
child=$!
wait "$child"
将上述代码用作入口脚本文件的内容,并根据需要进行修改。
在dockerfile或docker-compose文件中以exec形式声明:
ENTRYPOINT ["entrypoint_file"]
然后,Celery工作者可以在Docker容器中运行,也可以优雅地停止。
supervisord
进行实现。 - Taras Mykhalchuk