Featured image of post 最大化 Docker 构建缓存以加快部署管道Featured image of post 最大化 Docker 构建缓存以加快部署管道

最大化 Docker 构建缓存以加快部署管道

以逻辑方式构建 Dockerfile 以缓存包恢复,利用 BuildKit 包安装来加速 CI/CD 运行。

无论您是想加快本地迭代容器的运行速度,还是旨在缩短 CI/CD 管道的时间,优化 Docker 映像构建速度 都是工程生产力的一个重要方面。较长的构建等待时间会扰乱开发人员的注意力并推高云计算成本。

优化构建速度的最强大、最简单的方法之一是配置 Docker 文件以最大限度地利用 Docker 构建缓存

在本文中,我们将研究 Docker 的层缓存机制如何工作,并探索先进技术(包括依赖关系恢复缓存和 BuildKit 缓存挂载),以使您的容器构建速度快如闪电。


1.理解Docker的分层缓存机制

Docker 镜像由一系列只读层组成,每个层代表 Dockerfile 中的一条指令(例如 FROMRUNCOPY)。

在构建过程中,Docker 检查每个层是否有现有的缓存。但是,一旦 Docker 检测到某个层的修改(例如更改的文件或更新的参数),它就会使该特定层的缓存失效,并且该层下面的所有后续层都必须从头开始重建。

为了防止过早的缓存破坏,您应该根据以下黄金规则构建 Dockerfile:

“将稳定、不经常更改的指令(例如系统包和依赖项)放置在 Dockerfile 顶部附近,并将高度动态的指令(例如复制应用程序源代码)放置在底部附近。”


2. 依赖恢复分裂模式

Node.js、Python 或 Rust 容器设置中的一个常见反模式是在执行依赖项安装之前复制整个项目工作区。

反模式:

FROM node:20-alpine
WORKDIR /app
# Copies everything, including rapidly changing source files
COPY . .
# Runs npm install on every single minor source file update
RUN npm install
CMD ["npm", "start"]

在上面的示例中,即使更改源代码中的单个字符也会使 COPY . . 的缓存失效。因此,Docker 被迫再次运行 RUN npm install,浪费时间下载依赖项。

缓存优化:

FROM node:20-alpine
WORKDIR /app

# Copy ONLY dependency manifests first
COPY package.json package-lock.json ./

# Execute installation. This layer is fully cached unless packages change
RUN npm ci

# Copy the rest of the source files
COPY . .

CMD ["npm", "start"]

通过将 package.json 副本和 npm ci 隔离在其余应用程序文件之前,可以确保仅在添加或删除实际包时才获取依赖项。


3. 利用 BuildKit 缓存挂载 (--mount=type=cache)

虽然隔离依赖文件可以防止源文件更改时的缓存破坏,但修改 package.json 仍然会强制 Docker 运行完整、干净的安装。

为了解决这个问题,BuildKit引入了缓存挂载(--mount=type=cache。此功能允许您将目录(例如全局 npm 缓存或 pip 下载缓存)声明为跨构建的持久目录。

使用 Node.js 和 npm 的示例:

# syntax=docker/dockerfile:1
FROM node:20-alpine
WORKDIR /app

COPY package.json package-lock.json ./

# Cache npm cache directory across builds
RUN --mount=type=cache,target=/root/.npm \
    npm ci

COPY . .

使用 Python 和 pip 的示例:

# syntax=docker/dockerfile:1
FROM python:3.11-slim
WORKDIR /app

COPY requirements.txt ./
# Mount pip's cache directory
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

COPY . .

即使 package.jsonrequirements.txt 发生变化,构建引擎也会重用存储在缓存挂载目标中的下载包,从而在几秒钟而不是几分钟内解决依赖关系。


4. 制作坚固的 .dockerignore

每当构建命令运行时,客户端都会将 karentto 目录内容(构建上下文)传输到 Docker 守护进程。如果您的目录包含大量构建工件、日志或本地模块文件夹(如 node_modules),则此传输会减慢构建开始时间并导致意外的缓存失效。

始终指定 .dockerignore 文件:

.git
node_modules
dist
build
.env*
*.log
Docker/
README.md

结论

最大化 Docker 的构建缓存是一项高收益的配置更改。经过:

  1. 从最不常更改到最常更改的顺序排列指令,
  2. 在应用程序源代码之前复制依赖项清单文件,
  3. 利用 BuildKit 缓存挂载 (--mount=type=cache),以及
  4. 通过 .dockerignore 过滤掉不相关的文件,

您可以显着减少部署时间和开发人员的摩擦。