镜像构建最佳实践

镜像分层

使用该命令,您可以看到使用的命令 在镜像中创建每个图层。docker image history

  1. 使用命令可查看镜像中的图层 创建。docker image historygetting-started

    $ docker image history getting-started
    

    您应该获得如下所示的输出。

    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    a78a40cbf866        18 seconds ago      /bin/sh -c #(nop)  CMD ["node" "src/index.j…    0B                  
    f1d1808565d6        19 seconds ago      /bin/sh -c yarn install --production            85.4MB              
    a2c054d14948        36 seconds ago      /bin/sh -c #(nop) COPY dir:5dc710ad87c789593…   198kB               
    9577ae713121        37 seconds ago      /bin/sh -c #(nop) WORKDIR /app                  0B                  
    b95baba1cfdb        13 days ago         /bin/sh -c #(nop)  CMD ["node"]                 0B                  
    <missing>           13 days ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B                  
    <missing>           13 days ago         /bin/sh -c #(nop) COPY file:238737301d473041…   116B                
    <missing>           13 days ago         /bin/sh -c apk add --no-cache --virtual .bui…   5.35MB              
    <missing>           13 days ago         /bin/sh -c #(nop)  ENV YARN_VERSION=1.21.1      0B                  
    <missing>           13 days ago         /bin/sh -c addgroup -g 1000 node     && addu…   74.3MB              
    <missing>           13 days ago         /bin/sh -c #(nop)  ENV NODE_VERSION=12.14.1     0B                  
    <missing>           13 days ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B                  
    <missing>           13 days ago         /bin/sh -c #(nop) ADD file:e69d441d729412d24…   5.59MB   

    每条线代表镜像中的一个图层。此处的显示屏在底部显示底座,其中 顶部的最新图层。使用它,您还可以快速查看每个图层的大小,从而提供帮助 诊断大镜像。

  2. 您会注意到其中几行被截断。如果添加标志,您将获得 full 输出。--no-trunc

    $ docker image history --no-trunc getting-started
    

层缓存

现在您已经了解了分层的实际应用,需要学习重要的经验来帮助减少构建 时间。图层更改后,还必须重新创建所有下游图层。

查看您为入门应用程序创建的以下 Dockerfile。

# syntax=docker/dockerfile:1
FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]

返回镜像历史记录输出,您会看到 Dockerfile 中的每个命令都成为镜像中的一个新层。 您可能还记得,当您对镜像进行更改时,必须重新安装 yarn 依赖项。每次构建时都围绕相同的依赖项发布没有多大意义。

要修复它,你需要重新构建你的 Dockerfile 以帮助支持缓存 的依赖项。对于基于 Node 的应用程序,这些依赖项是定义的 在文件中。您只能先复制该文件,然后安装 dependencies,然后复制其他所有内容。然后,您只需重新创建纱线 dependencies (如果 .package.jsonpackage.json

  1. 首先更新 Dockerfile 以复制,安装依赖项,然后复制其他所有内容。package.json

    # syntax=docker/dockerfile:1
    FROM node:lts-alpine
    WORKDIR /app
    COPY package.json yarn.lock ./
    RUN yarn install --production
    COPY . .
    CMD ["node", "src/index.js"]
  2. 使用 构建新镜像。docker build

    $ docker build -t getting-started .
    

    您应该会看到如下所示的输出。

    [+] Building 16.1s (10/10) FINISHED
    => [internal] load build definition from Dockerfile
    => => transferring dockerfile: 175B
    => [internal] load .dockerignore
    => => transferring context: 2B
    => [internal] load metadata for docker.io/library/node:lts-alpine
    => [internal] load build context
    => => transferring context: 53.37MB
    => [1/5] FROM docker.io/library/node:lts-alpine
    => CACHED [2/5] WORKDIR /app
    => [3/5] COPY package.json yarn.lock ./
    => [4/5] RUN yarn install --production
    => [5/5] COPY . .
    => exporting to image
    => => exporting layers
    => => writing image     sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25
    => => naming to docker.io/library/getting-started
  3. 现在,对文件进行更改。例如,将 更改为 “The Awesome Todo App”。src/static/index.html<title>

  4. 现在再次使用 构建 Docker 镜像。这一次,您的输出看起来应该有点不同。docker build -t getting-started .

    [+] Building 1.2s (10/10) FINISHED
    => [internal] load build definition from Dockerfile
    => => transferring dockerfile: 37B
    => [internal] load .dockerignore
    => => transferring context: 2B
    => [internal] load metadata for docker.io/library/node:lts-alpine
    => [internal] load build context
    => => transferring context: 450.43kB
    => [1/5] FROM docker.io/library/node:lts-alpine
    => CACHED [2/5] WORKDIR /app
    => CACHED [3/5] COPY package.json yarn.lock ./
    => CACHED [4/5] RUN yarn install --production
    => [5/5] COPY . .
    => exporting to image
    => => exporting layers
    => => writing image     sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda
    => => naming to docker.io/library/getting-started

    首先,您应该注意到构建速度要快得多。而且,您将看到 这几个步骤使用的是以前缓存的图层。推拉 此镜像及其更新也将更快。

多阶段构建

多阶段构建非常强大 工具来帮助使用多个阶段创建镜像。他们有几个优点:

  • 将构建时依赖项与运行时依赖项分开
  • 通过仅提供应用程序运行所需的内容来减小整体镜像大小

Maven/Tomcat 示例

在构建基于 Java 的应用程序时,您需要 JDK 将源代码编译为 Java 字节码。然而 生产中不需要 JDK。此外,您可能正在使用 Maven 或 Gradle 等工具来帮助构建应用程序。 最终镜像中也不需要这些。多阶段构建有帮助。

# syntax=docker/dockerfile:1
FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package

FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 

在此示例中,您将使用一个阶段(称为)来使用 Maven 执行实际的 Java 构建。在第二个 stage (starting at ) 中,您可以从舞台中复制文件。最终镜像只是最后一个阶段 正在创建,可以使用该标志覆盖。buildFROM tomcatbuild--target

React 示例

在构建 React 应用程序时,您需要一个 Node 环境来编译 JS 代码(通常是 JSX)、SASS 样式表、 以及更多静态 HTML、JS 和 CSS 的知识。如果您不进行服务器端渲染,则甚至不需要 Node 环境 用于您的生产版本。您可以将静态资源放在静态 nginx 容器中。

# syntax=docker/dockerfile:1
FROM node:lts AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html

在前面的 Dockerfile 示例中,它使用镜像执行构建(最大化层缓存),然后复制输出 放入 nginx 容器中。node:lts

总结

在本节中,您学习了一些镜像构建最佳实践,包括层缓存和多阶段构建。

相关信息:

后续步骤

在下一节中,您将了解可用于继续了解容器的其他资源。