Условный ENV в Dockerfile

Можно ли условно установить ENV переменная в Dockerfile, основанная на значении сборки ARG?

Пример: что-то вроде

ARG BUILDVAR=sad
ENV SOMEVAR=if $BUILDVAR -eq "SO"; then echo "hello"; else echo "world"; fi

Обновление: текущее использование на основе ответа Марио:

ARG BUILD_ENV=prod
ENV NODE_ENV=production
RUN if [ "${BUILD_ENV}" = "test" ]; then export NODE_ENV=development; fi

Тем не менее, работает с --build-arg BUILD_ENV=test а потом, зайдя на хозяина, я все равно получаю

docker run -it mycontainer bin/bash
[root@brbqw1231 /]# echo $NODE_ENV
production

8 ответов

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

FROM debian:stable
ARG BUILD_DEVELOPMENT
# if --build-arg BUILD_DEVELOPMENT=1, set NODE_ENV to 'development' or set to null otherwise.
ENV NODE_ENV=${BUILD_DEVELOPMENT:+development}
# if NODE_ENV is null, set it to 'production' (or leave as is otherwise).
ENV NODE_ENV=${NODE_ENV:-production}

Тестирование сборки:

docker build --rm -t env_prod ./
...
docker run -it env_prod bash
root@2a2c93f80ad3:/# echo $NODE_ENV 
production
root@2a2c93f80ad3:/# exit
docker build --rm -t env_dev --build-arg BUILD_DEVELOPMENT=1 ./
...
docker run -it env_dev bash
root@2db6d7931f34:/# echo $NODE_ENV
development

Вы не можете запустить bash-код в Dockerfile напрямую, но вы должны использовать RUN команда. Так, например, вы можете изменить ENV с RUN и экспортировать переменную в ifкак показано ниже:

ARG BUILDVAR=sad 
RUN if [ "$BUILDVAR" == "SO"]; \
    then export SOMEVAR=hello; \
    else export SOMEVAR=world; \
    fi 

Я не пробовал это, но должно работать.

Ваша логика на самом деле правильно. Проблема здесь в том, что RUN export ... не будет работать в Dockerfile, потому что export Команда не будет сохраняться на всех изображениях. Dockerfiles создают временный контейнер, чтобы сгенерировать для него образ, поэтому переменные среды не существуют.

ENV с другой стороны согласно документации говорится:

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

Единственный способ сделать это во время вашего docker run Команда при создании контейнера из вашего изображения, и оберните вашу логику вокруг этого:

if [ "${BUILD_ENV}" = "test" ]; then
    docker run -e NODE_ENV=development myimage
else
    docker run myimage
fi

Пока вы не можете установить условный ENV переменные, но вы можете быть в состоянии достичь того, что вы после с RUN команда и переменная окружения с нулевым слиянием:

RUN node /var/app/current/index.js --env ${BUILD_ENV:-${NODE_ENV:-"development"}}

Если мы говорим только о переменной среды, просто установите ее с помощью production

ENV NODE_ENV prod

И во время запуска контейнера в разработке вы можете использовать -e NODE_ENV=dev.

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

Этот ответ хорош, если вам нужно только проверить, присутствует ли build-arg, и вы хотите установить значение по умолчанию. Чтобы улучшить это решение, если вы хотите использовать данные, передаваемые с помощью build-arg, вы можете сделать следующее:

FROM debian:stable
ARG BUILD_DEVELOPMENT=production
ENV NODE_ENV=$BUILD_DEVELOPMENT

Магия исходит от значения по умолчанию для ARG.

У меня была аналогичная проблема с настройкой прокси-сервера в контейнере.

Решение, которое я использую, - это сценарий точки входа и еще один сценарий для настройки переменных среды. Используя RUN, вы гарантируете, что сценарий конфигурации запускается при сборке, а ENTRYPOINT - при запуске контейнера.

--build-arg используется в командной строке для установки пользователя и пароля прокси.

Скрипт точки входа выглядит так:

      #!/bin/bash
# Load the script of environment variables
. /root/configproxy.sh
# Run the main container command
exec "$@"

configproxy.sh

      #!/bin/bash

function start_config {
read u p < /root/proxy_credentials

export HTTP_PROXY=http://$u:$p@proxy.com:8080
export HTTPS_PROXY=https://$u:$p@proxy.com:8080

/bin/cat <<EOF > /etc/apt/apt.conf 
Acquire::http::proxy "http://$u:$p@proxy.com:8080";
Acquire::https::proxy "https://$u:$p@proxy.com:8080";
EOF
}

if [ -s "/root/proxy_credentials" ]
then
start_config
fi

И в Dockerfile настройте:

      # Base Image
FROM ubuntu:18.04

ARG user
ARG pass

USER root

# -z the length of STRING is zero
# [] are an alias for test command
# if $user is not empty, write credentials file
RUN if [ ! -z "$user" ]; then echo "${user} ${pass}">/root/proxy_credentials ; fi

#copy bash scripts
COPY configproxy.sh /root
COPY startup.sh .

RUN ["/bin/bash", "-c", ". /root/configproxy.sh"]

# Install dependencies and tools
#RUN apt-get update -y && \
#    apt-get install -yqq --no-install-recommends \
#    vim iputils-ping

ENTRYPOINT ["./startup.sh"]
CMD ["sh", "-c", "bash"]

Сборка без настроек прокси

      docker build -t img01 -f Dockerfile . 

Сборка с настройками прокси

      docker build -t img01 --build-arg user=<USER> --build-arg pass=<PASS> -f Dockerfile . 

Взгляните сюда.

Передача значений в Dockerfile, а затем в скрипт точки входа

В командной строке введите нужное вам значение (TARG)

docker run --env TARG=T1_WS01 -i projects/msbob

Тогда в вашем Dockerfile положить что-то вроде этого

Dockerfile:
# if $TARG is not set then "entrypoint" defaults to Q0_WS01
CMD ./entrypoint.sh ${TARG} Q0_WS01

entrypoint.sh скрипт только читает первый аргумент

entrypoint.sh:
#!/bin/bash
[ $1 ] || { echo "usage: entrypoint.sh <$TARG>" ; exit ; }
target_env=$1
Другие вопросы по тегам