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

1
สิ่งนี้ควรได้รับการจัดการจากสคริปต์สร้าง
Snps

@ Зелёныйที่ไม่ถูกต้อง ดูคำตอบด้านล่างนี้สามารถทำได้ด้วย --build-arg
devnul3

คำตอบที่ยอมรับไม่ครอบคลุมส่วน "if else condition" ของคำถาม จะเป็นการดีกว่าหากเปลี่ยนชื่อเป็น "Dockerfile พร้อมอาร์กิวเมนต์ภายนอก" หากการตรวจสอบเงื่อนไขไม่ได้หมายความว่าเป็นข้อกำหนด
Ruslan Kabalin

@RuslanKabalin - คำตอบที่ยอมรับมีทั้งประโยค "then" และ "else" ข้อแตกต่างเพียงอย่างเดียวคือสิ่งที่ทดสอบใน "if condition" สำหรับรหัสที่แสดงในคำถาม: 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

4
ternary ในที่นี้กำลังตรวจสอบว่ามีการระบุอาร์กิวเมนต์หรือไม่แทนที่จะตรวจสอบอาร์กิวเมนต์เฉพาะ มันจะดูชัดเจนกว่านี้ถ้าเขียนใหม่if [ $arg != "" ] ;แต่ฉันแน่ใจว่ามี gotcha อยู่บ้างที่ฉันไม่คุ้นเคย
myol

21
if [[ -n "$arg" ]]จริงถ้าพารามิเตอร์ไม่ว่างเปล่าif [[ -z "$arg" ]]จริงถ้าพารามิเตอร์ว่างเปล่า
acumartini

6
จะเขียนคำสั่ง multiline if ได้อย่างไร?
Ashutosh Chamoli

12
@Quannt - ไม่ดูทำไมเชลล์สคริปต์ ... [ "x$arg" = "x" ]ดูเหมือนว่ากำลังเปรียบเทียบสองสตริงที่ยกมา แต่คำพูดถูกตัดออก สิ่งนี้ช่วยให้ไวยากรณ์ถูกต้อง หลังจากตัดใบเสนอราคา: ดี: if x = x ... แย่: if = . แต่มีวิธีที่ดีกว่าการตรวจสอบสำหรับการดำรงอยู่ของพารามิเตอร์: ตรวจสอบการดำรงอยู่ของการขัดแย้งการป้อนข้อมูล
ToolmakerSteve

21

จากเหตุผลบางประการคำตอบส่วนใหญ่ที่นี่ไม่ได้ช่วยฉัน (อาจเกี่ยวข้องกับภาพ FROM ของฉันใน Dockerfile)

ดังนั้นฉันจึงต้องการสร้างbash scriptในพื้นที่ทำงานของฉันรวมกับ--build-argเพื่อจัดการคำสั่ง if ในขณะที่ Docker build โดยตรวจสอบว่าอาร์กิวเมนต์ว่างเปล่าหรือไม่

สคริปต์ทุบตี:

#!/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 --pull -f "Dockerfile" -t $SERVICE_NAME --build-arg arg="yes" .

หมายเหตุ: สิ่งนี้จะไปที่อื่น (เท็จ) ในสคริปต์ทุบตี

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

หมายเหตุ: สิ่งนี้จะไปที่ if (จริง)

แก้ไข 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 เดียว , ต้องการเพียงสายเดียวที่จะสร้างนักเทียบท่าต่อเงื่อนไขการสร้างและหลีกเลี่ยงการทุบตี

สารละลาย:

ต่อไปนี้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_argDockerfile แบบมีเงื่อนไข ไปเลย

เอาต์พุตเมื่อทำงาน:

(ขอย่อนิดนึง ... )

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สำหรับความคิดที่น่าทึ่งนี้!


จนถึงตอนนี้แนวทางที่ดีที่สุด
Felipe Desiderati

ก็คิดเช่นกันนั่นคือเหตุผลที่ฉันโพสต์ที่นี่ คุยข่าว @FelipeDesiderati
User12547645

1
นี่เป็นทางออกที่ดีที่สุดของทั้งหมดที่นำเสนอในที่นี้เนื่องจากคุณไม่จำเป็นต้องใช้วิธีแก้ปัญหากับสคริปต์ / ทุบตี แต่ใช้วิธีที่เกี่ยวข้องกับความรู้ Dockerfile เท่านั้น คำตอบที่ดี
Akito

“ 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 แล้ว echo ทั้งสองจะถูกเรียกใช้งาน RUN test -z "$YOURVAR" || echo "var is set" && echo "var is not set"
Harshad Vyawahare

จริง โปรดจำไว้ว่าสิ่งเหล่านี้ไม่ใช่บล็อกเงื่อนไขที่แตกแขนงพวกเขาดำเนินการตามลำดับโดยวางไว้&&หน้า||:test ! -z "$YOURVAR" && echo "var is set" || echo "var is not set"
Brandt

5

ตามที่คนอื่นบอกเชลล์สคริปต์จะช่วยได้

เพียงแค่กรณีเพิ่มเติม IMHO มันมูลค่าการกล่าวขวัญ (สำหรับคนอื่นที่สะดุดนี่มองหากรณีที่ง่ายขึ้น) นั่นคือการเปลี่ยนสิ่งแวดล้อม

ตัวแปรสภาพแวดล้อม (ประกาศด้วย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 script และ 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
....
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.