Dockerfile 优化实践笔记

多阶段构建(Multi-stage Build)

通过 AS builder 等分离构建阶段与运行阶段,只把构建产物和必要配置拷贝到最终镜像,最小化生产镜像、减少构建体积。

最终镜像中不会包含构建阶段的源码、开发依赖、构建工具等,仅包含运行阶段需要的产物与配置。

Dockerfile 复制代码
# ========== 阶段一:构建 ==========
FROM node:22-slim AS builder
WORKDIR /app

COPY . /app

RUN corepack enable pnpm && pnpm install && pnpm build

# ========== 阶段二:生产运行 ==========
FROM nginx:latest AS production

# 从 builder 阶段仅复制构建产物与 Nginx 配置
COPY --from=builder /app/web-ele/dist /etc/nginx/site_avaliable/vben-admin

# 后续部署内容

说明:

  • COPY --from=builder:从指定阶段复制文件到当前阶段,不引入 builder 的其它层。
  • 可通过 docker build --target <AS 阶段名称> 可以只构建到该阶段

启用 BuildKit 与缓存

BuildKit 是 Docker 官方下一代构建引擎,需 Docker 18.09 及以上,并在环境中显式启用。

主要能力:

  • 多阶段任务并行:根据依赖关系将无依赖任务并行执行,缩短总构建时间。
  • Dockerfile 变更下的持久化缓存 :通过 --mount=type=cache 将目录挂载到宿主机缓存卷,在依赖文件未变时复用缓存。
  • 导出中间产物 :通过 --output=type=local,dest=<路径> 将某阶段产物导出到本地,便于测试或复用。
  • 默认非 root :默认不以 root 运行,需时可用 --user=root。
  • 新指令 :常用如 --mount ,以及 --network、--platform 等。
    • --mount=type=cache,target=<缓存目录>:持久化缓存,多次构建共享。
    • --mount=type=secret,id=<id>,target=<路径>:注入构建期密钥,构建结束后不留在镜像中。
    • --mount=type=bind,source=<宿主机路径>,target=<容器路径>:挂载本地文件,多用于本地调试,不建议在生产构建中依赖。

1. 在宿主机或 CI 中启用 BuildKit

在宿主机或 CI 配置中设置环境变量即可启用;使用 docker-compose 构建时建议同时开启 COMPOSE_DOCKER_CLI_BUILD。

yaml 复制代码
# 例如 .gitlab-ci.yml
variables:
  DOCKER_BUILDKIT: 1          # 启用 BuildKit 引擎
  COMPOSE_DOCKER_CLI_BUILD: 1 # 使用 docker-compose build 时生效(可选)

2. 将依赖目录挂载为缓存卷

把包管理器缓存目录挂载为 BuildKit 缓存卷,可在锁文件未变时复用依赖,加速后续构建。

dockerfile 复制代码
# 语法:RUN --mount=type=cache,target=<缓存目录> <命令>
# pnpm:缓存指定 store 目录
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile

# npm:缓存 .npm 目录,build 时用 --cache 指定缓存路径
# RUN --mount=type=cache,target=/root/.npm \
#     npm build --cache /root/.npm

参数说明:

  • type=cache:使用 BuildKit 缓存卷。
  • id :缓存卷标识,不同 id 互不共享(可选,用于区分 pnpm/npm 等)。
  • target:容器内缓存目录路径,与包管理器实际使用的路径一致。

为什么能优化

  1. Docker 按层缓存:每条指令对应一层,前面任一层内容变化,后续层缓存都会失效。
  2. 常见写法是先 COPY . . 再 RUN pnpm install。项目文件一旦改动,COPY 层变化,安装层缓存即失效。
  3. 使用 BuildKit + --mount=type=cache 将 pnpm 的 store(或 npm 的 cache)挂到宿主机缓存卷后,即使项目文件变化,只要锁文件未变,仍可复用该卷中的依赖,从而加速安装。

注意:在 GitLab CI 等共享 Runner 上,同一 Runner 上的不同流水线会共享该缓存卷。

缓存失效条件

  • 使用的 target 路径或缓存卷标识(如 id)发生变化。
  • 当前 RUN 所依赖的上下文发生变化(例如安装所依赖的 package.json / lockfile 变化)。

Dockerfile 指令优化

  1. 把常变指令放在后面。前面尽量只放不常变的步骤(如先只复制依赖描述文件 → 执行安装 → 再复制源码),提高层缓存命中率。
  2. COPY 时避免 COPY . .。尽量只复制当前步骤需要的文件或目录,减少无关变更导致缓存失效。
  3. 合并 RUN 。用 && 将多条命令写在一个 RUN 中,减少层数,便于维护与缓存。
  4. 安装后清理。安装系统依赖后删除 apt 缓存、临时文件等,减小镜像体积。

.dockerignore

通过 .dockerignore 排除不需要参与构建的文件(如 node_modules、日志、编辑器配置等),可加快构建并避免把敏感或无关文件 COPY 进镜像。

执行时机 :在 docker build 时,Docker CLI 会先读取构建上下文目录下的 .dockerignore,过滤掉匹配的文件,再把剩余文件列表发给 Docker Daemon 参与构建;过滤发生在发送上下文之前,而不是在容器内执行时再过滤。

Docker CLI:接收并解析用户输入命令的客户端。

Docker Daemon:在宿主机上常驻的守护进程,负责实际构建与运行。

轻量镜像选择

优先选用轻量基础镜像,例如 node:*-slim 、nginx:alpine 等,只保留运行所需的最小依赖,可减小最终镜像体积并降低攻击面。

例如 Google 的 distroless 系列镜像仅包含应用运行时依赖,不含 shell、包管理器等,依赖越少,潜在漏洞面越小。

参考内容

BuildKit 介绍 - 知乎

相关推荐
m4Rk_6 分钟前
【论文阅读】Agent 记忆机制(83):Inside Out——用可演化 PersonaTree 构建 Agent 的核心长期记忆
论文阅读·人工智能·学习·开源·github
andyweike18 分钟前
php笔记
开发语言·笔记·php
鬓戈1 小时前
Rust 语言与 AI 应用生态调研及学习路径
人工智能·学习·rust
传奇开心果编程1 小时前
【SwiftUI娓娓道来】第3课:让界面活起来——交互与动画指南
学习·macos·ui·ios·swiftui·swift
xian_wwq1 小时前
【学习笔记】等保合规安全基线核查实战
笔记·学习
传奇开心果编程2 小时前
【Jetpack Compose进阶学与练】第14课:系列收尾复习总结;Compose项目常见坑点汇总;学习路线与后续学习方向
android·学习·ui·kotlin·android jetpack
yangmu32032 小时前
发动机是怎么把油变成动力的:四冲程内燃机原理拆解
学习
m4Rk_2 小时前
【论文阅读】Agent 记忆机制(81):EMR——用情景记忆避免 Agent 在多步推理中反复绕圈
论文阅读·人工智能·学习·开源·github
海海不掉头发3 小时前
阶段五_实验34-38_学习总结
深度学习·神经网络·学习·机器学习
M78佐菲3 小时前
ARM学习笔记(11)
linux·arm开发·笔记·嵌入式硬件·学习