У меня есть следующий файл для моего проекта nodejs

FROM node:boron

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install



# Bundle app source
COPY . /usr/src/app

# Replace with env variable
RUN envsubs < fil1 > file2

EXPOSE 8080
CMD [ "npm", "start" ]

Я запускаю контейнер Docker с флагом -e, предоставляя переменную окружения

Но я не вижу замены. Будет ли выполнена команда Run, когда будет доступна переменная env?

35
user_mda 6 Янв 2017 в 21:44

3 ответа

Лучший ответ

Изображения неизменны

Dockerfile определяет процесс сборки образа. После создания изображение является неизменным (не может быть изменено). Переменные времени выполнения не являются чем-то, что запекается в это неизменное изображение. Так что Dockerfile - это не то место, где можно решить эту проблему.

Использование сценария точки входа

Вероятно, вы захотите переопределить стандартный ENTRYPOINT своим собственным сценарием и заставить этот сценарий что-то делать с переменными среды. Поскольку сценарий точки входа будет выполняться во время выполнения (при запуске контейнера), сейчас самое время собирать переменные среды и что-то с ними делать.

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

< Сильный > Dockerfile :

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
CMD ["npm", "start"]

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

< Сильный > entrypoint.sh :

#!/bin/sh

# Where $ENVSUBS is whatever command you are looking to run
$ENVSUBS < fil1 > file2

npm install

# This will exec the CMD from your Dockerfile, i.e. "npm start"
exec "$@"

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

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

Использование переменных среды во время выполнения

Сценарий точки входа знает, как использовать переменную среды, но вы все равно должны указать Docker импортировать переменную во время выполнения. Вы можете использовать флаг -e для docker run.

docker run -e "ENVSUBS=$ENVSUBS" <image_name>

Здесь Docker предписывается определить переменную среды ENVSUBS, а назначенное ей значение является значением $ENVSUBS из текущей среды оболочки.

Как работают сценарии входа

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

Когда Docker запускает контейнер, он выполняет одну (и только одну) команду внутри контейнера. Эта команда становится PID 1, как init или systemd в типичной системе Linux. Этот процесс отвечает за запуск любых других процессов, которые должен иметь контейнер.

По умолчанию ENTRYPOINT это /bin/sh -c. Вы можете переопределить его в Dockerfile, или docker-compose.yml, или используя команду docker.

Когда контейнер запускается, Docker запускает команду точки входа и передает ей команду (CMD) в виде списка аргументов. Ранее мы определили наш собственный ENTRYPOINT как /entrypoint.sh. Это означает, что в вашем случае Docker будет выполнять в контейнере при запуске:

/entrypoint.sh npm start

Поскольку ["npm", "start"] был определен как команда, это то, что передается в виде списка аргументов сценарию точки входа.

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

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

exec npm start

И поскольку exec запускает свои аргументы как команду, заменяя текущего процесса на себя, когда вы закончите, npm start становится PID 1 в вашем контейнере.

Почему вы не можете использовать несколько команд

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

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

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

Как упоминалось ранее, точка входа - это то, что действительно выполняется, и ей передается CMD в виде списка аргументов. Причина, по которой они являются отдельными, отчасти историческая. В ранних версиях Docker CMD был единственным доступным вариантом, а ENTRYPOINT был исправлен как /bin/sh -c. Но из-за подобных ситуаций Docker в конечном итоге разрешил пользователю определять ENTRYPOINT.

46
Dan Lowe 8 Янв 2017 в 00:43

Будет ли выполнена команда Run, когда будет доступна переменная env?

Переменные среды, установленные с флагом -e, устанавливаются, когда вы run контейнер.

Проблема в том, что Dockerfile читается в контейнере build, поэтому команда RUN не узнает об этих переменных среды.

Чтобы установить переменные окружения при сборке, добавьте в свой Dockerfile строку ENV. (https://docs.docker.com/engine/reference/builder / # / среда - замена )

Таким образом, ваш Dockerfile может быть:

FROM node:latest

WORKDIR /src
ADD package.json .

ENV A YOLO

RUN echo "$A"

И вывод:

$ docker build .
Sending build context to Docker daemon  2.56 kB
Step 1 : FROM node:latest
 ---> f5eca816b45d
Step 2 : WORKDIR /src
 ---> Using cache
 ---> 4ede3b23756d
Step 3 : ADD package.json .
 ---> Using cache
 ---> a4671a30bfe4
Step 4 : ENV A YOLO
 ---> Running in 7c325474af3c
 ---> eeefe2c8bc47
Removing intermediate container 7c325474af3c
Step 5 : RUN echo "$A"
 ---> Running in 35e0d85d8ce2
YOLO
 ---> 78d5df7d2322

Вы видите в последней строке, когда команда RUN запущена, контейнер знает, что переменная envrionment установлена.

3
Jacques Cornat 6 Янв 2017 в 19:20

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

FROM ubuntu
COPY init.sh /root/init.sh
RUN echo 'a=(${BEFORE_SHELL//:/ }); for c in ${a[@]}; do source $x; done' >> ~/.bashrc

И если вы хотите создать сценарий при входе в контейнер, вы передаете его путь в переменной среды BEFORE_SHELL. Пример использования docker-compose:

version: '3'
services:
  shell:
    build:
      context: .
    environment:
      BEFORE_SHELL: '/root/init.sh'

Некоторые замечания:

  • Если BEFORE_SHELL не установлен, то ничего не происходит (у нас есть поведение по умолчанию)
  • Вы можете передать любой путь скрипта, доступный в контейнере, включая смонтированные
  • Сценарии получены, поэтому переменные, определенные в сценариях, будут доступны в контейнере.
  • Можно передать несколько сценариев (используйте : для разделения путей)
1
piarston 2 Апр 2020 в 19:24