Можем ли мы выполнить скрипт от имени root во время запуска контейнера Docker?

Вопрос или проблема

Я пытаюсь выполнить скрипт во время запуска контейнера.

Для обычной машины на Linux, если у меня есть скрипт в /etc/rc.local, он будет выполняться при перезагрузке.

Я пытаюсь сделать то же самое при сборке Docker-образа. Я переписываю файл /etc/rc.local новым содержимым и затем выполняю

sudo docker run -d <image id>

Я ожидал, что /etc/rc.local будет выполняться, однако ничего не происходит.

Я делаю это в неправильном месте?

Docker запускает только тот процесс (или процессы), который вы указываете.

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

Если вы хотите, чтобы /etc/rc.local выполнялся, вам придется запустить его самостоятельно. Что-то вроде:

docker run image /bin/bash -c "/etc/rc.local; <ваша команда>"

Существуют альтернативные базовые образы, которые поддерживают запуск сервисов при старте, например, phusion/baseimage-docker.

Прочтите эту статью, чтобы понять, какие проблемы и вопросы возникают с Docker при запуске контейнера.

COPY <<'DASH' /etc/rc.local
    set -x
    printenv
DASH

RUN chmod +x /etc/rc.local
ENTRYPOINT dash -xc '/etc/rc.local && <оригинальный entrypoint> $@' $@
CMD <оригинальный cmd>

Объяснение:

  1. Файл /etc/rc.local — это историческое название файла для размещения скриптов, которые будут выполнены демон-программы допредсистемной эры SysV init во время загрузки системы.

    Еще один похожий путь для этой цели — /etc/init.d/*.

    Здесь мы просто берем это название для удобства, так как в контейнере Docker по умолчанию нет демона init/systemd, и ENTRYPOINT является pid 1.

  2. Оригинальное значение ENTRYPOINT образа можно найти в его Dockerfile или оно может быть переопределено в compose.yaml.

  3. Установка нового ENTRYPOINT сбросит оригинальный CMD в пустую строку:

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

    так что нам нужно скопировать значение CMD из Dockerfile оригинального образа или compose.yaml, если оно переопределено в нем.

  4. sh -xc 'echo $@' 1 2 3 — это способ передать аргументы оболочки в sh -c, и этот пример выполнит echo arg, что можно проверить с помощью set -x.

  5. dash — это еще одна реализация оболочки sh, которая быстрее чем bash и используется как по умолчанию /bin/sh в Debian.

    Если вы используете какие-либо bashism функции в /etc/rc.local, смело заменяйте их на bash или другие реализации оболочки.

  6. $@ — это значение всех аргументов оболочки, кроме первого, как $argv[0] или $0, которое передается в execv.

    В среде оболочки entrypoint, когда создан контейнер, его $@ будет значением CMD из Dockerfile, поэтому мы можем передать значение CMD из внешней оболочки во внутреннюю, создаваемую sh -c 'echo $@' $@.


В качестве примера использования официального docker-образа php:
Мы можем найти его оригинальный ENTRYPOINT как docker-php-entrypoint и оригинальный CMD как php-fpm, поэтому мы должны заполнить их таким образом:

ENTRYPOINT dash -xc '/etc/rc.local && docker-php-entrypoint $@' $@
CMD php-fpm

Если порядок выполнения скрипта до или после начала работы entrypoint не имеет значения для вас, попробуйте также более простой post-start цикл в Docker Compose.

Ответ или решение

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

Проблема и Контекст

На вашем Linux сервере использование директории /etc/rc.local позволяет автоматически запускать скрипты при каждом перезагрузке системы. Однако в случае Docker, эта концепция не распространяется на контейнеры, так как в них отсутствует установленная система init, которая бы выполняла подобные задачи.

Решение для Docker

Чтобы скрипт, находящийся в /etc/rc.local, был выполнен при запуске контейнера, необходимо вручную инициировать его выполнение через Dockerfile или команду запуска docker run.

Шаги для Запуска Скрипта:

  1. Модификация Dockerfile:

    • Скопируйте ваш скрипт в контейнер и сделайте его исполняемым. Например:
      COPY my-script.sh /etc/rc.local
      RUN chmod +x /etc/rc.local
  2. Обновление команды ENTRYPOINT или CMD:

    • На этапе сборки Docker-образа можно изменить команду ENTRYPOINT или CMD, чтобы она включала выполнение вашего скрипта.
    • Например, замените стандартный ENTRYPOINT следующей строкой:
      ENTRYPOINT ["/bin/bash", "-c", "/etc/rc.local && exec <your_original_entrypoint>"]
  3. Использование Альтернативных Базовых Образов:

    • Если ваш случай использования предполагает настройку служб и процессов, подобных традиционной ОС, рассмотрите использование базового образа, поддерживающего запуск служб, например, phusion/baseimage-docker.
  4. Команды и Хуки Docker Compose:

    • Если приоритет выполнения скрипта (до или после запуска основного процесса) не критичен, можно использовать хуки post-start в рамках Docker Compose.

Дополнительные Ресурсы

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

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

Капча загружается...