为什么要将 celery Worker 和 django 容器分开?

Nik*_*kko 7 django celery docker

我正在用 celery 构建一个 django 应用程序。我尝试编写一个没有容器的 docker-compose 供工作人员使用。在我的 django Dockerfile 中,运行 celery Worker 和 django 应用程序的入口点:

...
python manage.py migrate
celery -A api worker -l INFO --detach
python manage.py runserver 0.0.0.0:8000
Run Code Online (Sandbox Code Playgroud)

celery 将使用此命令运行,但不会使用 django runserver。我在教程中看到他们将 django 容器与woker 容器分开,反之亦然。我没有看到这种分离的解释。我还观察到两个 python 容器(django、worker)具有相同的体积。celery与django环境不同,如何添加任务?在我看来,两个容器将有两个 django 应用程序(相同的卷),其中一个运行 runserver,另一个运行 celery worker。我不明白分离。

rud*_*dra 6

正如 Celery文档提到的:

Celery 通过消息进行通信,通常使用代理在客户端和工作人员之间进行调解。为了启动任务,客户端将消息添加到队列中,然后代理将该消息传递给工作人员。

这意味着客户端(Django)和工作人员(Celery)之间的通信是通过消息队列完成的。因此,工人和客户是否在不同的容器甚至不同的机器中并不重要。如果客户端可以访问消息队列(例如使用 Redis 或 RabbitMQ)并且工作线程可以从该队列中弹出任务,那么它将始终有效。

关于 docker-compose 部分,没有保留或分离 Celery 和 Django 的理想标准。您可以将它们放在同一个容器中,也可以不同,这取决于您以及项目的要求。如果您使用两个容器,那么它们需要共享卷,因为执行任务需要源代码和任何其他数据。


Dav*_*aze 5

您应该将容器设置为在每个容器中仅运行一个前台进程,而不运行任何后台进程。即使在这个简单的例子中,也有两个明显的优点:如果 Celery Worker 失败,你可以重新启动一个独立的容器,但它作为后台进程对 Docker 来说是不可见的;您可以分别阅读docker logsWeb 服务器和后台工作人员的信息,而无需将它们交织在一起。在更大规模的情况下,您可以想象根据负载运行不同数量的 Django 和 Celery 容器。

为了使这项工作正常进行,入口点脚本不能直接运行程序,这一点很重要。它通过(可能被覆盖的)容器命令作为参数传递,并且您可以使用特殊的 shell 构造来运行该命令

#!/bin/sh
./manage.py migrate
exec "$@"
Run Code Online (Sandbox Code Playgroud)

在 Dockerfile 中,声明ENTRYPOINT并默认CMD运行 Web 服务器

ENTRYPOINT ["./entrypoint.sh"]  # probably unchanged, must be JSON array syntax
CMD ["./manage.py", "runserver", "0.0.0.0:8000"]
Run Code Online (Sandbox Code Playgroud)

在 Compose 设置中,您可以在同一个映像上运行多个容器,但要覆盖command:Celery Worker 的容器。

version: '3.8'
services:
  web:
    build: .
    ports: ['8000:8000']
    environment:
      REDIS_HOST: redis
  worker:
    build: .
    command: celery -A api worker -l INFO
    environment:
      REDIS_HOST: redis
  redis:
    image: redis
Run Code Online (Sandbox Code Playgroud)

主应用程序通过 Redis(或其他存储)中的队列与工作线程进行通信,因此它们不需要位于同一个容器中。