🐳 Как запустить несколько служб в одном контейнере Docker |

🐳 Как запустить несколько служб в одном контейнере Docker

Мануал

Docker – это технология упаковки компонентов вашего стека в виде изолированных контейнеров.

Обычная практика – запускать каждый процесс в отдельном контейнере, создавая чистое разделение между компонентами.

Это повышает модульность и позволяет использовать преимущества масштабируемости, которые дает контейнеризация.

Тем не менее, могут возникнуть ситуации, когда вы захотите запустить несколько сервисов в одном контейнере.

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

Определение проблемы

Контейнеры Docker запускают один процесс переднего плана.

Это определяется инструкциями ENTRYPOINT и CMD образа.

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

Контейнеры автоматически останавливаются, когда завершается их процесс переднего плана.

Вы можете запускать другие процессы из CMD, но контейнер будет работать только до тех пор, пока жив исходный процесс переднего плана. Сохранение работоспособности контейнера в течение всего срока службы двух независимых сервисов напрямую невозможно с помощью механизма ENTRYPOINT/CMD.

Обертывание нескольких процессов

Скрипты-обертки – это самое простое решение проблемы.

Вы можете написать скрипт, который запускает все ваши процессы и ждет их завершения.

Установив этот скрипт в качестве Docker ENTRYPOINT, вы запустите его как процесс переднего плана контейнера, и контейнер будет работать до тех пор, пока один из обернутых скриптов не завершится.

#!/bin/bash

/opt/first-process &

/opt/second-process &

wait -n

exit $?

Этот скрипт запускает бинарные файлы /opt/first-process и /opt/second-process внутри контейнера.

Использование & позволяет скрипту продолжать работу, не дожидаясь завершения каждого процесса. wait используется для приостановки работы сценария до тех пор, пока один из процессов не завершится.

Затем скриптзавершается с кодом состояния, выданным завершенным сценарием.

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

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

Чтобы использовать этот скрипт, измените ENTRYPOINT и CMD вашего образа Docker, чтобы сделать его процессом переднего плана контейнера:

ENTRYPOINT ["/bin/sh"]
CMD ["./path/to/script.sh"]

Опция –init контейнера

Одна из проблем управления контейнерными процессами – эффективная очистка после их завершения.

Docker запускает ваш CMD как процесс с идентификатором 1, что делает его ответственным за обработку сигналов и устранение зомби.

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

Команда docker run имеет флаг –init, который изменяет точку входа, чтобы использовать tini в качестве PID 1.

Это минимальная реализация процесса init, которая запускает ваш CMD, обрабатывает пересылку сигналов и постоянно пожинает зомби.

Стоит использовать –init, если вы ожидаете породить много процессов и не хотите вручную заниматься их очисткой.

Tini – это облегченный вариант init, предназначенный для контейнеров.

Он намного меньше, чем полноценные альтернативы, такие как systemd и upsta.

Использование специализированного менеджера процессов

Ручное выполнение скриптов быстро становится неоптимальным, если вам нужно управлять большим количеством процессов.

Использование менеджера процессов – это еще один способ запуска нескольких служб внутри контейнеров Docker.

Менеджер процессов становится вашим ENTRYPOINT и несет ответственность за запуск, обслуживание и очистку рабочих процессов.

Существует несколько вариантов реализации этого подхода. supervisord является популярным выбором, который легко настраивается с помощью файла /etc/supervisor/conf.d/supervisord.conf:

[program:apache2]
command=/usr/sbin/apache2 -DFOREGROUND

[program:mysqld]
command=/usr/sbin/mysqld_safe

Этот конфигурационный файл настраивает supervisord на запуск Apache и MySQL.

Чтобы использовать его в контейнере Docker, добавьте все необходимые пакеты в образ, а затем скопируйте конфигурационный файл supervisord в нужное место.

Установите supervisord в качестве CMD, чтобы он запускался автоматически при старте контейнера.

FROM ubuntu:latest
RUN apt-get install -y apache2 mysql-server supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
ENTRYPOINT ["/bin/sh"]
CMD ["/usr/bin/supervisord"]

Поскольку supervisord работает непрерывно, невозможно остановить контейнер при завершении одного из контролируемых процессов.

Альтернативным вариантом является s6-overlay, который имеет такую возможность.

Он использует декларативную модель сервисов, где вы размещаете скрипты сервисов непосредственно в файле /etc/services.d:

# Добавим s6-overlay в образ
ADD https://github.com/just-containers/s6-overlay/releases/download/v3.1.0.0/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz

RUN printf "#!/bin/shn/usr/sbin/apache2 -DFOREGROUND" > /etc/services.d/first-service/run
RUN chmod +x /etc/services.d/first-service/run

# Используйте s6-overlay в качестве entrypoint
ENTRYPOINT ["/init"]

Вы можете добавить исполняемый скрипт завершения в каталоги ваших сервисов для обработки остановки контейнера с помощью docker stop.

s6-overlay будет автоматически запускать эти скрипты, когда его процесс получит сигнал TERM из-за команды stop.

Скрипты завершения получают код завершения своего сервиса в качестве первого аргумента.

Код устанавливается в 256, когда служба завершается из-за не пойманного сигнала.

Скрипт должен записать код завершения в файл /run/s6-linux-init-container-results/exitcode; s6-overlay считывает этот файл и завершает работу с указанным в нем значением, в результате чего этот код будет использован в качестве кода остановки вашего контейнера.

#!/bin/sh
echo "$1" > /run/s6-linux-init-container-results/exitcode

Когда следует запускать несколько процессов в контейнере?

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

У вас может быть программа, которая полагается на фоновую вспомогательную утилиту, или монолитное приложение, которое самостоятельно управляет отдельными процессами.

Приведенные выше приемы помогут вам контейнеризировать такие типы программ.

По возможности следует избегать запуска нескольких процессов в контейнере.

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

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

Заключение

Контейнеры обычно имеют один процесс на переднем плане и выполняются до тех пор, пока он жив.

Эта модель соответствует лучшим практикам контейнеризации и позволяет получить максимальную выгоду от технологии.

В некоторых ситуациях вам может понадобиться несколько процессов для запуска в контейнере.

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

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

Скрипты-обертки более просты, но могут потребовать использования флага Docker –init для предотвращения разрастания зомби-процессов.

 

Добавить комментарий