Dockerfile nếu điều kiện khác với các đối số bên ngoài


130

Tôi có dockerfile

FROM centos:7
ENV foo=42

sau đó tôi xây dựng nó

docker build -t my_docker .

và chạy nó.

docker run -it -d  my_docker

Có thể chuyển đối số từ dòng lệnh và sử dụng nó với if else trong Dockerfile không? Ý tôi là

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

và xây dựng bằng lập luận này.

 docker build -t my_docker . --my_arg=42

1
Điều này có thể được xử lý từ một kịch bản xây dựng.
Snps

@ Зелёный không chính xác. Xem câu trả lời bên dưới, điều này có thể được thực hiện với --build-arg
devnul3

Câu trả lời được chấp nhận không bao gồm phần "nếu điều kiện khác" của câu hỏi. Sẽ tốt hơn nếu đổi tên nó thành "Dockerfile với các đối số bên ngoài" nếu kiểm tra điều kiện không có nghĩa là một yêu cầu.
Ruslan Kabalin

@RuslanKabalin - câu trả lời được chấp nhận có cả mệnh đề "then" và "else". Sự khác biệt duy nhất là những gì được kiểm tra trong "điều kiện nếu". Đối với mã thể hiện trong câu hỏi: RUN if [ "$arg" == "42" ]; then ENV=TRUE; else ENV=FALSE; fi. Hoặc nếu có thể thiếu đối số: RUN if [ "x$arg" == "x42" ]; then ...
ToolmakerSteve

Câu trả lời:


192

Nó có thể trông không sạch sẽ nhưng bạn có thể có Dockerfile (có điều kiện) như sau:

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

và sau đó xây dựng hình ảnh như:

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

hoặc là

docker build -t my_docker .


20
Không nên [ "$arg" = "x" ]thay vì [ "x$arg" = "x" ]?
Quannt

4
Đệ tam ở đây là kiểm tra xem có bất kỳ đối số nào được cung cấp hay không, thay vì kiểm tra đối số cụ thể. Nó sẽ có vẻ rõ ràng hơn nếu viết lại như if [ $arg != "" ] ;nhưng im chắc chắn có một số Gotcha tôi không quen thuộc với
myol

21
if [[ -n "$arg" ]]đúng nếu một param không trống rỗng, if [[ -z "$arg" ]]đúng nếu một param rỗng
acumartini

6
Làm thế nào để viết một câu lệnh if nhiều dòng?
Ashutosh Chamoli

12
@Quannt - không, hãy xem Tại sao tập lệnh shell ... [ "x$arg" = "x" ] trông giống như nó đang so sánh hai chuỗi được trích dẫn, nhưng các dấu ngoặc kép bị loại bỏ; Điều này giữ cho cú pháp chính xác. Sau quote-stripping: Tốt: if x = x ... Nhược điểm: if = . TUY NHIÊN, có nhiều cách tốt hơn để kiểm tra sự tồn tại của một tham số: Kiểm tra sự tồn tại của các đối số đầu vào .
ToolmakerSteve

20

Từ một số lý do mà hầu hết các câu trả lời ở đây không giúp được tôi (có thể nó liên quan đến hình ảnh FROM của tôi trong Dockerfile)

Vì vậy, tôi muốn tạo một bash scripttrong không gian làm việc của mình kết hợp với --build-argđể xử lý câu lệnh if trong khi Docker xây dựng bằng cách kiểm tra xem đối số có trống hay không

Tập lệnh 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" .

Lưu ý: Điều này sẽ chuyển sang khác (sai) trong tập lệnh bash

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

Nhận xét: Điều này sẽ chuyển đến if (true)

Chỉnh sửa 1:

Sau nhiều cố gắng tôi đã tìm thấy sau bài báo và điều này một mà đã giúp tôi hiểu được 2 điều:

1) ARG trước FROM nằm ngoài bản dựng

2) Shell mặc định là / bin / sh có nghĩa là if else hoạt động hơi khác một chút trong bản dựng docker. ví dụ: bạn chỉ cần một "=" thay vì "==" để so sánh các chuỗi.

Vì vậy, bạn có thể làm điều này bên trong Dockerfile

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

và trong docker build:

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


17

Có một giải pháp thay thế thú vị cho các giải pháp được đề xuất, hoạt động với một Dockerfile duy nhất , chỉ yêu cầu một lệnh gọi duy nhất đến bản dựng docker cho mỗi bản dựng có điều kiện và tránh bash .

Giải pháp:

Sau đây Dockerfilegiải quyết vấn đề đó. Sao chép-dán nó và tự mình thử.

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

Giải thích về Dockerfile:

Trước tiên, chúng tôi lấy một basehình ảnh ( centos:7trong trường hợp của bạn) và đưa nó vào giai đoạn của riêng nó. Các basegiai đoạn nên chứa những điều mà bạn muốn làm trước khi tình trạng này. Sau đó, chúng ta có hai giai đoạn nữa, đại diện cho các nhánh của tình trạng của chúng ta: branch-version-1branch-version-2. Chúng tôi xây dựng cả hai. Các finalgiai đoạn hơn sẽ chọn một trong những giai đoạn, dựa trên my_arg. Dockerfile có điều kiện. Làm đi.

Đầu ra khi chạy:

(Tôi viết tắt cái này một chút ...)

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

Cảm ơn Tõnis vì ý tưởng tuyệt vời này!


Cho đến nay, cách tiếp cận tốt nhất.
Felipe Desiderati

Tôi cũng nghĩ vậy, đó là lý do tại sao tôi đăng nó ở đây. Đọc tin tức @FelipeDesiderati
Người dùng12547645

1
Đây là giải pháp tốt nhất trong số tất cả các giải pháp được trình bày ở đây, bởi vì bạn không cần phải sử dụng các giải pháp thay thế bằng script / bash, mà sử dụng một cách chỉ liên quan đến kiến ​​thức Dockerfile. Câu trả lời chính xác.
Akito

"ARG ENV" làm gì? Bạn có thể làm sáng tỏ về điều này? Cảm ơn.
Tối đa

@Max cảm ơn vì đã chỉ ra điều đó. Nó không cần thiết
Người dùng12547645

12

Chỉ cần sử dụng nhị phân "kiểm tra" trực tiếp để thực hiện việc này. Bạn cũng nên sử dụng lệnh noop ":" nếu bạn không muốn chỉ định điều kiện "khác", vì vậy docker không dừng lại với lỗi giá trị trả về khác 0.

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
Tương tự như RUN [ -z "$YOURVAR" ] && ... || :, tôi tin rằng
OneCricketeer

4
Đối với câu lệnh sau, nếu var được đặt thì cả tiếng vọng đều được thực thi. RUN test -z "$YOURVAR" || echo "var is set" && echo "var is not set"
Harshad Vyawahare

Thật. Hãy nhớ rằng đó không phải là các khối có điều kiện phân nhánh, chúng thực thi tuần tự, đặt &&trước ||:test ! -z "$YOURVAR" && echo "var is set" || echo "var is not set"
Brandt

5

Chính xác như những người khác đã nói, shell script sẽ hữu ích.

Chỉ là một trường hợp bổ sung, IMHO cần phải nhắc đến (đối với những người khác tình cờ gặp ở đây, đang tìm kiếm một trường hợp dễ dàng hơn), đó là Môi trường thay thế .

Các biến môi trường (được khai báo với ENVcâu lệnh) cũng có thể được sử dụng trong một số lệnh nhất định như là các biến được thông dịch bởi Dockerfile.

Các ${variable_name}cú pháp cũng hỗ trợ một vài trong số các từ bổ nghĩa bash tiêu chuẩn theo quy định dưới đây:

  • ${variable:-word}chỉ ra rằng nếu variableđược đặt thì kết quả sẽ là giá trị đó. Nếu variablekhông được thiết lập thì wordsẽ là kết quả.

  • ${variable:+word}chỉ ra rằng nếu variableđược đặt thì wordsẽ là kết quả, nếu không thì kết quả là chuỗi trống.


5

Các câu trả lời được chấp nhận có thể giải quyết vấn đề, nhưng nếu bạn muốn multiline ifđiều kiện trong dockerfile, bạn có thể làm việc đặt rằng \vào cuối mỗi dòng (tương tự như cách bạn sẽ làm gì trong một kịch bản shell) và kết thúc mỗi lệnh với ;. Bạn thậm chí có thể định nghĩa someting như set -euxlệnh đầu tiên.

Thí dụ:

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

Trong trường hợp của bạn:

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

Sau đó, xây dựng với:

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

4

Sử dụng tập lệnh Bash và 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

Tranh luận vượt qua: 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

Không có lập luận: 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
....
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.