如何为开发和生产配置不同的Dockerfile

99

我在开发和生产laravel项目时,都使用Docker。我为开发和生产环境准备了略有不同的dockerfile。例如,在开发环境中,我会将本地目录挂载到docker容器中,这样每次更改代码时就不需要再进行docker构建。

由于挂载的目录仅在运行docker容器时可用,因此我无法在开发环境的dockerfile中添加诸如“composer install”或“npm install”之类的命令。

目前,我管理着两个docker文件,是否有任何方法可以使用单个docker文件来完成此操作,并通过发送参数来决定在进行docker构建时要运行哪些命令。

我想要实现的目标是:

在docker文件中

...
IF PROD THEN RUN composer install
...

在 Docker 构建期间:
docker build [PROD] -t mytag .
6个回答

93

作为最佳实践,您应该尽量使用一个Dockerfile以避免在不同环境之间出现意外错误。但是,您可能有一种情况无法这样做。

Dockerfile语法不足以支持这种情况,但是您可以使用shell脚本来实现。

创建一个名为install.sh的shell脚本,执行以下操作:

if [ ${ENV} = "DEV" ]; then 
    composer install
else
    npm install
fi
在你的Dockerfile中添加这个脚本,然后在构建时执行它。
...
COPY install.sh install.sh
RUN chmod u+x install.sh && ./install.sh
...

构建时传递构建参数以指定环境,例如:

docker build --build-arg "ENV=PROD" ...

1
请确保在Dockerfile的第一个FROM下添加ARG ENV,以便获取构建参数。至少这就是我不得不做的。谢谢,这真的很有用。 - MBillau

68

更新(2020):由于这篇文章写于三年前,许多事情发生了变化(包括我对这个话题的看法)。我建议的做法是,使用一个dockerfile和脚本。请参见@yamenk的答案

原文:

您可以使用两个不同的Dockerfiles。

# ./Dockerfile (non production)
FROM foo/bar
MAINTAINER ...

# ....

还有第二个:

# ./Dockerfile.production
FROM foo/bar
MAINTAINER ...

RUN composer install

在调用构建命令时,您可以指定它应该使用哪个文件:

$> docker build -t mytag .
$> docker build -t mytag-production -f Dockerfile.production .

12

您可以直接使用构建参数而不提供额外的sh脚本。这样可能看起来有点凌乱,但它是有效的。

Dockerfile必须像这样:

FROM alpine
ARG mode
RUN if [ "x$mode" = "xdev" ] ; then echo "Development" ; else echo "Production" ; fi

以下是检查的命令:

docker build -t app --build-arg mode=dev .
docker build -t app --build-arg mode=prod .

5
好的,为什么 $mode 前面要加上 'x'?一定有原因。 - John Small
2
仅是插值的一个例子。 - Nick Roz

8

我已经尝试了几种方法,包括使用docker-compose、多阶段构建、通过文件传递参数以及其他答案中使用的方法。在尝试这些方法后,我公司需要一种好的方法来完成此任务,下面是我的意见。

最佳方法是通过cmd传递参数。你可以通过vscode右键点击并选择构建image来传递参数。 点击图片构建时的Visual Studio Code图像 使用以下代码:

ARG BuildMode
RUN echo $BuildMode
RUN if [ "$BuildMode" = "debug" ] ; then apt-get update \
    && apt-get install -y --no-install-recommends \
       unzip \
    && rm -rf /var/lib/apt/lists/* \
    && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg ; fi

同时,在 Dockerfile 的构建部分:

ARG BuildMode
ENV Environment=${BuildMode:-debug}
RUN dotnet build "debugging.csproj" -c $Environment -o /app

FROM build AS publish
RUN dotnet publish "debugging.csproj" -c $Environment -o /app

3

最好的方法是在您的项目中使用.env文件。

您可以定义两个变量CONTEXTDIRECTORY和DOCKERFILENAME,并创建Dockerfile-dev和Dockerfile-prod

以下是使用示例:

Docker组合文件:

services:
  serviceA:
    build: 
      context: ${CONTEXTDIRECTORY:-./prod_context}
      dockerfile: ${DOCKERFILENAME:-./nginx/Dockerfile-prod}

在项目根目录下,存在一个名为`.env`的文件:
CONTEXTDIRECTORY=./
DOCKERFILENAME=Dockerfile-dev

注意上下文。其路径从您指定的Dockerfile所在的目录开始,而不是从docker-compose目录开始。

在默认值中,我使用prod,因为如果您忘记指定环境变量,则无法在生产中意外构建dev版本

使用不同的Dockerfile解决方案比脚本更方便。更容易更改和维护


哇,我不知道那个构造像是如果为空,则${CONTEXTDIRECTORY:-./prod_context}这样工作,谢谢! - bora89

2
如果您使用docker-compose运行容器,也可以使用多阶段Dockerfile。首先,您声明通用部分,两个环境都使用并将其别名为“base”或其他名称。然后在同一个Dockerfile中,您声明新的FROM指令,该指令继承base并运行环境的命令。
例如,我有一个Ruby镜像,我想要运行:
  • 开发中的bundle install
  • 生产环境下的bundle install --without development test
因此,我的Dockerfile将类似于以下内容:
FROM ruby:2.7.1-slim as base
CMD ['/bin/bash']

COPY Gemfile .
COPY Gemfile.lock .

FROM base as dev
RUN bundle install

FROM base as prod
RUN bundle install --without development test

在我的开发docker-compose.yml文件中,我使用了以下内容:
services:
  web:
    build:
      target: dev

docker-compose.prod.yml 文件中,包含如下内容:
services:
  web:
    build:
      target: prod

你可以在这里了解更多关于多阶段 Dockerfile 的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接