Dockerfile if else mengondisikan dengan argumen eksternal

131

Saya memiliki dockerfile

FROM centos:7
ENV foo=42

lalu saya membangunnya

docker build -t my_docker .

dan jalankan.

docker run -it -d  my_docker

Apakah mungkin untuk meneruskan argumen dari baris perintah dan menggunakannya dengan if else di Dockerfile? Maksudku seperti itu

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

dan membangun dengan argumen ini.

 docker build -t my_docker . --my_arg=42
nick_gabpe
sumber
1
Ini mungkin harus ditangani dari skrip build.
Snps
@ Зелёный itu salah. Lihat jawaban di bawah, ini dapat dicapai dengan --build-arg
devnul3
Jawaban yang diterima tidak mencakup bagian "jika kondisi lain" dari pertanyaan. Akan lebih baik untuk mengganti namanya menjadi "Dockerfile dengan argumen eksternal" jika pemeriksaan kondisi tidak berarti menjadi persyaratan.
Ruslan Kabalin
@RuslanKabalin - jawaban yang diterima memiliki klausa "lalu" dan "lain". Satu-satunya perbedaan adalah apa yang diuji dalam "kondisi jika". Untuk kode yang ditunjukkan pada pertanyaan: RUN if [ "$arg" == "42" ]; then ENV=TRUE; else ENV=FALSE; fi. Atau jika arg mungkin hilang: RUN if [ "x$arg" == "x42" ]; then ...
ToolmakerSteve

Jawaban:

193

Ini mungkin tidak terlihat bersih tetapi Anda dapat memiliki Dockerfile (bersyarat) sebagai berikut:

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

dan kemudian buat gambar sebagai:

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

atau

docker build -t my_docker .

Qasim Sarfraz
sumber
20
Bukankah seharusnya [ "$arg" = "x" ]bukan [ "x$arg" = "x" ]?
Quannt
4
Terner di sini memeriksa apakah ada argumen yang diberikan, daripada memeriksa argumen tertentu. Akan tampak lebih jelas jika ditulis ulang if [ $arg != "" ] ;tetapi saya yakin ada beberapa gotcha yang tidak saya kenal
myol
21
if [[ -n "$arg" ]]benar jika parameter tidak kosong, if [[ -z "$arg" ]]benar jika parameter kosong
acumartini
6
Bagaimana cara menulis pernyataan if multiline?
Ashutosh Chamoli
12
@Quannt - tidak, lihat Mengapa skrip shell ... [ "x$arg" = "x" ] terlihat seperti membandingkan dua string yang dikutip, tetapi tanda kutipnya dihapus ; Ini membuat sintaks tetap benar. Setelah kutipan-pengupasan: Baik: if x = x ... Bad: if = . NAMUN, ada cara yang lebih baik untuk memeriksa keberadaan parameter: Periksa keberadaan argumen input .
ToolmakerSteve
20

Dari beberapa alasan sebagian besar jawaban di sini tidak membantu saya (mungkin ini terkait dengan gambar FROM saya di Dockerfile)

Jadi saya lebih suka membuat bash scriptdi ruang kerja saya dikombinasikan dengan --build-arguntuk menangani pernyataan if sementara Docker membangun dengan memeriksa apakah argumennya kosong atau tidak

Skrip 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 Build:

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

Catatan: Ini akan menuju ke else (false) di skrip bash

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

Catatan: Ini akan pergi ke if (true)

Edit 1:

Setelah beberapa kali saya telah menemukan berikut artikel dan ini salah satu yang membantu saya untuk memahami 2 hal:

1) ARG before FROM berada di luar build

2) Shell default adalah / bin / sh yang berarti if else berfungsi sedikit berbeda dalam build buruh pelabuhan. misalnya Anda hanya perlu satu "=" daripada "==" untuk membandingkan string.

Jadi, Anda dapat melakukan ini di dalam Dockerfile

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

dan di docker build:

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

Ada alternatif menarik untuk solusi yang diusulkan, yang berfungsi dengan satu Dockerfile , hanya memerlukan satu panggilan ke build docker per build bersyarat dan menghindari bash .

Larutan:

Berikut ini Dockerfilememecahkan masalah itu. Salin-tempel dan coba sendiri.

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

Penjelasan Dockerfile:

Kami pertama kali mendapatkan basegambar ( centos:7dalam kasus Anda) dan memasukkannya ke dalam panggungnya sendiri. The basepanggung harus berisi hal-hal yang ingin Anda lakukan sebelum kondisi tersebut. Setelah itu, kami memiliki dua tahap lagi, mewakili cabang dari kondisi kami: branch-version-1dan branch-version-2. Kami membangun keduanya. The finalpanggung dari memilih salah satu dari tahap ini, berdasarkanmy_arg . Dockerfile Bersyarat. Ini dia.

Output saat berjalan:

(Saya menyingkat ini sedikit ...)

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

Terima kasih kepada Tõnis untuk ide luar biasa ini!

Pengguna12547645
sumber
Sejauh ini, pendekatan terbaik.
Felipe Desiderati
Berpikir begitu juga, itulah mengapa saya mempostingnya di sini. Spead berita @FelipeDesiderati
Pengguna12547645
1
Ini adalah solusi terbaik dari semua yang disajikan di sini, karena Anda tidak perlu menggunakan solusi dengan skrip / bash, tetapi gunakan cara yang hanya melibatkan pengetahuan Dockerfile. Jawaban yang bagus.
Akito
Apa yang dilakukan "ARG ENV"? dapatkah Anda menjelaskan hal ini? Terima kasih.
Maks
@ Max, terima kasih telah menunjukkan hal itu. Tidak perlu
User12547645
12

Cukup gunakan biner "test" secara langsung untuk melakukan ini. Anda juga harus menggunakan perintah noop ":" jika Anda tidak ingin menentukan kondisi "lain", jadi buruh pelabuhan tidak berhenti dengan kesalahan nilai pengembalian bukan nol.

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
sumber
2
Sama seperti RUN [ -z "$YOURVAR" ] && ... || :, saya percaya
OneCricketeer
4
Untuk pernyataan berikut, jika var diset maka kedua echo akan dijalankan. RUN test -z "$YOURVAR" || echo "var is set" && echo "var is not set"
Harshad Vyawahare
Memang. Ingat itu bukan blok bersyarat bercabang, mereka mengeksekusi secara serial, diletakkan &&di depan ||:test ! -z "$YOURVAR" && echo "var is set" || echo "var is not set"
Brandt
5

Persis seperti yang dikatakan orang lain, skrip shell akan membantu.

Hanya kasus tambahan, IMHO itu layak disebut (untuk orang lain yang tersandung di sini, mencari kasus yang lebih mudah), yaitu pengganti Lingkungan .

Variabel lingkungan (dideklarasikan dengan ENVpernyataan) juga dapat digunakan dalam instruksi tertentu sebagai variabel yang akan diinterpretasikan olehDockerfile .

The ${variable_name}sintaks juga mendukung beberapa pengubah pesta standar yang ditentukan di bawah ini:

  • ${variable:-word}menunjukkan bahwa jika variablediset maka hasilnya adalah nilai tersebut. Jika variabletidak diset maka wordakan menjadi hasilnya.

  • ${variable:+word}menunjukkan bahwa jika variablediset maka wordakan menjadi hasil, jika tidak hasilnya adalah string kosong.

Jing Li
sumber
5

The jawaban yang diterima dapat memecahkan pertanyaan, tetapi jika Anda ingin multiline ifkondisi di dockerfile, Anda dapat melakukan penempatan yang \pada akhir setiap baris (mirip dengan bagaimana Anda akan melakukannya dalam shell script) dan berakhir setiap perintah dengan ;. Anda bahkan dapat mendefinisikan sesuatu sepertiset -eux perintah pertama.

Contoh:

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

Dalam kasus Anda:

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

Kemudian bangun dengan:

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

Menggunakan script Bash dan 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

Mengoper 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

Tanpa 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
....
Muhammad Soliman
sumber