Dockerfile, якщо інше обумовлено зовнішніми аргументами


130

У мене є докерфайл

FROM centos:7
ENV foo=42

то я будую його

docker build -t my_docker .

і запустити його.

docker run -it -d  my_docker

Чи можна передавати аргументи з командного рядка і використовувати їх, якщо інше в Dockerfile? Я маю на увазі щось подібне

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

і будувати з цим аргументом.

 docker build -t my_docker . --my_arg=42

1
Це, мабуть, має вирішуватися зі сценарію збірки.
Snps

@ Зелёный, що невірно. Дивіться нижче відповідь, це можна зробити за допомогою --build-arg
devnul3

Прийнята відповідь не охоплює частину питання "якщо це умова". Було б краще перейменувати його на "Dockerfile із зовнішніми аргументами", якщо перевірка стану не означала б вимоги.
Руслан Кабалін

@RuslanKabalin - прийнята відповідь має як "тоді", так і "else". Єдина відмінність - те, що тестується в умовах "якщо умови". Для коду показаний на питання: RUN if [ "$arg" == "42" ]; then ENV=TRUE; else ENV=FALSE; fi. Або якщо аргумент може бути відсутнім: RUN if [ "x$arg" == "x42" ]; then ...
ToolmakerSteve

Відповіді:


192

Це може виглядати не так чисто, але ви можете мати свій 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" ]?
Куант

4
Потрійний тут перевіряє, чи подано якийсь аргумент, а не перевірку конкретного аргументу. Здавалося б , більш очевидним , якщо переписати так , if [ $arg != "" ] ;але я впевнений , є деякі Гоча я не знайомий з
myol

21
if [[ -n "$arg" ]]правда, якщо парам не порожній, if [[ -z "$arg" ]]правда, якщо парам порожній
acumartini

6
Як написати багаторядковий, якщо заява?
Ашутош Шамолі

12
@Quannt - ні, див. Чому сценарії оболонки ... [ "x$arg" = "x" ] схоже, вони порівнюють дві цитовані рядки, але цитати позбавляються; Це підтримує правильність синтаксису. Після зняття цитат: Добре : if x = x ... Погано: if = . ЗАРАЗ, Є кращі способи перевірити наявність параметра: Перевірте наявність вхідних аргументів .
ToolmakerSteve

20

Чомусь більшість відповідей тут мені не допомогли (можливо, це пов’язано з моїм зображенням ВІД в Dockerfile)

Тому я вважав за краще створити bash scriptу своїй робочій області в поєднанні з --build-argтим, щоб обробляти, якщо оператор, поки Docker будує, перевіряючи, чи аргумент порожній чи ні

Баш сценарій:

#!/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

Докерфайл:

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

Докер збірка:

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

Зауваження: Це перейде до іншого (false) в скрипті bash

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

Зауваження: це перейде до параметра if (true)

Редагувати 1:

Після декількох спроб я знайшов наступну статтю , і цей один , який допоміг мені зрозуміти 2 речі:

1) ARG перед FROM знаходиться поза складанням

2) Оболонка за замовчуванням - / bin / sh, що означає, що if if працює трохи іншим чином у складанні докера. наприклад, вам потрібно лише один "=" замість "==" для порівняння рядків.

Таким чином, ви можете зробити це всередині 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 .


16

Існує цікава альтернатива запропонованим рішенням, яка працює з одним Dockerfile , вимагає лише одного виклику для складання докера за умовну збірку і уникає 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. Умовний докерфайл. Ось так.

Вихід при виконанні:

(Я це трохи скоротив ...)

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

Дякую Tõnis за цю дивовижну ідею!


Поки що найкращий підхід.
Феліпе Десідерати

Думав також, ось чому я і розмістив це тут. Повідомте новини @FelipeDesiderati
User12547645

1
Це найкраще рішення з усіх представлених тут, тому що вам не потрібно використовувати обхідні шляхи зі скриптами / 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" && :

2
Те саме RUN [ -z "$YOURVAR" ] && ... || :, що я вважаю
OneCricketeer

4
Для наступного твердження, якщо встановлено var, виконується обидва відлуння. 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

Як саме розповідали інші, сценарій оболонки допоможе.

Просто додатковий випадок, IMHO, який варто згадати (для когось іншого, хто натрапив на це, шукаючи більш простий випадок), це заміна середовища .

Змінні середовища (задекларовані разом із ENVтвердженням) також можуть використовуватися в певних інструкціях як змінні для інтерпретації Dockerfile.

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

  • ${variable:-word}вказує, що якщо variableвстановлено, результатом буде це значення. Якщо variableне встановлено word, результат буде результатом.

  • ${variable:+word}вказує, що якщо variableвстановлено значення, тоді wordбуде результатом, інакше результатом буде порожній рядок.


5

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

Приклад:

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

Докерфайл

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
....
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.