#docker #docker-compose #dockerfile #celery
#docker #docker-compose #dockerfile #сельдерей
Вопрос:
Недавно я начал использовать Docker Celery. Я также поделился полными примерами кодов для этого примера на github, и ниже приведены некоторые фрагменты кода из него, чтобы помочь объяснить мою точку зрения.
Для контекста мой пример разработан как узел, который подписывается на события в системе микросервисов. В этом узле он состоит из следующих служб:
- подписчик (использует kombu для подписки на события)
- рабочий (использующий сельдерей для асинхронной задачи, действующей на события)
- Redis (как посредник сообщений и серверная часть результатов для celery)
Службы определены в docker-compose.yml
файле следующим образом:
version: "3.7"
services:
# To launch the Subscriber (using Kombu incl. in Celery)
subscriber:
build: .
tty: true
#entrypoint: ...
# To launch Worker (Celery)
worker:
build: .
entrypoint: celery worker -A worker.celery_app --loglevel=info
depends_on:
- redis
redis:
image: redis
ports:
- 6379:6379
entrypoint: redis-server
Для простоты я опустил коды для подписчика, и я подумал, что для этого примера должно быть достаточно использования интерактивной оболочки python в контейнере подписчика:
python3
>>> from worker import add
>>> add.delay(2,3).get()
5
И в журналах рабочего контейнера:
worker_1 | [2020-09-17 10:12:34,907: INFO/ForkPoolWorker-2] worker.add[573cff6c-f989-4d06-b652-96ae58d0a45a]: Adding 2 3, res: 5
worker_1 | [2020-09-17 10:12:34,919: INFO/ForkPoolWorker-2] Task worker.add[573cff6c-f989-4d06-b652-96ae58d0a45a] succeeded in 0.011764664999645902s: 5
Хотя все, кажется, работает, я чувствовал себя неловко. Я думал, что в этом примере не соблюдается принцип изоляции контейнера docker.
Разве контейнеры не предназначены для изоляции на уровне ОС, процессов и сети? И если контейнеры должны обмениваться данными, разве это не должно выполняться через IP-адрес и сетевые протоколы (TCP / UDP и т. Д.)
Во-первых, рабочий и подписчик запускают одну и ту же кодовую базу в моем примере, поэтому никаких проблем с инструкцией import не ожидается.
Однако рабочий элемент celery запускается из entrypoint
контейнера worker, таким образом, как подписчику удалось вызвать экземпляр celery worker в предположительно изолированном рабочем контейнере?
Чтобы дополнительно убедиться, что он действительно вызывает экземпляр celery worker из контейнера worker, я остановил контейнер worker и повторил пример интерактивной оболочки python в контейнере subscriber. Ожидаемый запрос (ожидаемый от сельдерея) и вернул тот же результат, как только рабочий контейнер снова будет включен. Итак, IMO, да, служба из одного контейнера вызывает экземпляр приложения из другого контейнера БЕЗ подключения к сети, как в случае подключения к redis (используя IP-адрес и т.д.).).
Пожалуйста, сообщите, если мое понимание неверно или где-то может быть неправильная реализация, о которой я не знаю.
Ответ №1:
Как потребитель (worker), так и производитель (subsriber) настроены на использование Redis (редис) как в качестве посредника, так и в качестве серверной части результата. Вот почему все это сработало. — Когда вы выполняли add.delay(2,3).get()
в контейнере подписчика, он отправил задачу в Redis, и она была выбрана работником Celery, запущенным в другом контейнере. Имейте в виду, что процесс Python, выполняющий add.delay(2,3).get()
код, выполняется в контейнере подписчика, в то время как ForkPoolWorker-2
процесс, который выполнил функцию add() и сохранил результат в серверной части результата, выполняется в рабочем контейнере. Эти процессы полностью независимы.
Процесс подписчика ничего не вызывал в рабочем контейнере! — На простом английском языке он сделал следующее: «здесь (в Redis) то, что мне нужно сделать, пожалуйста, сделайте это и дайте мне знать, что вы закончили, чтобы я мог получить результат».
Комментарии:
1. 1 Деянлекич. Я продолжил доказывать вашу точку зрения, останавливая worker и отслеживая данные, вставленные в Redis, когда
add.delay(2,3).get()
вызывается несколько раз от подписчика последовательно. Конечно же, задачи находились в очереди (Redis). Когда рабочий запускается снова, очередь очищается, и результат возвращается подписчику! Просто для того, чтобы это стоило того, я также попытался сделатьadd.apply_async((1,2), countdown=10).get()
на подписчике, чтобы приостановить очистку очереди рабочим, чтобы я был вдвойне уверен, и да, он ведет себя так же, как описано.
Ответ №2:
Docker-compose создает сеть docker по умолчанию для контейнеров, созданных в одном файле. Поскольку вы указываете все соответствующим образом, он выполняет запросы по этой сети, и именно поэтому это успешно. Я был бы удивлен, узнав, что это все еще работает, если бы вы, например, параллельно запускали каждый контейнер отдельно без использования docker-compose.
Комментарии:
1. Да, я согласен. Но я чувствовал, что принятый ответ содержал более краткое указание на то, что Redis является моим слепым пятном, что привело к вопросу. Тем не менее, вы также сделали соответствующую заметку о
docker-compose
, чтобы иметь в виду!