Подтвердить что ты не робот

Несколько точек входа Docker

Скажем, у меня есть следующий файл Docker:

FROM ubuntu

RUN apt-get update
RUN apt-get install -y apache2
RUN apt-get install -y mongod #pretend this exists

EXPOSE 80

ENTRYPOINT ["/usr/sbin/apache2"]

Команда ENTRYPOINT делает это так, что apache2 запускается при запуске контейнера. Я также хочу запустить mongod, когда контейнер начинается с команды service mongod start. Согласно документации, в файле Docker должен быть только один ENTRYPOINT. Что было бы правильным способом сделать это?

4b9b3361

Ответ 1

Как сказал Джаред Маркелл, если вы хотите запустить несколько процессов в док-контейнере, вы должны использовать supervisor. Вам нужно будет настроить супервизора, чтобы он велел запускать ваши разные процессы.

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

По сути, вы захотите сделать что-то вроде:

FROM ubuntu

RUN apt-get update
RUN apt-get install -y apache2
RUN apt-get install -y mongod #pretend this exists
RUN apt-get install -y supervisor # Installing supervisord

ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf 

EXPOSE 80

ENTRYPOINT ["/usr/bin/supervisord"]

И добавьте конфигурацию в файл supervisord.conf

[supervisord]
nodaemon=true

[program:mongodb]
command=/etc/mongod/mongo #To adapt, I don't know how to launch your mongodb process

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

РЕДАКТИРОВАТЬ: Поскольку этот ответ получил довольно много голосов, я хочу привести в качестве предупреждения, что использование Supervisor не считается лучшей практикой для выполнения нескольких заданий. Вместо этого вы можете быть заинтересованы в создании нескольких контейнеров для ваших различных процессов и управлении ими через docker compose. В двух словах, Docker Compose позволяет вам определять в одном файле все контейнеры, необходимые для вашего приложения, и запускать их одной командой.

Ответ 2

Мое решение - бросить отдельные скрипты в /opt/run/ и выполнить их с помощью:

#!/bin/bash

LOG=/var/log/all

touch $LOG

for a in /opt/run/*
do
    $a >> $LOG &
done

tail -f $LOG

И моя точка входа - это только расположение этого script, скажем, оно называется /opt/bin/run_all:

ADD 00_sshd /opt/run/
ADD 01_nginx /opt/run/

ADD run_all /opt/bin/
ENTRYPOINT ["/opt/bin/run_all"]

Ответ 3

Ответ прост: вы не должны этого делать, потому что это нарушает принцип единой ответственности: один контейнер, один сервис. Представьте, что вы хотите порождать дополнительные облачные образы MongoDB из-за внезапной рабочей нагрузки - зачем также увеличивать экземпляры Apache2 и в соотношении 1:1? Вместо этого вы должны связать блоки и заставить их говорить через TCP. См. Https://docs.docker.com/userguide/dockerlinks/ для получения дополнительной информации.

Ответ 4

Мне не удалось использовать && для работы. Я смог решить это, как описано здесь: fooobar.com/questions/155978/...

Итак, в вашем случае вы можете сделать:

RUN echo "/usr/sbin/apache2" >> /etc/bash.bashrc
RUN echo "/path/to/mongodb" >> /etc/bash.bashrc
ENTRYPOINT ["/bin/bash"]

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

Будьте осторожны, если вы запустите свой файл Docker более одного раза, вы, вероятно, не хотите добавлять несколько копий команд в ваш файл bash.bashrc. Вы можете использовать grep и инструкцию if, чтобы сделать команду RUN idempotent.

Ответ 5

Я могу представить несколько способов:

  • вы можете написать script для размещения контейнера (ADD), который выполняет все команды запуска, а затем поместите это в ENTRYPOINT
  • Я думаю, вы можете поместить любые команды оболочки в ENTRYPOINT, поэтому вы можете сделать service mongod start && /usr/sbin/apache2

Ответ 6

В документации докера есть ответ: https://docs.docker.com/config/containers/multi-service_container/

Но короче

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

Первый - запустить скрипт, который управляет вашим процессом.

Второй - использовать диспетчер процессов, например, supervisord

Ответ 7

Вы не можете указать несколько точек входа в Dockerfile. Чтобы запустить несколько серверов в одном док-контейнере, вы должны использовать команду, которая сможет запускать ваши серверы. Об Supervisord уже упоминалось, но я также могу порекомендовать multirun, мой проект, который является более легкой альтернативой.

Ответ 9

Как правило, вы бы этого не делали. Это анти-шаблон, потому что:

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

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


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

ENTRYPOINT ["/entrypoint-parent.sh"]

и вы хотите добавить следующее к вашему дочернему изображению:

ENTRYPOINT ["/entrypoint-child.sh"]

Затем значение ENTRYPOINT в полученном изображении заменяется на /entrypoint-child.sh, другими словами, для ENTRYPOINT существует только одно значение. Docker вызовет только один процесс для запуска вашего контейнера, хотя этот процесс может порождать дочерние процессы. Есть несколько методов для расширения точек входа.

Вариант A: вызовите вашу точку входа, а затем запустите родительскую точку входа в конце, например, /entrypoint-child.sh может выглядеть так:

#!/bin/sh

echo Running child entrypoint initialization steps here
/usr/bin/mongodb ... &

exec /entrypoint-parent.sh "[email protected]"

Часть exec важна, она заменяет текущую оболочку оболочкой или процессом /entrypoint-parent.sh, что устраняет проблемы с обработкой сигналов. В результате вы запускаете первый бит инициализации в дочерней точке входа, а затем делегируете исходную родительскую точку входа. Для этого необходимо, чтобы вы отслеживали имя родительской точки входа, это может измениться между версиями вашего базового образа. Это также означает, что вы теряете обработку ошибок и изящное завершение работы на mongodb, поскольку он запускается в фоновом режиме. Это может привести к ложному исправному контейнеру и потере данных, которые я бы не рекомендовал для продакшен среды.

Вариант Б: Запустите родительскую точку входа в фоновом режиме. Это не идеально, так как вы больше не будете обрабатывать ошибки в родительском процессе, если не предпримете никаких дополнительных шагов. В простейшем случае это выглядит следующим образом в вашем /entrypoint-child.sh:

#!/bin/sh

# other initialization steps

/entrypoint-parent.sh "[email protected]" &

# potentially wait for parent to be running by polling

# run something new in the foreground, that may depend on parent processes
exec /usr/bin/mongodb ...

Обратите внимание, что нотация "[email protected]" я продолжаю использовать, передается через значение CMD качестве аргументов родительской точке входа.

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

Вариант D: Подобно опциям A и B, я часто создаю каталог сценариев точки входа, который можно расширять на разных уровнях сборки образа. Сама точка входа не изменяется, я просто добавляю новые файлы в каталог, который вызывается последовательно на основе имени файла. В моих сценариях все эти сценарии выполняются на переднем плане, и в конце я запускаю CMD. Вы можете увидеть пример этого в моем entrypoint.d базовых изображений, в частности в entrypoint.d и bin/entrypointd.sh который включает в себя раздел:

# ...

for ep in /etc/entrypoint.d/*; do
  ext="${ep##*.}"
  if [ "${ext}" = "env" -a -f "${ep}" ]; then
    # source files ending in ".env"
    echo "Sourcing: ${ep}"
    set -a && . "${ep}" && set +a
  elif [ "${ext}" = "sh" -a -x "${ep}" ]; then
    # run scripts ending in ".sh"
    echo "Running: ${ep}"
    "${ep}"
  fi
done

# ...

# run command with exec to pass control
echo "Running CMD: [email protected]"
exec "[email protected]"

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