Dockerfile if else условие с внешними аргументами

132

У меня есть dockerfile

FROM centos:7
ENV foo=42

тогда я построю это

docker build -t my_docker .

и запустите его.

docker run -it -d  my_docker

Можно ли передавать аргументы из командной строки и использовать их с if else в Dockerfile? Я имею в виду что-то вроде

FROM centos:7
if (my_arg==42)
     {ENV=TRUE}
else:
     {ENV=FALSE}

и строить с этим аргументом.

 docker build -t my_docker . --my_arg=42
nick_gabpe
источник
1
Вероятно, это следует сделать из сценария сборки.
Snps
@ Зелёный, что неверно. См. Ответ ниже, это можно сделать с помощью --build-arg
devnul3
Принятый ответ не распространяется на часть вопроса "если еще условие". Было бы лучше переименовать его в «Dockerfile с внешними аргументами», если проверка условий не была обязательной.
Руслан Кабалин
@RuslanKabalin - в принятом ответе есть предложения then и else. Единственная разница - это то, что проверяется в «условии если». Для кода показан на вопрос: RUN if [ "$arg" == "42" ]; then ENV=TRUE; else ENV=FALSE; fi. Или если аргумент может отсутствовать: RUN if [ "x$arg" == "x42" ]; then ...
ToolmakerSteve

Ответы:

193

Это может выглядеть не так чисто, но у вас может быть свой Dockerfile (условный) следующим образом:

FROM centos:7
ARG arg
RUN if [ "x$arg" = "x" ] ; then echo Argument not provided ; else echo Argument is $arg ; fi

а затем создайте образ как:

docker build -t my_docker . --build-arg arg=45

или

docker build -t my_docker .

Касим Сарфраз
источник
20
Разве не должно быть [ "$arg" = "x" ]вместо [ "x$arg" = "x" ]?
Quannt 07
4
Тернар здесь проверяет, предоставлен ли какой-либо аргумент, а не проверяет конкретный аргумент. Это казалось бы более очевидным, если бы его переписали так, if [ $arg != "" ] ;но я уверен, что есть кое-что, с чем я не знаком
myol
21
if [[ -n "$arg" ]]истина, если параметр не пуст, if [[ -z "$arg" ]]истина, если параметр пуст
acumartini
6
Как написать многострочную инструкцию if?
Ашутош Чамоли
12
@Quannt - нет, см. Почему сценарии оболочки ... [ "x$arg" = "x" ] похоже, что сравниваются две строки в кавычках, но кавычки удаляются; Это сохраняет правильный синтаксис. После цитаты зачистки: Хорошо: if x = x ... Плохо: if = . ОДНАКО, ЕСТЬ лучшие способы проверить наличие параметра: Проверить наличие входных аргументов .
ToolmakerSteve
21

По какой-то причине большинство ответов здесь мне не помогли (возможно, это связано с моим изображением FROM в Dockerfile)

Поэтому я предпочел создать bash scriptв своей рабочей области в сочетании с --build-arg, чтобы обрабатывать оператор if во время сборки Docker, проверяя, является ли аргумент пустым или нет

Сценарий Bash:

#!/bin/bash -x

if test -z $1 ; then 
    echo "The arg is empty"
    ....do something....
else 
    echo "The arg is not empty: $1"
    ....do something else....
fi

Dockerfile:

FROM ...
....
ARG arg
COPY bash.sh /tmp/  
RUN chmod u+x /tmp/bash.sh && /tmp/bash.sh $arg
....

Сборка Docker:

docker build --pull -f "Dockerfile" -t $SERVICE_NAME --build-arg arg="yes" .

Примечание: это перейдет к else (false) в сценарии bash

docker build --pull -f "Dockerfile" -t $SERVICE_NAME .

Замечание: это перейдет к if (true)

Изменить 1:

После нескольких попыток я нашел следующую статью , и этот один , который помог мне понять 2 вещи:

1) ARG перед FROM находится вне сборки

2) Оболочка по умолчанию - / bin / sh, что означает, что if else работает немного по-другому в сборке докеров. например, для сравнения строк вам нужен только один "=" вместо "==".

Так что вы можете сделать это внутри Dockerfile

ARG argname=false   #default argument when not provided in the --build-arg
RUN if [ "$argname" = "false" ] ; then echo 'false'; else echo 'true'; fi

и в docker build:

docker build --pull -f "Dockerfile" --label "service_name=${SERVICE_NAME}" -t $SERVICE_NAME --build-arg argname=true .
Дсайдон
источник
17

Существует интересная альтернатива предлагаемым решениям, которая работает с одним Dockerfile , требует только одного вызова сборки Docker для каждой условной сборки и позволяет избежать bash .

Решение:

Следующее Dockerfileрешает эту проблему. Скопируйте и вставьте его и попробуйте сами.

ARG my_arg

FROM centos:7 AS base
RUN echo "do stuff with the centos image"

FROM base AS branch-version-1
RUN echo "this is the stage that sets VAR=TRUE"
ENV VAR=TRUE

FROM base AS branch-version-2
RUN echo "this is the stage that sets VAR=FALSE"
ENV VAR=FALSE

FROM branch-version-${my_arg} AS final
RUN echo "VAR is equal to ${VAR}"

Объяснение Dockerfile:

Сначала мы получаем baseизображение ( centos:7в вашем случае) и помещаем его в отдельную сцену. baseСтадия должна содержать вещи , которые вы хотите сделать , прежде чем условия. После этого у нас есть еще два этапа, представляющие ветви нашего условия: branch-version-1и branch-version-2. Мы строим их обоих. finalЭтап , чем выбирает один из этих этапов, на основе my_arg. Условный Dockerfile. Вот так.

Выход при работе:

(Я немного сократил это ...)

my_arg==2

docker build --build-arg my_arg=2 .
Step 1/12 : ARG my_arg
Step 2/12 : ARG ENV
Step 3/12 : FROM centos:7 AS base
Step 4/12 : RUN echo "do stuff with the centos image"
do stuff with the centos image
Step 5/12 : FROM base AS branch-version-1
Step 6/12 : RUN echo "this is the stage that sets VAR=TRUE"
this is the stage that sets VAR=TRUE
Step 7/12 : ENV VAR=TRUE
Step 8/12 : FROM base AS branch-version-2
Step 9/12 : RUN echo "this is the stage that sets VAR=FALSE"
this is the stage that sets VAR=FALSE
Step 10/12 : ENV VAR=FALSE
Step 11/12 : FROM branch-version-${my_arg}
Step 12/12 : RUN echo "VAR is equal to ${VAR}"
VAR is equal to FALSE

my_var==1

docker build --build-arg my_arg=1 .
...
Step 11/12 : FROM branch-version-${my_arg}
Step 12/12 : RUN echo "VAR is equal to ${VAR}"
VAR is equal to TRUE

Спасибо Тынису за эту замечательную идею!

User12547645
источник
Пока что лучший подход.
Фелипе Дезидерати
Я тоже так думал, поэтому и разместил здесь. Расскажите о новостях @FelipeDesiderati
User12547645
1
Это лучшее решение из всех представленных здесь, потому что вам не нужно использовать обходные пути с помощью scripts / bash, но используйте способ, который включает только знания Dockerfile. Отличный ответ.
Акито
Что делает «ARG ENV»? Вы можете пролить свет на это? Спасибо.
Макс
@Max спасибо, что указали на это. Не надо
User12547645
12

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

RUN test -z "$YOURVAR" || echo "var is set" && echo "var is not set"
RUN test -z "$YOURVAR" && echo "var is not set" || :
RUN test -z "$YOURVAR" || echo "var is set" && :
Benjhess
источник
2
Так же, как RUN [ -z "$YOURVAR" ] && ... || :я полагаю
OneCricketeer
4
Для следующего оператора, если переменная установлена, выполняются оба эха. RUN test -z "$YOURVAR" || echo "var is set" && echo "var is not set"
Харшад Вьявахаре
На самом деле. Помните, что это не разветвленные условные блоки, они выполняются последовательно, &&перед ||:test ! -z "$YOURVAR" && echo "var is set" || echo "var is not set"
Брандт
5

Точно, как говорили другие, сценарий оболочки поможет.

Просто дополнительный случай, ИМХО, стоит упомянуть (для кого-то, кто наткнется здесь, ищет более простой случай), то есть замена среды .

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

${variable_name}Синтаксис также поддерживает несколько стандартных модификаторы Баша , как указано ниже:

  • ${variable:-word}указывает, что если variableустановлено, то результатом будет это значение. Если variableне установлен, то wordбудет результат.

  • ${variable:+word}указывает, что если variableустановлено, то wordбудет результат, иначе результат будет пустой строкой.

Цзин Ли
источник
5

Общепринятый ответ может решить этот вопрос, но если вы хотите многострочные ifусловия в dockerfile, вы можете сделать это размещение \в конце каждой строки (подобно тому , как вы могли бы сделать в сценарии оболочки) и заканчивая каждую команду ;. Вы даже можете определить что-то вроде set -euxкак 1-ю команду.

Пример:

RUN set -eux; \
  if [ -f /path/to/file ]; then \
    mv /path/to/file /dest; \
  fi; \
  if [ -d /path/to/dir ]; then \
    mv /path/to/dir /dest; \
  fi

В твоем случае:

FROM centos:7
ARG arg
RUN if [ -z "$arg" ] ; then \
    echo Argument not provided; \
  else \
    echo Argument is $arg; \
  fi

Затем создайте с помощью:

docker build -t my_docker . --build-arg arg=42
Лукас Баскеротто
источник
4

Использование сценария Bash и Alpine / Centos

Dockerfile

FROM alpine  #just change this to centos 

ARG MYARG=""
ENV E_MYARG=$MYARG

ADD . /tmp
RUN chmod +x /tmp/script.sh && /tmp/script.sh

script.sh

#!/usr/bin/env sh

if [ -z "$E_MYARG" ]; then
    echo "NO PARAM PASSED"
else
    echo $E_MYARG
fi

Передача аргумента: docker build -t test --build-arg MYARG="this is a test" .

....
Step 5/5 : RUN chmod +x /tmp/script.sh && /tmp/script.sh
 ---> Running in 10b0e07e33fc
this is a test
Removing intermediate container 10b0e07e33fc
 ---> f6f085ffb284
Successfully built f6f085ffb284

Без аргумента: docker build -t test .

....
Step 5/5 : RUN chmod +x /tmp/script.sh && /tmp/script.sh
 ---> Running in b89210b0cac0
NO PARAM PASSED
Removing intermediate container b89210b0cac0
....
Мухаммад Солиман
источник