如何利用 BuildKit 的 `--mount=type=cache` 加速 apt/yum 安装

解读

国内镜像构建普遍面临两大痛点:

  1. 每次 docker build 都会重新下载软件包,外网带宽不稳定导致 CI 超时;
  2. 多阶段构建或并行 Job 重复拉取相同依赖,浪费时间和存储。
    BuildKit 的 --mount=type=cache 可以把宿主机目录绑定到构建容器内部,实现跨构建、跨阶段共享缓存,从而把 apt/yum 的元数据和软件包持久化,显著缩短构建耗时。面试官想确认你是否:
  • 知道 BuildKit 在国内环境下的启用方式;
  • 能正确写出 Dockerfile 语法并解释缓存目录映射原理;
  • 理解权限、并发安全、缓存失效策略等生产细节。

知识点

  1. 启用 BuildKit
    • 环境变量 DOCKER_BUILDKIT=1 或 daemon.json "features": { "buildkit": true }
    • 国内 CI(Jenkins、GitLab Runner、阿里云 ACR、腾讯云 TCR)均默认开启,无需 root 改 daemon。
  2. 缓存挂载语法
    RUN --mount=type=cache,target=/var/cache/apt,sharing=locked ...
    • type=cache:标识缓存挂载;
    • target:容器内路径,需与 apt/yum 缓存路径一致;
    • sharing=locked:防止并行构建写冲突;
    • id:可选,显式命名缓存,便于多镜像共享;
    • mode/uid/gid:保证与安装用户一致,避免权限拒绝。
  3. apt 缓存路径
    /var/cache/apt/archives 存放 deb 包,/var/lib/apt/lists 存放索引;两者均需挂载。
  4. yum/dnf 缓存路径
    /var/cache/yum(CentOS 7)或 /var/cache/dnf(CentOS 8+),同时保留 /etc/yum.repos.d 不变即可。
  5. 缓存失效策略
    • BuildKit 默认永不过期,需手动 docker builder prune --filter type=cache 或设置 --mount=type=cache,id=xxx,timeout=24h
    • 国内云厂商的远程构建机每日清盘,可忽略手动清理。
  6. 与多阶段构建结合
    在 builder 阶段挂载缓存,最终阶段仅拷贝产物,缓存层不会进入镜像,既提速又保持镜像最小化。

答案

以国内 Debian 源为例,给出完整 Dockerfile 与构建命令:

# syntax=docker/dockerfile:1.4   # 必须放在第一行,启用 1.4 语法
FROM debian:11-slim AS builder
ARG DEBIAN_FRONTEND=noninteractive
RUN rm -f /etc/apt/apt.conf.d/docker-clean && \
    echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/99-no-languages
# 挂载缓存目录并锁定,防止并行冲突
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-cache \
    --mount=type=cache,target=/var/lib/apt/lists,sharing=locked,id=apt-lists \
    apt-get update && \
    apt-get install -y --no-install-recommends \
        build-essential \
        python3-dev \
        python3-pip && \
    apt-get clean

# 多阶段拷贝产物,缓存层不会带到最终镜像
FROM debian:11-slim
COPY --from=builder /usr/local/lib/python3.9/dist-packages /usr/local/lib/python3.9/dist-packages
CMD ["python3"]

构建命令:

export DOCKER_BUILDKIT=1
docker build -t myapp:1.0 .

效果:

  • 首次构建耗时 120 s,后续仅更新索引,耗时降至 8 s;
  • 缓存目录位于宿主机 /var/lib/docker/buildkit/cache镜像层不含缓存,体积不变;
  • 同一台宿主机上并行 Job 通过 sharing=locked 排队写,避免损坏。

对于 CentOS 7,只需替换路径:

RUN --mount=type=cache,target=/var/cache/yum,sharing=locked,id=yum-cache \
    yum install -y gcc make ...

拓展思考

  1. 国内源加速与缓存结合
    apt update 前把 /etc/apt/sources.list 换成清华、阿里、中科大源,再挂载缓存,可进一步节省索引下载时间;注意源地址变化会导致缓存失效,需统一在基础镜像层完成换源。
  2. CI 缓存跨节点共享
    自建 GitLab Runner 可配置 [[runners.docker]] helper_image = "docker/buildkit:latest",并把 /var/lib/docker/buildkit 挂载到 NFS 或 OSS,实现多节点共享同一份缓存,但需加 id 命名空间防止冲突。
  3. 安全加固
    • 缓存目录默认 755,若公司安全基线要求 700,可在挂载后加 RUN chmod 700 /var/cache/apt
    • 对公网构建机,建议给缓存目录加 node.unique 后缀,防止不同租户横向读取缓存包。
  4. 与 BuildKit 外置化缓存(gha、s3)对比
    国内云原生场景下,外置化缓存需额外开放公网出口,直接使用本地 type=cache 性价比最高;只有跨地域构建才考虑 gha/s3。
  5. 清理策略
    在 nightly pipeline 里加 docker builder prune --filter type=cache --keep-storage 5GB,防止宿主机 inode 耗尽;阿里 ACR 企业版已内置自动清理,无需干预。