Dockerfile(如果带有外部参数的其他条件)


129

我有dockerfile

FROM centos:7
ENV foo=42

然后我建立它

docker build -t my_docker .

并运行它。

docker run -it -d  my_docker

是否可以从命令行传递参数并将其与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 '18

接受的答案不涵盖问题的“是否满足条件”部分。如果条件检查并非必需,最好将其重命名为“带有外部参数的Dockerfile”。
Ruslan Kabalin '18

@RuslanKabalin-接受的答案同时包含“ then”和“ else”子句。唯一的区别是什么是“如果条件”进行测试。对于有问题的代码:RUN if [ "$arg" == "42" ]; then ENV=TRUE; else ENV=FALSE; fi。或者,如果可能缺少arg: RUN if [ "x$arg" == "x42" ]; then ...
ToolmakerSteve

Answers:


191

看起来可能不太干净,但是您可以按照以下方式使用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
这里的三进制是检查是否提供了任何参数,而不是检查特定的参数。如果将其重写为以下内容,则似乎会更明显,if [ $arg != "" ] ;但我肯定存在一些我不熟悉的
陷阱

21
if [[ -n "$arg" ]]如果参数不为空,if [[ -z "$arg" ]]则为true;如果参数为空
则为

6
如何编写多行if语句?
Ashutosh Chamoli

12
@Quannt-不,请参见为什么要使用Shell脚本... [ "x$arg" = "x" ]看起来好像在比较两个带引号的字符串,但是引号被剥夺了;这样可以使语法正确。引号后:好: if x = x ..。不好:if = 。但是,有更好的方法来检查参数是否存在检查输入参数是否存在
ToolmakerSteve

20

由于某种原因,这里的大多数答案都对我没有帮助(也许与Dockerfile中的FROM映像有关)

所以我更喜欢bash script在我的工作空间中创建一个,--build-arg以便在Docker构建时通过检查参数是否为空来处理if语句

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构建

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

备注:这将转到bash脚本中的else(false)

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

备注:这将转到if(true)

编辑1:

几次尝试之后,我发现下面的文章,这一个 这让我明白两两件事:

1)FROM之前的ARG在构建之外

2)默认shell是/ bin / sh,这意味着if else在docker构建中的工作方式略有不同。例如,您只需要一个“ =”而不是“ ==”来比较字符串。

因此,您可以在 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 .


16

提议的解决方案有一个有趣的替代方案,它可以与单个Dockerfile一起使用,每个条件构建仅需要对Docker 构建进行一次调用,避免使用bash

解:

以下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-1branch-version-2。我们都建立它们。然后final,该阶段基于选择这些阶段之一my_arg。有条件的Dockerfile。你去。

运行时输出:

(我略了一下...)

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
这是这里提出的所有解决方案中最好的解决方案,因为您不需要对脚本/ bash使用变通办法,而只需使用仅涉及Dockerfile知识的方法即可。好答案。
Akito

“ ARG ENV”有什么作用?你能对此有所启发吗?谢谢。
最高

@Max感谢您指出这一点。不必要
User12547645

12

只需直接使用“测试”二进制文件即可。如果您不想指定“其他”条件,还应该使用noop命令“:”,因此docker不会因非零返回值错误而停止。

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,则将执行两个回显。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"
布兰特

5

就像其他人所说的那样,shell脚本会有所帮助。

只是一个额外的案例,恕我直言,值得一提(对于偶然发现这里的其他人,寻找一个更简单的案例),那就是环境替换

环境变量(用该ENV语句声明)也可以在某些指令中用作由解释的变量Dockerfile

${variable_name}语法还支持一些标准的bash修饰符,如下所示:

  • ${variable:-word}表示如果variable设置,则结果将是该值。如果variable未设置,则为word结果。

  • ${variable:+word}表示如果variable设置了if,则将是word结果,否则结果为空字符串。


5

接受的答案可以解决问题的,但如果你想多if在dockerfile的条件下,你可以做到这一点放置\在每行的末尾(类似于你将如何在shell脚本做),并结束与每个命令;。您甚至可以像set -eux第一条命令一样定义某物。

例:

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脚本和Alpine / Centos

Docker文件

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

传递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

没有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
....
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.