Dockerfile if else条件与外部参数

时间:2017-04-27 10:05:23

标签: docker dockerfile

我有dockerfile

FROM centos:7
ENV foo=42

然后我建立它

docker build -t my_docker .

并运行它。

docker run -it -d  my_docker

是否可以从命令行传递参数并在Dockerfile中使用if else?我的意思是

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

并使用此参数构建。

 docker build -t my_docker . --my_arg=42

11 个答案:

答案 0 :(得分:115)

看起来可能看起来不干净但你可以使用你的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 .

答案 1 :(得分:44)

建议的解决方案还有一个有趣的替代方法,它与单个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_arg==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

Thanks to Tõnis for this amazing idea!

答案 2 :(得分:19)

根据build命令documentation,有一个名为--build-arg的参数

https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables-build-arg

示例用法
docker build --build-arg HTTP_PROXY=http://10.20.30.2:1234 .

IMO它是你需要的:)

答案 3 :(得分:10)

由于某些原因,这里的大部分答案对我没有帮助(也许它与我在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 Build:

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:

经过多次尝试后,我找到了以下article和此one 这让我理解了两件事:

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

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

所以你可以在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 .

答案 4 :(得分:8)

accepted answer可能解决了这个问题,但是如果您想在dockerfile中使用多行if条件,可以这样做,将\放在每行的末尾(类似于您将在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

答案 5 :(得分:7)

----- 编辑 -----

更容易使用目标。

Dockerfile

FROM foo as base

RUN ...

# Build dev image
FROM base as image-dev

RUN ...
COPY ...

# Build prod image
FROM base as image-prod

RUN ...
COPY ...
docker build --target image-dev -t foo .
version: '3.4'

services:

  dev:
    build:
      context: .
      dockerfile: Dockerfile
      target: image-dev

----- 编辑结束 -----

您可以使用最适合您需求的条件系统。

Dockerfile

ARG ENV

FROM foo as base

ARG ENV

# run common
RUN ...

# For long running tasks that would slow down deployments
RUN if [[ "$ENV" == "dev" ]] ; then \
        yum install -y lots of big dev packages ; \
    fi

# Build dev image
FROM base as image-dev

RUN ...
COPY ...

# Build prod image
FROM base as image-prod

RUN ...
COPY ...

FROM image-$ENV AS final

请注意,我们定义了两次 ENV - 您需要全局定义 ENV,并在使用它的每个图像中定义。

使用泊坞窗:

docker build -t my_docker . --build-arg ENV="dev"

使用docker-compose:

version: '3'

services:

  dev:
    container_name: dev
    ports:
      - 3000:8080
    volumes:
      - ./:/var/task
    tty: true
    build:
      context: .
      dockerfile: Dockerfile
      args:
        ENV: dev
docker-compose build --no-cache dev && docker-compose up dev

答案 6 :(得分:6)

直接使用“test”二进制文件来执行此操作。如果您不想指定“else”条件,也应该使用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" && :

答案 7 :(得分:2)

正如其他人所说,shell脚本会有所帮助。

还有另外一个案例,恕我直言,值得一提(对于偶然发现这里的其他人,寻找更简单的案例),就是Environment replacement

  

环境变量(用const express = require('express') const cookieParser = require('cookie-parser') const app = express() app.use(cookieParser()) app.get('/', (req, res) => { if (!req.cookies.auth) { res.cookie('auth', { id: '123' }) } res.json({ message: 'It worked!' }) }) app.listen(8080, () => console.log('http://localhost:8080)) 声明)也可以在某些指令中用作ENV解释的变量。

     

Dockerfile语法还支持一些下面指定的标准bash修饰符:

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

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

  •   

答案 8 :(得分:0)

如果您想要的是动态构建图像,那么您应该使用构建脚本来完成。

Docker已经为许多语言提供了SDK,这些语言通常包含一个构建调用,允许您提供任意字符串或Dockerfile来构建。

例如,使用Ruby:

require 'docker'

val = my_arg == 42 ? "TRUE" : "FALSE"

# Create an Image from a Dockerfile as a String.
Docker::Image.build("FROM centos:7\nENV MY_ENV=" + val)

答案 9 :(得分:0)

使用Bash脚本和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

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

无参数: 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
....

答案 10 :(得分:0)

我在容器上设置代理服务器时遇到了类似的问题。

我使用的解决方案是一个入口点脚本和另一个用于环境变量配置的脚本。使用 RUN,您可以确保配置脚本在构建时运行,并在运行容器时运行 ENTRYPOINT。

--build-arg 用于命令行设置代理用户和密码。

因为我在容器启动时需要相同的环境变量,所以我使用了一个文件来“持久化”它从构建到运行。

入口点脚本如下所示:

<div class="bg-home-page-image bg-opacity-25">

configproxy.sh

#!/bin/bash
# Load the script of environment variables
. /root/configproxy.sh
# Run the main container command
exec "$@"

在 Dockerfile 中,配置:

#!/bin/bash

function start_config {
read u p < /root/proxy_credentials

export HTTP_PROXY=http://$u:$p@proxy.com:8080
export HTTPS_PROXY=https://$u:$p@proxy.com:8080

/bin/cat <<EOF > /etc/apt/apt.conf 
Acquire::http::proxy "http://$u:$p@proxy.com:8080";
Acquire::https::proxy "https://$u:$p@proxy.com:8080";
EOF
}

if [ -s "/root/proxy_credentials" ]
then
start_config
fi

无需代理设置即可构建

# Base Image
FROM ubuntu:18.04

ARG user
ARG pass

USER root

# -z the length of STRING is zero
# [] are an alias for test command
# if $user is not empty, write credentials file
RUN if [ ! -z "$user" ]; then echo "${user} ${pass}">/root/proxy_credentials ; fi

#copy bash scripts
COPY configproxy.sh /root
COPY startup.sh .

RUN ["/bin/bash", "-c", ". /root/configproxy.sh"]

# Install dependencies and tools
#RUN apt-get update -y && \
#    apt-get install -yqq --no-install-recommends \
#    vim iputils-ping

ENTRYPOINT ["./startup.sh"]
CMD ["sh", "-c", "bash"]

使用代理设置构建

docker build -t img01 -f Dockerfile . 

看看here