我有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

当前回答

使用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

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

其他回答

使用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

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

对于上述提出的解决方案,有一个有趣的替代方案,它使用单个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说明:

我们首先得到一个基本图像(在您的例子中是centos:7),并将其放入自己的阶段。基本阶段应该包含在条件之前要做的事情。在那之后,我们还有两个阶段,代表我们的条件的分支:branch-version-1和branch-version-2。我们两个都做了。最终阶段根据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

感谢Tõnis提供这个惊人的想法!

根据docker build命令的文档,有一个名为——build-arg的参数。

使用示例:

docker build --build-arg HTTP_PROXY=http://10.20.30.2:1234 .

在我看来,这正是你所需要的:)

在可能的情况下,不要使用其他答案中描述的构建参数。这是一个古老的混乱的解决方案。Docker的target属性解决了这个问题。

目标的例子

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在现实世界中变得很复杂。使用buildkit & COPY——from可以获得更快、更可维护的dockerfile:

Docker在目标之上构建每个阶段,而不管它是否被继承。使用buildkit只构建继承的阶段。Docker必须是v19+。希望这很快会成为默认功能。 目标可以共享构建阶段。使用COPY——from来简化继承。

FROM foo as base
RUN ...
WORKDIR /opt/my-proj

FROM base as npm-ci-dev
# invalidate cache
COPY --chown=www-data:www-data ./package.json /opt/my-proj/package.json
COPY --chown=www-data:www-data ./package-lock.json /opt/my-proj/package-lock.json
RUN npm ci

FROM base as npm-ci-prod
# invalidate cache
COPY --chown=www-data:www-data ./package.json /opt/my-proj/package.json
COPY --chown=www-data:www-data ./package-lock.json /opt/my-proj/package-lock.json
RUN npm ci --only=prod

FROM base as proj-files
COPY --chown=www-data:www-data ./ /opt/my-proj

FROM base as image-dev
# Will mount, not copy in dev environment
RUN ...

FROM base as image-ci
COPY --from=npm-ci-dev /opt/my-proj .
COPY --from=proj-files /opt/my-proj .
RUN ...

FROM base as image-stage
COPY --from=npm-ci-prod /opt/my-proj .
COPY --from=proj-files /opt/my-proj .
RUN ...

FROM base as image-prod
COPY --from=npm-ci-prod /opt/my-proj .
COPY --from=proj-files /opt/my-proj .
RUN ...

开启实验模式。

sudo echo '{"experimental": true}' | sudo tee /etc/docker/daemon.json

启用buildkit进行构建。Buildkit默认构建时不带缓存-启用——build-arg BUILDKIT_INLINE_CACHE=1

CI构建工作。

DOCKER_BUILDKIT=1 \
    docker build \
    --build-arg BUILDKIT_INLINE_CACHE=1 \
    --target image-ci\
    -t foo:ci
    .

使用——cache-from从拉出的图像中缓存

生产工作

docker pull foo:ci
docker pull foo:stage

DOCKER_BUILDKIT=1 \
    docker build \
    --cache-from foo:ci,foo:stage \
    --target image-prod \
    -t prod
    .

我看到了很多可能的解决方案,但没有一个适合我今天面临的问题。所以,我正在花时间用另一个对我有用的可能的解决方案来回答这个问题。

在我的例子中,我利用了众所周知的if ["$VAR" == "this"];然后重复“do that”;fi。警告是Docker,我不知道为什么,在这种情况下不喜欢双等号。所以我们需要这样写if ["$VAR" = "this"];然后重复“do that”;fi。

这里有一个完整的例子,适用于我的情况:

FROM node:16

# Let's set args and envs
ARG APP_ENV="dev"
ARG NPM_CMD="install"
ARG USER="nodeuser"
ARG PORT=8080
ENV NPM_CONFIG_PREFIX=/home/node/.npm-global
ENV PATH=$PATH:/home/node/.npm-global/bin
ENV NODE_ENV=${APP_ENV}

# Let's set the starting point
WORKDIR /app

# Let's build a cache
COPY package*.json .
RUN date \
 # If the environment is production or staging, omit dev packages
 # If any other environment, install dev packages
 && if [ "$APP_ENV" = "production" ]; then NPM_CMD="ci --omit=dev"; fi \
 && if [ "$APP_ENV" = "staging" ]; then NPM_CMD="ci --omit=dev"; fi \
 && npm ${NPM_CMD} \
 && usermod -d /app -l ${USER} node

# Let's add the App
COPY . .

# Let's expose the App port
EXPOSE ${PORT}

# Let's set the user
USER ${USER}

# Let's set the start App command
CMD [ "node", "server.js" ]

因此,如果用户传递了正确的build参数,docker build命令将为生产应用创建一个映像。如果没有,它将创建一个带有dev Node.js包的应用程序映像。

为了让它工作,你可以这样调用:

# docker build --build-arg APP_ENV=production -t app-node .