Дизайн Docker: обменивайтесь данными между контейнерами или помещайте несколько процессов в один контейнер?

#docker #architecture

#docker #архитектура

Вопрос:

В текущем проекте я должен выполнить следующие задачи (среди прочих):

  • захватывайте видеокадры с пяти IP-камер и объединяйте панораму
  • запустите обнаружение объектов на основе машинного обучения в panorama
  • транслируйте панораму, чтобы ее можно было отобразить в пользовательском интерфейсе

В настоящее время объединение и потоковая передача выполняются в одном контейнере docker, а обнаружение объектов выполняется в другом, считывая поток panorama в качестве входных данных.

Поскольку мне нужно увеличить разрешение ввода для детектора объектов при сохранении разрешения потока для пользовательского интерфейса, я должен искать альтернативные способы получения сшитой панорамы (в полном разрешении) (~ 10 МБ на кадр) из контейнера прошивки в контейнер детектора.

Мои мысли относительно потенциальных решений:

  • общий том. Потенциальный недостаток: одна дополнительная запись и чтение за кадр могут быть слишком медленными?
  • Использование очереди сообщений или, например, redis. Потенциальный недостаток: еще один компонент в архитектуре.
  • объединение двух контейнеров. Потенциальный недостаток (ы): мало того, что это кажется неправильным, но два контейнера имеют совершенно разные базовые образы и зависимости. Плюс мне пришлось бы беспокоиться о распараллеливании.

Поскольку я не самый острый нож в ящике docker, то, о чем я прошу, это советы, опыт и лучшие практики относительно быстрого обмена данными между контейнерами docker.

Ответ №1:

Обычно большая часть обмена данными между контейнерами Docker осуществляется через сетевые сокеты. Это нормально, когда вы общаетесь с чем-то вроде реляционной базы данных или HTTP-сервера. Похоже, что ваше приложение немного больше предназначено для совместного использования файлов, и в этом Docker немного уступает.

Если вам нужна только одна копия каждого компонента или вы все еще активно разрабатываете конвейер: я бы, вероятно, не использовал Docker для этого. Поскольку каждый контейнер имеет изолированную файловую систему и собственное пространство идентификаторов пользователей, совместное использование файлов может оказаться неожиданно сложным (каждый контейнер должен согласовывать числовые идентификаторы пользователей). Но если вы просто запускаете все на хосте от имени того же пользователя, указывая на тот же каталог, это не проблема.

Если вы пытаетесь масштабировать это в рабочей среде: я бы добавил какую-нибудь общую файловую систему и систему очередей сообщений, такую как RabbitMQ. Для локальной работы это может быть Docker с именем volume или каталог хоста, подключенный к привязке; облачное хранилище, такое как Amazon S3, тоже будет работать нормально. Настройка такая:

  • Каждый компонент знает об общем хранилище и подключается к RabbitMQ, но не знает о других компонентах.
  • Каждый компонент считывает сообщение из очереди RabbitMQ, в котором указывается имя файла для обработки.
  • Компонент считывает файл и выполняет свою работу.
  • По завершении работы компонент записывает файл результата обратно в общее хранилище и записывает его местоположение в RabbitMQ exchange.

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

Эта модель также хорошо подходит для крупномасштабных кластерных вычислительных систем на базе Docker, таких как Kubernetes.

Запуская это локально, я бы абсолютно сохранил отдельные задачи в отдельных контейнерах (особенно, если отдельные задачи обработки изображений и ML являются дорогостоящими). Для предлагаемой мною настройки требуется как очередь сообщений (для отслеживания работы), так и общая файловая система (поскольку очереди сообщений, как правило, не оптимизированы для отдельных сообщений размером более 10 МБ). Вы получаете выбор между именованными томами Docker и привязкой к хосту в качестве легкодоступного общего хранилища. Привязки проще проверять и администрировать, но на некоторых платформах они невероятно медленные. Я думаю, что именованные тома работают достаточно быстро, но вы можете получить к ним доступ только из контейнеров Docker, что означает необходимость запуска большего количества контейнеров для выполнения основных действий, таких как резервное копирование и обрезка.

Ответ №2:

Хорошо, давайте распакуем это:

  • IMHO общий том работает просто отлично, но со временем становится слишком запутанным. Особенно, если вы работаете со службами с отслеживанием состояния.
  • MQ: На мой взгляд, это лучший вариант. Да, это еще один компонент вашей архитектуры, но имеет смысл иметь его, а не поддерживать беспорядочные общие тома или обрабатывать массивные образы контейнеров (если вам удается объединить 2 образа контейнеров)
  • Да, потенциально вы могли бы это сделать, но это не очень хорошая идея. Учитывая ваш вариант использования, я собираюсь пойти дальше и сделать предположение, что у вас есть огромный список зависимостей, которые потенциально могут привести к конфликту. Кроме того, множество зависимостей = увеличенное изображение = Увеличенная поверхность атаки — что с точки зрения безопасности не очень хорошо.

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

https://docs.docker.com/config/containers/multi-service_container/