使用构建缓存
原文:https://docs.docker.com/guides/docker-concepts/building-images/using-the-build-cache/
解释
考虑以下您为入门应用创建的 Dockerfile。
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "./src/index.js"]
当您运行docker build
命令创建新镜像时,Docker 会执行 Dockerfile 中的每个指令,为每个命令创建一个层,并按照指定的顺序。对于每个指令,Docker 检查是否可以重用之前构建的指令。如果发现您之前已经执行过类似的指令,Docker 就不需要重新执行。相反,它会使用缓存的结果。这样,您的构建过程将变得更快更高效,为您节省宝贵的时间和资源。
有效使用构建缓存可以让您通过重用之前构建的结果和跳过不必要的工作来实现更快的构建。 为了最大化缓存使用并避免资源密集型和耗时的重建,了解缓存失效的工作原理非常重要。 以下是一些可能导致缓存失效的情况示例:
任何
RUN
指令的命令更改都会使该层失效。如果 Dockerfile 中的RUN
命令有任何修改,Docker 会检测到更改并使构建缓存失效。使用
COPY
或ADD
指令复制到镜像中的文件发生任何更改。Docker 会监视项目目录中的任何文件更改。无论是内容还是属性(如权限)的更改,Docker 都将这些修改视为触发缓存失效的因素。一旦一层失效,所有后续层也将失效。如果任何之前的层,包括基础镜像或中间层,因更改而失效,Docker 将确保依赖它的后续层也失效。这保持了构建过程的同步,防止了不一致。
当您编写或编辑 Dockerfile 时,留意不必要的缓存未命中,以确保构建尽可能快速高效地运行。
试一试
在这个动手指南中,您将学习如何有效地使用 Docker 构建缓存来构建 Node.js 应用程序。
构建应用程序
下载并安装 Docker Desktop。
打开终端并克隆这个示例应用程序。
consolegit clone https://github.com/dockersamples/todo-list-app
导航到
todo-list-app
目录:consolecd todo-list-app
在此目录中,您将找到一个名为
Dockerfile
的文件,内容如下:dockerfileFROM node:20-alpine WORKDIR /app COPY . . RUN yarn install --production EXPOSE 3000 CMD ["node", "./src/index.js"]
执行以下命令构建 Docker 镜像:
consoledocker build .
构建过程的结果如下:
console[+] Building 20.0s (10/10) FINISHED
第一次构建可能需要一些时间,因为它需要安装依赖。
不做任何更改重新构建。
现在,重新运行
docker build
命令,不对源代码或 Dockerfile 进行任何更改,如下所示:consoledocker build .
初始构建后的后续构建由于缓存机制而变得更快,只要命令和上下文保持不变。Docker 缓存了构建过程中生成的中间层。当您在不对 Dockerfile 或源代码进行任何更改的情况下重新构建镜像时,Docker 可以重用缓存的层,显著加快构建过程。
console[+] Building 1.0s (9/9) FINISHED docker:desktop-linux => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 187B 0.0s ... => [internal] load build context 0.0s => => transferring context: 8.16kB 0.0s => CACHED [2/4] WORKDIR /app 0.0s => CACHED [3/4] COPY . . 0.0s => CACHED [4/4] RUN yarn install --production 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => exporting manifest
后续构建只用了 1.0 秒,通过利用缓存的层完成。无需重复像安装依赖这样耗时的步骤。
更新 Dockerfile,首先复制
package.json
文件,安装依赖,然后复制其他所有内容。dockerfileFROM node:20-alpine WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install --production COPY . . EXPOSE 3000 CMD ["node", "src/index.js"]
在与 Dockerfile 同一文件夹中创建一个名为
.dockerignore
的文件,内容如下:plaintextnode_modules
构建新镜像:
consoledocker build .
您将看到类似以下的输出:
console[+] Building 16.1s (10/10) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 175B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/node:21-alpine 0.0s => [internal] load build context 0.8s => => transferring context: 53.37MB 0.8s => [1/5] FROM docker.io/library/node:21-alpine 0.0s => CACHED [2/5] WORKDIR /app 0.0s => [3/5] COPY package.json yarn.lock ./ 0.2s => [4/5] RUN yarn install --production 14.0s => [5/5] COPY . . 0.5s => exporting to image 0.6s => => exporting layers 0.6s => => writing image sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25 0.0s => => naming to docker.io/library/node-app:2.0 0.0s
您将看到所有层都被重建了。这很好,因为您对 Dockerfile 做了很多修改。
现在,更改
src/static/index.html
文件(例如更改标题为“The Awesome Todo App”)。构建 Docker 镜像。这次,您的输出应该看起来有所不同。
consoledocker build -t node-app:3.0 .
然后您将看到类似以下的输出:
console[+] Building 1.2s (10/10) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/node:21-alpine 0.0s => [internal] load build context 0.2s => => transferring context: 450.43kB 0.2s => [1/5] FROM docker.io/library/node:21-alpine 0.0s => CACHED [2/5] WORKDIR /app 0.0s => CACHED [3/5] COPY package.json yarn.lock ./ 0.0s => CACHED [4/5] RUN yarn install --production 0.0s => [5/5] COPY . . 0.5s => exporting to image 0.3s => => exporting layers 0.3s => => writing image sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda 0.0s => => naming to docker.io/library/node-app:3.0 0.0s
首先,您应该注意到构建速度更快了。您将看到几个步骤正在使用之前缓存的层。这是个好消息;您正在使用构建缓存。推送和拉取这个镜像及其更新也会更快。
通过遵循这些优化技巧,您可以使 Docker 构建更快、更高效,从而加快迭代周期,提高开发效率。