Dockerfile if else condizione con argomenti esterni


131

Ho dockerfile

FROM centos:7
ENV foo=42

poi lo costruisco

docker build -t my_docker .

ed eseguilo.

docker run -it -d  my_docker

È possibile passare argomenti dalla riga di comando e usarlo con if else in Dockerfile? Intendo qualcosa di simile

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

e costruire con questo argomento.

 docker build -t my_docker . --my_arg=42

1
Questo dovrebbe probabilmente essere gestito da uno script di compilazione.
Snps

@ Зелёный che non è corretto. Vedi sotto la risposta, questo può essere ottenuto con --build-arg
devnul3

La risposta accettata non copre la parte "se altrimenti condizione" della domanda. Sarebbe meglio rinominarlo in "Dockerfile con argomenti esterni" se il controllo delle condizioni non fosse un requisito.
Ruslan Kabalin

@RuslanKabalin - la risposta accettata ha entrambe le clausole "then" e "else". L'unica differenza è ciò che viene testato in "if condition". Per il codice mostrato in questione: RUN if [ "$arg" == "42" ]; then ENV=TRUE; else ENV=FALSE; fi. O se arg potrebbe mancare: RUN if [ "x$arg" == "x42" ]; then ...
ToolmakerSteve

Risposte:


193

Potrebbe non sembrare così pulito ma puoi avere il tuo Dockerfile (condizionale) come segue:

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

e quindi crea l'immagine come:

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

o

docker build -t my_docker .


20
Non dovrebbe essere [ "$arg" = "x" ]invece di [ "x$arg" = "x" ]?
Quannt

4
Il ternario qui controlla se viene fornito un argomento, piuttosto che cercare un argomento specifico. Sembrerebbe più ovvio se riscritto, if [ $arg != "" ] ;ma sono sicuro che ci sia qualche trucco che non conosco
myol

21
if [[ -n "$arg" ]]true se un parametro non è vuoto, if [[ -z "$arg" ]]vero se un parametro è vuoto
acumartini

6
Come scrivere un'istruzione if su più righe?
Ashutosh Chamoli

12
@Quannt - no, vedi Perché gli script di shell ... [ "x$arg" = "x" ] sembra che stiano confrontando due stringhe tra virgolette, ma le virgolette vengono rimosse; Ciò mantiene la sintassi corretta. Dopo quote-strippaggio: Buono: if x = x ... Bad: if = . TUTTAVIA, ci sono modi migliori per verificare l'esistenza di un parametro: Controllare l'esistenza di argomenti di input .
ToolmakerSteve

20

Per qualche motivo la maggior parte delle risposte qui non mi ha aiutato (forse è correlato alla mia immagine FROM nel Dockerfile)

Quindi ho preferito creare un bash scriptnel mio spazio di lavoro combinato con --build-argper gestire l'istruzione if mentre Docker creava controllando se l'argomento è vuoto o meno

Script 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
....

Build Docker:

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

Nota: questo andrà a else (false) nello script bash

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

Nota: questo andrà al if (true)

Modifica 1:

Dopo diversi tentativi ho trovato il seguente articolo e questo uno che mi ha aiutato a capire 2 cose:

1) ARG prima di FROM è al di fuori della build

2) La shell predefinita è / bin / sh, il che significa che if else funziona in modo leggermente diverso nella build docker. ad esempio è necessario un solo "=" invece di "==" per confrontare le stringhe.

Quindi puoi farlo all'interno del file Dockerfile

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

e in docker build:

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


17

Esiste un'interessante alternativa alle soluzioni proposte, che funziona con un singolo Dockerfile , richiede solo una singola chiamata a docker build per build condizionale ed evita bash .

Soluzione:

Quanto segue Dockerfilerisolve il problema. Copia-incolla e prova tu stesso.

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

Spiegazione di Dockerfile:

Per prima cosa otteniamo baseun'immagine ( centos:7nel tuo caso) e la inseriamo nella sua fase. Lo basestage dovrebbe contenere le cose che vuoi fare prima della condizione. Dopodiché, abbiamo altre due fasi, che rappresentano i rami della nostra condizione: branch-version-1e branch-version-2. Li costruiamo entrambi. La finalfase che sceglie una di queste fasi, basata su my_arg. Dockerfile condizionale. Ecco qua.

Uscita durante l'esecuzione:

(L'ho abbreviato un po '...)

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

Grazie a Tõnis per questa fantastica idea!


Finora, l'approccio migliore.
Felipe Desiderati

Lo pensavo anch'io, ecco perché l'ho pubblicato qui. Spead the news @FelipeDesiderati
User12547645

1
Questa è la migliore soluzione tra tutte presentate qui, perché non è necessario utilizzare soluzioni alternative con scripts / bash, ma utilizzare un modo che coinvolga solo la conoscenza di Dockerfile. Bella risposta.
Akito

Cosa fa "ARG ENV"? puoi far luce su questo? Grazie.
Max

@ Max grazie per averlo fatto notare. Non è necessario
Utente12547645

12

Basta usare direttamente il binario "test" per farlo. Dovresti anche usare il comando noop ":" se non vuoi specificare una condizione "else", così docker non si ferma con un errore di valore di ritorno diverso da zero.

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
Lo stesso di RUN [ -z "$YOURVAR" ] && ... || :, credo
OneCricketeer

4
Per la seguente istruzione, se var è impostato, vengono eseguiti entrambi gli echo. RUN test -z "$YOURVAR" || echo "var is set" && echo "var is not set"
Harshad Vyawahare

Infatti. Ricorda che questi non sono blocchi condizionali ramificati, vengono eseguiti in serie, messi &&davanti a ||:test ! -z "$YOURVAR" && echo "var is set" || echo "var is not set"
Brandt

5

Esattamente come altri hanno detto, lo script di shell sarebbe stato d'aiuto.

Solo un caso aggiuntivo, IMHO vale la pena menzionare (per qualcun altro che si imbatte qui, alla ricerca di un caso più semplice), che è la sostituzione dell'ambiente .

Le variabili d'ambiente (dichiarate con l' ENVistruzione) possono anche essere utilizzate in alcune istruzioni come variabili da interpretare dal Dockerfile.

La ${variable_name}sintassi supporta anche alcuni dei modificatori bash standard come specificato di seguito:

  • ${variable:-word}indica che se variableè impostato, il risultato sarà quel valore. Se variablenon è impostato, wordsarà il risultato.

  • ${variable:+word}indica che se variableè impostato allora wordsarà il risultato, altrimenti il ​​risultato è una stringa vuota.


5

La risposta accettata può risolvere la domanda, ma se desideri ifcondizioni su più righe nel dockerfile, puoi farlo posizionando \alla fine di ogni riga (simile a come faresti in uno script di shell) e terminando ogni comando con ;. Puoi anche definire qualcosa come set -euxil primo comando.

Esempio:

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

Nel tuo caso:

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

Quindi crea con:

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

4

Utilizzando lo script Bash e 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

Passaggio 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

Senza arg: 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
....
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.