Вопрос или проблема
Я пытаюсь выполнить скрипт во время запуска контейнера.
Для обычной машины на 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>
Объяснение:
-
Файл
/etc/rc.local
— это историческое название файла для размещения скриптов, которые будут выполнены демон-программы допредсистемной эры SysV init во время загрузки системы.Еще один похожий путь для этой цели —
/etc/init.d/*
.Здесь мы просто берем это название для удобства, так как в контейнере Docker по умолчанию нет демона init/systemd, и
ENTRYPOINT
является pid 1. -
Оригинальное значение
ENTRYPOINT
образа можно найти в егоDockerfile
или оно может быть переопределено вcompose.yaml
. -
Установка нового
ENTRYPOINT
сбросит оригинальныйCMD
в пустую строку:Если
CMD
определен в базовом образе, установкаENTRYPOINT
сброситCMD
до пустого значения. В этом сценарииCMD
должен быть определен в текущем образе, чтобы иметь значение.так что нам нужно скопировать значение
CMD
изDockerfile
оригинального образа илиcompose.yaml
, если оно переопределено в нем. -
sh -xc 'echo $@' 1 2 3
— это способ передать аргументы оболочки вsh -c
, и этот пример выполнитecho arg
, что можно проверить с помощьюset -x
. -
dash
— это еще одна реализация оболочкиsh
, которая быстрее чемbash
и используется как по умолчанию/bin/sh
в Debian.Если вы используете какие-либо bashism функции в
/etc/rc.local
, смело заменяйте их наbash
или другие реализации оболочки. -
$@
— это значение всех аргументов оболочки, кроме первого, как$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
.
Шаги для Запуска Скрипта:
-
Модификация Dockerfile:
- Скопируйте ваш скрипт в контейнер и сделайте его исполняемым. Например:
COPY my-script.sh /etc/rc.local RUN chmod +x /etc/rc.local
- Скопируйте ваш скрипт в контейнер и сделайте его исполняемым. Например:
-
Обновление команды
ENTRYPOINT
илиCMD
:- На этапе сборки Docker-образа можно изменить команду
ENTRYPOINT
илиCMD
, чтобы она включала выполнение вашего скрипта. - Например, замените стандартный
ENTRYPOINT
следующей строкой:ENTRYPOINT ["/bin/bash", "-c", "/etc/rc.local && exec <your_original_entrypoint>"]
- На этапе сборки Docker-образа можно изменить команду
-
Использование Альтернативных Базовых Образов:
- Если ваш случай использования предполагает настройку служб и процессов, подобных традиционной ОС, рассмотрите использование базового образа, поддерживающего запуск служб, например,
phusion/baseimage-docker
.
- Если ваш случай использования предполагает настройку служб и процессов, подобных традиционной ОС, рассмотрите использование базового образа, поддерживающего запуск служб, например,
-
Команды и Хуки Docker Compose:
- Если приоритет выполнения скрипта (до или после запуска основного процесса) не критичен, можно использовать хуки
post-start
в рамках Docker Compose.
- Если приоритет выполнения скрипта (до или после запуска основного процесса) не критичен, можно использовать хуки
Дополнительные Ресурсы
Использование вышеуказанных методов позволит вам добиться требуемого запуска скриптов в контейнерах Docker, соблюдая при этом их архитектурные особенности.