Dockerfile if else Bedingung mit externen Argumenten

127

Ich habe Docker-Datei

FROM centos:7
ENV foo=42

dann baue ich es

docker build -t my_docker .

und starte es.

docker run -it -d  my_docker

Ist es möglich, Argumente über die Befehlszeile zu übergeben und mit if else in Dockerfile zu verwenden? Ich meine so etwas wie

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

und mit diesem Argument bauen.

 docker build -t my_docker . --my_arg=42
nick_gabpe
quelle
1
Dies sollte wahrscheinlich über ein Build-Skript erfolgen.
Snps
@ Зелёный das ist falsch. Siehe unten Antwort, dies kann mit --build-arg
devnul3
Die akzeptierte Antwort deckt nicht den Teil der Frage ab, in dem die Bedingung "wenn sonst" vorliegt. Es wäre besser, es in "Dockerfile mit externen Argumenten" umzubenennen, wenn die Bedingungsprüfung nicht erforderlich wäre.
Ruslan Kabalin
@ RuslanKabalin - Die akzeptierte Antwort enthält sowohl "then" - als auch "else" -Klauseln. Der einzige Unterschied besteht darin, was unter "Wenn-Bedingung" getestet wird. Für den in Frage gestellten Code : RUN if [ "$arg" == "42" ]; then ENV=TRUE; else ENV=FALSE; fi. Oder wenn arg fehlen könnte: RUN if [ "x$arg" == "x42" ]; then ...
ToolmakerSteve

Antworten:

186

Es sieht vielleicht nicht so sauber aus, aber Sie können Ihre Docker-Datei (bedingt) wie folgt haben:

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

und erstellen Sie dann das Image als:

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

oder

docker build -t my_docker .

Qasim Sarfraz
quelle
20
Sollte es nicht [ "$arg" = "x" ]statt sein [ "x$arg" = "x" ]?
Quannt
4
Der Ternär prüft hier, ob ein Argument angegeben ist, anstatt nach einem bestimmten Argument zu suchen. Es scheint offensichtlicher, wenn es umgeschrieben wird, if [ $arg != "" ] ;aber ich bin mir sicher, dass es einige Fallstricke gibt, mit denen ich nicht vertraut bin
myol
21
if [[ -n "$arg" ]]wahr, wenn ein Parameter nicht leer ist, if [[ -z "$arg" ]]wahr, wenn ein Parameter leer ist
acumartini
6
Wie schreibe ich eine mehrzeilige if-Anweisung?
Ashutosh Chamoli
12
@Quannt - nein, siehe Warum werden Shell-Skripte ausgeführt ? Es [ "x$arg" = "x" ]sieht so aus, als würden zwei Zeichenfolgen in Anführungszeichen verglichen, aber die Anführungszeichen werden entfernt. Dadurch bleibt die Syntax korrekt. Nach dem Strippen des Zitats: Gut : if x = x ... Schlecht : if = . Es gibt jedoch bessere Möglichkeiten, die Existenz eines Parameters zu überprüfen: Überprüfen Sie die Existenz von Eingabeargumenten .
ToolmakerSteve
18

Aus irgendeinem Grund haben mir die meisten Antworten hier nicht geholfen (vielleicht hängt es mit meinem FROM-Image in der Docker-Datei zusammen)

Daher habe ich es vorgezogen, bash scriptin meinem Arbeitsbereich eine Kombination mit zu erstellen --build-arg, um die if-Anweisung während der Docker-Erstellung zu verarbeiten, indem überprüft wird, ob das Argument leer ist oder nicht

Bash-Skript:

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

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

Anmerkung: Dies geht zu else (false) im Bash-Skript

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

Bemerkung: Dies geht an das if (true)

Bearbeiten 1:

Nach mehreren Versuchen habe ich folgendes gefunden Artikel und dieses ein der mir geholfen hat, zwei Dinge zu verstehen:

1) ARG vor FROM befindet sich außerhalb des Builds

2) Die Standard-Shell ist / bin / sh, was bedeutet, dass if if im Docker-Build etwas anders funktioniert. Zum Beispiel benötigen Sie nur ein "=" anstelle von "==", um Zeichenfolgen zu vergleichen.

Sie können dies also in der Dockerfile

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

und in der docker build:

docker build --pull -f "Dockerfile" --label "service_name=${SERVICE_NAME}" -t $SERVICE_NAME --build-arg argname=true .
Dsaydon
quelle
16

Es gibt eine interessante Alternative zu den vorgeschlagenen Lösungen, die mit einer einzelnen Docker-Datei funktioniert , nur einen einzigen Aufruf des Docker- Builds pro bedingtem Build erfordert und Bash vermeidet .

Lösung:

Das Folgende Dockerfilelöst dieses Problem. Kopieren Sie es und fügen Sie es selbst ein.

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}"

Erklärung der Docker-Datei:

Wir bekommen zuerst ein baseBild ( centos:7in Ihrem Fall) und stellen es auf eine eigene Bühne. Die baseBühne sollte Dinge enthalten, die Sie vor der Bedingung tun möchten. Danach haben wir zwei weitere Stufen, die die Zweige unseres Zustands darstellen: branch-version-1und branch-version-2. Wir bauen beide. Die finalBühne wählt dann eine dieser Stufen aus, basierend auf my_arg. Bedingte Docker-Datei. Los geht's.

Ausgabe beim Ausführen:

(Ich habe das ein wenig abgekürzt ...)

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

Vielen Dank an Tõnis für diese tolle Idee!

User12547645
quelle
Bisher der beste Ansatz.
Felipe Desiderati
Das habe ich auch gedacht, deshalb habe ich es hier gepostet. Lesen Sie die Nachrichten @FelipeDesiderati
User12547645
1
Dies ist die beste Lösung, die hier vorgestellt wird, da Sie keine Problemumgehungen mit Skripten / Bash verwenden müssen, sondern nur Dockerfile-Kenntnisse verwenden müssen. Gute Antwort.
Akito
Was macht "ARG ENV"? Kannst du etwas Licht ins Dunkel bringen? Vielen Dank.
Max
@ Max danke für den Hinweis. Es ist nicht notwendig
User12547645
12

Verwenden Sie dazu einfach die "Test" -Binärdatei direkt. Sie sollten auch den Befehl noop ":" verwenden, wenn Sie keine "else" -Bedingung angeben möchten, damit Docker nicht mit einem Rückgabewert ungleich Null stoppt.

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
quelle
2
Genauso wie RUN [ -z "$YOURVAR" ] && ... || :ich glaube
OneCricketeer
4
Wenn für die folgende Anweisung die Variable gesetzt ist, werden beide Echos ausgeführt. RUN test -z "$YOURVAR" || echo "var is set" && echo "var is not set"
Harshad Vyawahare
Tatsächlich. Denken Sie daran , diese sind nicht verzweigt bedingte Blöcke, sie auszuführen , seriell, setzen Sie &&vor ||:test ! -z "$YOURVAR" && echo "var is set" || echo "var is not set"
Brandt
5

Genau wie andere sagten, würde Shell-Skript helfen.

Nur ein zusätzlicher Fall, IMHO ist es erwähnenswert (für jemanden, der hier stolpert und nach einem einfacheren Fall sucht), das ist ein Ersatz für die Umgebung .

Umgebungsvariablen (mit der ENVAnweisung deklariert ) können in bestimmten Anweisungen auch als Variablen verwendet werden, die von der interpretiert werden sollen Dockerfile.

Die ${variable_name}Syntax unterstützt auch einige der unten angegebenen Standard-Bash-Modifikatoren:

  • ${variable:-word}gibt an, dass wenn gesetzt variableist, das Ergebnis dieser Wert ist. Wenn variablenicht gesetzt, wordwird das Ergebnis sein.

  • ${variable:+word}Gibt an, dass wenn variablegesetzt ist word, das Ergebnis ist, andernfalls ist das Ergebnis die leere Zeichenfolge.

Jing Li
quelle
5

Die akzeptierte Antwort kann die Frage lösen. Wenn Sie jedoch mehrzeilige ifBedingungen in der Docker-Datei wünschen, können Sie diese \am Ende jeder Zeile platzieren (ähnlich wie in einem Shell-Skript) und jeden Befehl mit beenden ;. Sie können sogar etwas wie set -euxden 1. Befehl definieren.

Beispiel:

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

In deinem Fall:

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

Dann bauen mit:

docker build -t my_docker . --build-arg arg=42
Lucas Basquerotto
quelle
4

Verwenden von Bash-Skript und 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

Passing arg: 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

Ohne Argument: 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
....
Muhammad Soliman
quelle