Как службе docker удалось вызвать экземпляр из отдельного контейнера docker?

#docker #docker-compose #dockerfile #celery

#docker #docker-compose #dockerfile #сельдерей

Вопрос:

Недавно я начал использовать Docker Celery. Я также поделился полными примерами кодов для этого примера на github, и ниже приведены некоторые фрагменты кода из него, чтобы помочь объяснить мою точку зрения.

Для контекста мой пример разработан как узел, который подписывается на события в системе микросервисов. В этом узле он состоит из следующих служб:

  1. подписчик (использует kombu для подписки на события)
  2. рабочий (использующий сельдерей для асинхронной задачи, действующей на события)
  3. 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 , чтобы иметь в виду!