在 Docker 上开发应用 - 编写 Dockerfile 的最佳实践

在 Docker 上开发应用 - 编写 Dockerfile 的最佳实践

2018年02月25日 13:01:04 阅读数:673 标签: dockerdockerfile最佳实践 更多php

我的分类: Dockerpython

原文地址nginx

Docker 能够经过从 Dockerfile 中读取指令来自动构建镜像,Dockerfile 是一个文本文件,其中包含了按顺序排列的构建指定镜像所需的所有命令。Dockerfiles 采用特殊格式,使用一系列特别的指令。能够在 Dockerfile 参考页面 学习这些基础知识。若是对于编写 Dockerfile 你仍是新手,那么接着往下看吧。git

本文档介绍了由 Docker 公司和 Docker 社区推荐的用于构建高效镜像的最佳实践和方法。要查看更多实践和建议,请点击 Dockerfile for buildpack-depsgithub

注意:要查看 Dockerfile 命令的详情,点击 Dockerfile 参考页面 。golang

1. 通常准则和建议

1.1 容器应该精简 ephemeral

由 Dockerfile 定义的映像生成的容器应尽量精简。意思是说,在容器被中止和销毁,而且创建和配置完成一个新的容器时,有绝对最少的设置和配置。 你可能须要查看 十二要素应用宣言 的 Processes 部分(译文在 这里),以了解以这种无状态方式运行容器的动机。sql

原文: 
The container produced by the image your Dockerfile defines should be as ephemeral as possible. By “ephemeral,” we mean that it can be stopped and destroyed and a new one built and put in place with an absolute minimum of set-up and configuration. You may want to take a look at the Processes section of the 12 Factor app methodology to get a feel for the motivations of running containers in such a stateless fashion.docker

1.2 使用 .dockerignore 文件

执行 docker build 命令时你所在的当前工做目录被称为构建上下文,Dockerfile 文件必须在这个构建上下文中。默认状况下,Dockerfile 被假设在当前目录中,可是能够经过 -f 标志指定一个不一样位置。无论 Dockerfile 文件位于何处,当前目录中的全部文件和目录都会做为构建上下文发送到 Docker 守护进程。无心中包含了构建镜像不须要的文件会产生更大的构建上下文和更大的镜像大小。这些反过来又会增长构建时间、获取和上传镜像的时间以及容器的运行时间。要查看构建上下文有多大,在构建 Dockerfile 时查找相似下面的消息。shell

Sending build context to Docker daemon  187.8MB
  • 1

可使用 .dockerignore 文件排除与构建无关的文件而不重构源代码库。该文件支持相似 .gitignore 文件的排除模式。有关建立此文件的信息,参考 这里数据库

1.3 使用多段构建

若是 Docker 版本是 17.05 或更高,那就可使用 多段构建 来大幅下降最终镜像的大小,而无需在构建期间跳过 through hoops 来减小中间层的数量或删除中间文件。

镜像仅由最终一个阶段构建,大部分时间既有利于构建缓存,又能使镜像图层最小化。(Images being built by the final stage only, you can most of the time benefit both the build cache and minimize images layers.)

你的构建阶段可能包含多个层,下面例子从最不常见的变动到最多见的变动排序:

  • 安装构建应用程序所需的工具

  • 安装或更新库和依赖

  • 产生应用

一个 Go 应用程序的 Dockerfile 示例:

FROM golang:1.9.2-alpine3.6 AS build
# Install tools required to build the project
# We need to run `docker build --no-cache .` to update those dependencies
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# Gopkg.toml and Gopkg.lock lists project dependencies
# These layers are only re-built when Gopkg files are updated
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# Install library dependencies
RUN dep ensure -vendor-only

# Copy all project and build it
# This layer is rebuilt when ever a file has changed in the project directory
COPY . /go/src/project/
RUN go build -o /bin/project

# This results in a single layer image
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

1.4 避免安装无用包

要下降复杂性、依赖、文件大小和构建时间,就要避免安装额外的或不须要的包。例如在数据库镜像中不须要文本编辑器。

1.5 每一个容器只解决一个问题

将应用程序解耦为多个容器使得横向扩展和重用容器变得更容易。例如,一个 Web 应用程序堆栈可能由三个独立的容器组成,每一个容器都有其独特的镜像,以解耦的方式管理 Web 应用程序、数据库和内存中的缓存。

你可能听过这句话“每一个容器一个进程”。虽然这个口头禅的意图很好,但并不必定每一个容器只有一个操做系统进程。除了如今可使用 init 进程建立容器 以外,一些程序可能会自行产生其余进程。例如,Celery 能够派生多个工做进程,或者 Apache 可能会为每一个请求建立一个进程。 虽然“每一个容器一个进程”是一个很好的经验法则,但它并非硬性规定。 尽你最大的努力使容器保持干净和模块化。

若是容器互相依赖,可使用 Docker 容器网络 来确保容器之间的通讯。

1.6 最小化层数

在 Docker 17.05 甚至 1.10 以前,最小化镜像的层数是很重要的。下面的改善措施缓解了这个需求:

  • Docker 1.10 及更高版本中,只有 RUN、COPY 和 ADD 命令会建立层。其余命令建立临时的中间层镜像,不会在构建时增长体积。

  • Docker 17.05 及更高版本,增长了分段构建功能,使得能够只复制所需的项目文件到最终的镜像中。这让你能够在中间层构建过程当中添加工具和调试信息,而不会增大最终镜像的体积。

1.7 排序多行参数

只要有可能,经过按字母数字顺序排列多行参数来简化后面的更改。这有助于避免软件包重复并使列表更容易更新。这也使得 PR 更容易阅读和审核。在反斜杠(\)以前添加空格也有帮助。

这是来自 buildpack-deps 镜像 的例子:

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.8 构建缓存

在构建镜像的过程当中,Docker 会按照指定的顺序执行 Dockerfile 文件中的指令。检查完全部指令后,Docker 会从缓存中寻找可用的镜像,而不是建立一个新镜像。若是不想使用缓存,能够在执行 docker build 命令是添加 --no-cache=true选项。

然而,若是容许 Docker 使用缓存,就须要理解它什么时候能,什么时候不能,找到匹配的镜像。Docker 遵照的基本规则以下:

  • 从缓存中已经存在的父镜像开始,将下一条指令与从该基本镜像派生的全部子镜像进行比较,以查看是否使用彻底相同的指令构建了其中的一个子镜像。若是没有则缓存失效。

  • 大多数状况下,简单的将 Dockerfile 中的指令和子镜像中的一个进行比较就足够了。然而,部分指令须要更多的检查和解释。

  • 对于 ADD 和 COPY 指令,镜像中的文件内容都须要检查并为每一个文件计算校验和 checksum。这些校验和中不考虑文件的最后编辑时间和最后访问时间。在缓存查找过程当中,将校验和与现有镜像中的校验和进行比较。若是文件中的内容有任何更改,如内容和元数据,则缓存将失效。

  • 除了 ADD 和 COPY 指令,缓存检查时不会经过检查容器中的文件来决定缓存是否匹配。例如在处理 RUN apt-get -y update 命令时,不会经过检查容器中更新过的文件来决定缓存是否命中。此时只会对比命令字符串是否相同来寻找匹配的缓存。

一旦关闭缓存,全部后续的 Dockerfile 命令都会生成新镜像,不使用缓存。

2. The Dockerfile instructions

这些建议能够帮助你写出高效的、容易维护的 Dockerfile。

FROM

FROM 指令的 Dockerfile 参考资料

只要有可能,使用官方仓库做为镜像的基础。推荐使用 Alpine 镜像,由于它的控制很是严格,而且保持最小(目前低于5 MB),同时仍然是完整的发行版。

LABEL

理解 labels 对象

能够给镜像添加标签,来帮助项目组织镜像、记录许可信息、帮助自动化或出于其余缘由。对于每一个标签,添加一行以 LABEL 开头并带有一个或多个键值对的行。下面示例显示了多种支持的格式。解释性意见包含在内。

注意:若是字符串中包含空格,则必须用双引号引发来或转义这个空格。若是字符串中包含双引号,必须转义。

# 设置一个或多个独立的标签
LABEL com.example.version="0.0.1-beta"
LABEL vendor="ACME Incorporated"
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""
  • 1
  • 2
  • 3
  • 4
  • 5

镜像能够有多个标签。在 Docker 1.10 版本以前,建议将全部的标签合并到一个 LABEL 指令中,以防止建立额外的层。如今不须要这么作了,可是仍然支持合并标签。

# 在同一行中设置多个标签
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"
  • 1
  • 2

上面的例子也可用下面的写法:

# 一次设置多个标签,并使用续行字符打断很长的行
LABEL vendor=ACME\ Incorporated \
      com.example.is-beta= \
      com.example.is-production="" \
      com.example.version="0.0.1-beta" \
      com.example.release-date="2015-02-12"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

有关可以使用的标签中键和值的信息,参阅 Understanding object labels。有关查询 querying 标签的信息,参阅 Managing labels on objects 中与过滤相关的项目。另请参阅 Dockerfile 参考中的 LABEL

RUN

RUN 指令的 Dockerfile 参考资料

将很长或很复杂的 RUN 语句用反斜线(\)切分为多行可让 Dockerfile 文件易读、易理解而且易维护。

1. APT-GET 指令

RUN 最多见的用例多是 apt-get 应用程序。由于 RUN apt-get 命令会安装软件包,有几个须要注意的问题。

应该避免使用 RUN apt-get upgrade 或 dist-upgrade,由于许多来自父镜像的“essential”基本软件包没法在非特权容器内升级。若是父镜像中的软件包已过期,应联系其维护人员。若是你知道须要更新某个特定软件包,好比“foo”,请使用 apt-get install -y foo 自动更新。

在同一个 RUN 语句中一同运行 apt-get update 和 apt-get install。例如:

RUN apt-get update && apt-get install -y \
        package-bar \
        package-baz \
        package-foo
  • 1
  • 2
  • 3
  • 4

RUN 语句中单独使用 apt-get update 会致使缓存问题,并使后面的 apt-get install 指令执行失败。例如,看下面的 Dockerfile:

FROM ubuntu:14.04
    RUN apt-get update
    RUN apt-get install -y curl
  • 1
  • 2
  • 3

上面的镜像构建完成后,全部的层都会在 Docker 缓存中。假设后面会经过添加额外的包来变动 apt-get install 这条指令:

FROM ubuntu:14.04
    RUN apt-get update
    RUN apt-get install -y curl nginx
  • 1
  • 2
  • 3

此时 Docker 会认为这个例子中的前两步和上个例子的同样,从而使用上个例子生成的缓存,致使 apt-get update 命令并未执行。apt-get update 没有运行,因此后面可能会安装的 curl 和 nginx 可能不是最新版本。

使用 RUN apt-get update && apt-get install -y 能够确保 Dockerfile 安装最新版本的包,无需进一步编码或手动干预。这种技术被称为“缓存破坏”(cache busting)。 也能够经过指定软件包的版原本清除缓存。这被称为版本固定(version pinning),例如:

RUN apt-get update && apt-get install -y \
        package-bar \
        package-baz \
        package-foo=1.3.*
  • 1
  • 2
  • 3
  • 4

版本固定会强制构建时检索特定的版本,而无论缓存中的内容。该技术还能够减小因为所需软件包的意外更改而致使的故障。

下面是一个组织良好的 RUN 指令,用来演示全部的 apt-get 建议。

RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
 && rm -rf /var/lib/apt/lists/*
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

s3cmd 指定要安装 1.1.* 版本。若是镜像在以前使用的是旧的版本,指定新版本会致使 apt-get update 命令的缓存破坏,从而确保安装的是这个指定的新版本。每一个包单独出如今一行中,能够防止出现包重复的错误。

此外,当经过删除 /var/lib/apt/lists 目录来清除 apt 的缓存时,能够减少镜像尺寸(由于 apt 缓存不会存入层)。这里的 RUN 语句用 apt-get update 命令开头,因此在执行 apt-get install 命令以前包缓存老是会获得更新。

注意:官方的 Debian 和 Ubuntu 镜像会 自动运行 apt-get clean,所以不须要显式调用。

2. 使用管道

部分 RUN 命令借助管道 pipe 将一个命令的输出发送到另外一个命令。下面例子演示了管道符号 | 的使用:

RUN wget -O - https://some.site | wc -l > /number
  • 1

Docker 使用 /bin/sh -c 解释器执行这些命令,该解释器只评估管道中最后一个操做的退出代码以肯定是否成功。在上面的示例中,只要 wc -l 命令执行成功,即便 wget 命令执行失败,此构建步骤也会成功并生成新镜像。

预先设置 set -o pipefail && 命令,可使管道中的任何一步发生错误时,都会致使命令执行失败,从而再也不构建镜像。例如:

RUN set -o pipefail && wget -O - https://some.site | wc -l > /number
  • 1

注意:并使是全部的 shell 都支持 -o pipefail 选项(好比 Debian 基础镜像中的默认 shell dash)。此时,可使用 RUN 的 exec 形式来显式选择一个支持 pipefail 选项的 shell。例如:

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]
  • 1

CMD

CMD 指令的 Dockerfile 参考资料

CMD 指令应该用来运行镜像中的软件,能够有任意多个参数。格式为:CMD [“executable”, “param1”, “param2”…]。所以,若是镜像用来运行服务,例如 Apache 和 Rails,能够经过 CMD ["apache2","-DFOREGROUND"] 来运行。事实上,全部的基于服务的镜像都推荐使用这种命令格式。

大多数状况下,CMD 须要交互式的 shell,例如 bash、Python 或 Perl。例如,CMD ["perl", "-de0"]CMD ["python"]或 CMD ["php", "-a"]。CMD 采用这种形式时,意味着当你执行相似 docker run -it python 这样的命令时能够直接进入到一个可用的 shell。除非您和您的预期用户已经很是熟悉 ENTRYPOINT 的工做方式,不然 CMD 应该不多以 CMD ["param", "param"] 和 ENTRYPOINT 的方式使用。

EXPOSE

EXPOSE 指令的 Dockerfile 参考资料

EXPOSE 指令指示开启容器的哪一个端口来监听链接。应该为应用程序使用通用的传统端口。例如,包含 Apache Web 服务器的镜像将使用EXPOSE 80,而包含 MongoDB 的映像将使用 EXPOSE 27017 等。

为了使外部能够访问,用户能够在执行 docker run 命令时使用标志将容器的某个端口映射到用户选择的端口。对于容器连接,Docker 为从服务容器返回到源的路径(即 MYSQL_PORT_3306_TCP)提供环境变量。(原文:For container linking, Docker provides environment variables for the path from the recipient container back to the source (ie, MYSQL_PORT_3306_TCP).)

ENV

ENV 指令的 Dockerfile 参考资料

要让新软件更容易运行,可使用 ENV 来更新容器中安装的软件的 PATH 环境变量。例如,ENV PATH /usr/local/nginx/bin:$PATH 能够确保 CMD ["nginx"] 正常工做。

经过 ENV 指令能够提供所需的环境变量,指示服务按照预期运行,例如 Postgres 的 PGDATA 环境变量。

最后,ENV 还可用于设置经常使用的版本号,使版本更容易维护,例以下面的例子:

ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
  • 1
  • 2
  • 3
  • 4

跟程序中的常量(而不是硬编码值)相似,此方法可以让你更改单个 ENV 指令,以自动的地处理容器中的软件版本。

跟 RUN 命令同样,每一个 ENV 行会建立一个新的中间层。这意味着即便在后面的层中 unset 环境变量,这个值仍然会持久化在这个层中,其值可能会丢弃。能够经过建立相似下面的 Dockerfile 而且构建镜像来测试一下:

FROM alpine
ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER
CMD sh
  • 1
  • 2
  • 3
  • 4
  • 5
$ docker run --rm -it test sh echo $ADMIN_USER

mark
  • 1
  • 2
  • 3

在同一个层中使用带 shell 命令的 RUN 命令来 set、use 和 unset 变量能够避免这种状况,而且确保完全 unset 环境变量。能够经过分号 ; 或 && 来分隔命令。使用 && 时,任何一个命令执行失败都会致使镜像构建失败。这是个好主意。使用反斜线 \ 做为行继续符号,能够提升 Linux 中 Dockerfile 的可读性。能够把全部的命令放入一个 shell 脚本中,经过 RUN 命令直接运行这个脚本。

FROM alpine
RUN export ADMIN_USER="mark" \
    && echo $ADMIN_USER > ./mark \
    && unset ADMIN_USER
CMD sh
  • 1
  • 2
  • 3
  • 4
  • 5
$ docker run --rm -it test sh echo $ADMIN_USER
  • 1

ADD or COPY

ADD 指令的 Dockerfile 参考资料

COPY 指令的 Dockerfile 参考资料

ADD 和 COPY 在功能上类似,一般来讲优先使用 COPY。由于 COPY 比 ADD 更加清晰。COPY 只支持将本地文件复制到容器,而 ADD 有好几个不能一会儿区分出来的特性(像只支持本地的 tar 文件提取,远程 URL 支持)。所以,ADD 的最佳用途是将本地 tar 文件自动提取到镜像中,如 ADD rootfs.tar.xz /

若是 Dockerfile 中有多个步骤使用了上下文中的不一样文件,挨个使用 COPY 命令,而不是一次所有完成。这可确保每一个步骤的构建缓存仅在特定的所需文件发生更改时才会失效(强制从新运行该步骤)。

示例:

COPY requirements.txt /tmp/
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/
  • 1
  • 2
  • 3

上面的例子中,相比使用 COPY . /tmp/,用于 RUN 这一步的缓存更加不容易失效。

由于镜像大小的考虑,很是不建议经过 ADD 从远程 URL 获取包,可使用 curl 或 wget 来代替,这样能够删除在解压缩后再也不须要的文件,而且没必要在镜像中添加其余层。例如,避免使用下面的例子

ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
  • 1
  • 2
  • 3

相反,使用这个例子:

RUN mkdir -p /usr/src/things \
    && curl -SL http://example.com/big.tar.xz \
    | tar -xJC /usr/src/things \
    && make -C /usr/src/things all
  • 1
  • 2
  • 3
  • 4

对于其余不须要 ADD 的 tar 文件自动解压缩功能的时候,尽可能使用 COPY。

ENTRYPOINT

ENTRYPOINT 指令的 Dockerfile 参考资料

ENTRYPOINT 指令的最佳用途是设置镜像的主命令,容许该镜像像该命令同样运行(而后使用 CMD 做为默认标志)。

下面的镜像,ENTRYPOINT 设置为命令行工具 s3cmd:

ENTRYPOINT ["s3cmd"]
CMD ["--help"]
  • 1
  • 2

如今要查看命令的帮助能够这样运行:

$ docker run s3cmd
  • 1

或使用正确的参数来执行一次命令:

$ docker run s3cmd ls s3://mybucket
  • 1

这颇有用,由于如上面的命令所示,镜像名称能够做为对二进制文件的二次引用。

ENTRYPOINT 指令也能够与辅助脚本结合使用,即便启动工具可能须要多个步骤,也可使其与上述命令相似(封装到了脚本中)。

例如,Postgres 官方镜像 使用下面的脚本做为其 ENTRYPOINT:

#!/bin/bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意:这个脚本使用了 exec 这个 Bash 命令,所以最终运行的应用程序称为容器的 PID 1。这会容许应用程序接受任何发送到容器的 Unix 信号。更多信息参考 ENTRYPOINT

辅助脚本被复制到容器中,而且在容器启动时经过 ENTRYPOINT 运行:

COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
  • 1
  • 2

这个脚本容许用户使用多种方式同 Postgres 交互。

能够简单的启动 Postgres:

$ docker run postgres
  • 1

或者用来运行 Postgres 而且向服务器传参数:

$ docker run postgres postgres --help
  • 1

最后,还能够用来开启彻底不一样的工具,好比 Bash:

$ docker run --rm -it postgres bash
  • 1

VOLUME

VOLUME 指令的 Dockerfile 参考资料

VOLUME 指令应该用来暴露数据库存储区域、配置存储或 docker 容器建立的文件及文件夹。强烈建议将 VOLUME 用于镜像的任何可变部分和用户可用部分。

USER

USER 指令的 Dockerfile 参考资料

若是服务运行时不须要特权,使用 USER 指令切换为非 root 用户。在 Dockerfile 中经过相似 RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres 的命令建立用户和用户组。

注意:镜像中的用户和用户组会获得非肯定性的 UID/GID,由于无论镜像如何重建,“下一个”UID/GID 都会被分配。 因此,若是 UID/GID 很关键,就必须明确指定。

注意:因为 Go archive/tar 包处理稀疏文件(sparse files)时存在 未解决的错误,试图在 Docker 容器内建立具备足够大UID的用户可能致使磁盘耗尽,由于容器层中的 /var/log/faillog 文件会填满 NUL(\0)字符。 将 --no-log-init 标志传递给 useradd 能够解决此问题。 Debian/Ubuntu 的 adduser 不支持 --no-log-init 标志。

避免安装或使用 sudo,由于它具备可能致使问题的不可预知的 TTY 和信号转发行为。 若是须要与 sudo 相似的功能,例如以 root 身份初始化守护程序,但将其做为非 root 用户运行),请考虑使用 gosu

最后,为了减小层数和复杂性,避免频繁切换 USER。

WORKDIR

WORKDIR 指令的 Dockerfile 参考资料

应该始终为 WORKDIR 使用绝对路径来保证清晰可靠。另外,应该使用 WORKDIR 而不是像 RUN CD ... && do-something这样的繁琐指令,这些指令很难读懂、排除故障和维护。

ONBUILD

ONBUILD 指令的 Dockerfile 参考资料

ONBUILD 指令在所在的 Dockerfile 构建完成后执行。ONBUILD 在从当前镜像派生的任何子镜像中执行。能够将 ONBUILD 命令看做父 Dockerfile 给子 Dockerfile 的指令。

Docker 构建时会在执行子 Dockerfile 的任何命令以前执行 ONBUILD 命令。

ONBUILD 命令在从指定镜像构建新镜像时颇有用。例如,能够为语言堆栈镜像使用 ONBUILD,在 Dockerfile 中使用该语言编写任意用户软件,就像在 Ruby 的 ONBUILD 变体中看到的同样。

从 ONBUILD 构建的镜像应该有一个独立的标签,例如:ruby:1.9-onbuild 或 ruby:2.0-onbuild

在 ONBUILD 中使用 ADD 或 COPY 时须要当心。若是新构建的上下文缺乏所需资源,或致使 ONBUILD 的镜像构建失败。按照上面的建议添加一个单独的标签,经过容许 Dockerfile 做者作出选择能够帮助缓解这种状况。

3. 官方仓库示例

这些官方仓库具备示例性 Dockerfiles:

4. 附加资源

相关文章
相关标签/搜索