Зачем разделять celery worker и django container?

Я создаю приложение django с помощью celery. Я попробовал составить docker-compose без контейнера для рабочего. В моем Dockerfile для django точка входа, запускающая celery worker и django app:

...
python manage.py migrate
celery -A api worker -l INFO --detach
python manage.py runserver 0.0.0.0:8000

Celery будет запускаться, используя этот порядок, но не django runserver. Я видел в учебниках, что они отделили контейнер django от контейнера woker или наоборот. Я не вижу объяснения этому разделению. Я также заметил, что оба контейнера python (django, worker) имеют одинаковый объем. Как celery может добавлять задачи, если у него разное окружение с django? В моем воображении было бы два приложения django (тот же объем) для двух контейнеров, только один запускает runserver, а другой - celery worker. Я не понимаю разделения.

Как упоминает Celery документация:

Celery общается с помощью сообщений, обычно используя брокера для посредничества между клиентами и рабочими. Чтобы инициировать задачу, клиент добавляет сообщение в очередь, брокер затем доставляет это сообщение рабочему. рабочему.

То есть связь между клиентом (Django) и рабочим (Celery) осуществляется через очередь сообщений. Следовательно, не имеет значения, находятся ли рабочие и клиенты в отдельных контейнерах или даже на разных машинах. Если клиент может получить доступ к очереди сообщений (например, используя Redis или RabbitMQ), а рабочий может выполнять задания из этой очереди, то все будет работать.

Что касается части docker-compose, нет идеального стандарта для хранения или разделения Celery и Django. Вы можете поместить их в один контейнер или нет, это зависит от вас и от требований проекта.

Вы должны стремиться настроить свои контейнеры так, чтобы в каждом контейнере выполнялся только один процесс переднего плана и никаких фоновых процессов. Даже в этом простом примере есть два очевидных преимущества: если рабочий Celery выйдет из строя, вы можете перезапустить отдельный контейнер, но он будет невидим для Docker как фоновый процесс; и вы можете отдельно читать docker logs веб-сервера и фонового рабочего, не переплетая их. При больших масштабах можно представить, что вам захочется запускать разное количество контейнеров Django и Celery в зависимости от нагрузки.

Для того чтобы это работало, важно, чтобы сценарий точки входа не запускал программу напрямую. В качестве аргументов ему передается (возможно, переопределенная) команда контейнера, и вы можете использовать специальную конструкцию оболочки для ее запуска

#!/bin/sh
./manage.py migrate
exec "$@"

В Dockerfile объявите как ENTRYPOINT, так и стандартный CMD для запуска, скажем, веб-сервера

ENTRYPOINT ["./entrypoint.sh"]  # probably unchanged, must be JSON array syntax
CMD ["./manage.py", "runserver", "0.0.0.0:8000"]

В установке Compose вы можете запускать несколько контейнеров с одного образа, но переопределять command: для рабочего Celery.

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

Главное приложение взаимодействует с рабочим через очередь в Redis (или другом хранилище), поэтому нет необходимости в том, чтобы они находились в одном контейнере.

Вернуться на верх