Skip to content

挂载

原文:https://docs.docker.com/build/guide/mounts/

本节介绍如何在 Docker 构建中使用缓存挂载和绑定挂载。

缓存挂载允许您指定在构建期间使用的持久化包缓存。持久化缓存有助于加速构建步骤,尤其是涉及使用包管理器安装包的步骤。拥有包的持久化缓存意味着即使您重新构建层,也只需下载新的或更改的包。

使用 Dockerfile 中的 RUN 指令和 --mount 标志一起创建缓存挂载。使用缓存挂载的格式为 --mount=type=cache,target=<path>,其中 <path> 是您希望挂载到容器中的缓存目录位置。

添加缓存挂载

用于缓存挂载的目标路径取决于您使用的包管理器。本指南中的应用示例使用 Go 模块。这意味着缓存挂载的目标目录是写入 Go 模块缓存的目录。根据 Go 模块参考,模块缓存的默认位置是 $GOPATH/pkg/mod$GOPATH 的默认值是 /go

更新下载包和编译程序的构建步骤,将 /go/pkg/mod 目录挂载为缓存挂载:

diff
  # syntax=docker/dockerfile:1
  FROM golang:1.21-alpine AS base
  WORKDIR /src
  COPY go.mod go.sum .
- RUN go mod download
+ RUN --mount=type=cache,target=/go/pkg/mod/ \
+     go mod download -x
  COPY . .

  FROM base AS build-client
- RUN go build -o /bin/client ./cmd/client
+ RUN --mount=type=cache,target=/go/pkg/mod/ \
+     go build -o /bin/client ./cmd/client

  FROM base AS build-server
- RUN go build -o /bin/server ./cmd/server
+ RUN --mount=type=cache,target=/go/pkg/mod/ \
+     go build -o /bin/server ./cmd/server

  FROM scratch AS client
  COPY --from=build-client /bin/client /bin/
  ENTRYPOINT [ "/bin/client" ]

  FROM scratch AS server
  COPY --from=build-server /bin/server /bin/
  ENTRYPOINT [ "/bin/server" ]

添加到 go mod download 命令的 -x 标志打印发生的下载操作。添加此标志可让您在下一步中看到缓存挂载的使用情况。

重建镜像

在重新构建镜像之前,清除您的构建缓存。这确保您从干净的状态开始,便于准确看到构建在做什么。

console
docker builder prune -af

现在是时候重新构建镜像了。这次在调用构建命令时,一起使用 --progress=plain 标志,并将输出重定向到日志文件。

console
docker build --target=client --progress=plain . 2> log1.txt

构建完成后,检查 log1.txt 文件。日志显示了构建过程中下载的 Go 模块。

console
awk '/proxy.golang.org/' log1.txt
# 许多相关输出
...

为了看到缓存挂载正在被使用,更改程序导入的其中一个 Go 模块的版本。通过更改模块版本,您迫使 Go 在下次构建时下载依赖项的新版本。如果您没有使用缓存挂载,您的系统将重新下载所有模

块。但是因为您添加了缓存挂载,Go 可以重用大部分模块,并且只下载 /go/pkg/mod 目录中不存在的包版本。

更新应用中服务器组件使用的 chi 包的版本:

console
docker run -v $PWD:$PWD -w $PWD golang:1.21-alpine \
    go get github.com/go-chi/chi/v5@v5.0.8

现在,再次运行构建,并将构建日志重定向到日志文件:

console
docker build --target=client --progress=plain . 2> log2.txt

如果您检查 log2.txt 文件,您会发现只有更改过的 chi 包被下载了:

console
awk '/proxy.golang.org/' log2.txt
# 相关输出
...

添加绑定挂载

您还可以实现一些更小的优化以改善 Dockerfile。当前,它使用 COPY 指令拉取 go.modgo.sum 文件,然后下载模块。您可以使用绑定挂载代替将这些文件复制到容器的文件系统。绑定挂载使容器可以直接从主机访问这些文件。这种更改完全消除了额外的 COPY 指令(和层)的需要。

diff
  # syntax=docker/dockerfile:1
  FROM golang:1.21-alpine AS base
  WORKDIR /src
- COPY go.mod go.sum .
  RUN --mount=type=cache,target=/go/pkg/mod/ \
+     --mount=type=bind,source=go.sum,target=go.sum \
+     --mount=type=bind,source=go.mod,target=go.mod \
      go mod download -x
  COPY . .

  FROM base AS build-client
  RUN --mount=type=cache,target=/go/pkg/mod/ \
      go build -o /bin/client ./cmd/client

  FROM base AS build-server
  RUN --mount=type=cache,target=/go/pkg/mod/ \
      go build -o /bin/server ./cmd/server

  FROM scratch AS client
  COPY --from=build-client /bin/client /bin/
  ENTRYPOINT [ "/bin/client" ]

  FROM scratch AS server
  COPY --from=build-server /bin/server /bin/
  ENTRYPOINT [ "/bin/server" ]

类似地,您可以使用相同的技术删除第二个 COPY 指令的需求。在 build-clientbuild-server 阶段指定绑定挂载,用于挂载当前工作目录。

diff
  # syntax=docker/dockerfile:1
  FROM golang:1.21-alpine AS base
  WORKDIR /src
  RUN --mount=type=cache,target=/go/pkg/mod/ \
      --mount=type=bind,source=go.sum,target=go.sum \
      --mount=type=bind,source=go.mod,target=go.mod \
      go mod download -x
- COPY . .

  FROM base AS build-client
  RUN --mount=type=cache,target=/go/pkg/mod/ \
+     --mount=type=bind,target=. \
      go build -o /bin/client ./cmd/client

  FROM base AS build-server
  RUN --mount=type=cache,target=/go/pkg/mod/ \
+     --mount=type=bind,target=. \
      go build -o /bin/server ./cmd/server

  FROM scratch AS client
  COPY --from=build-client /bin/client /bin/
  ENTRYPOINT [ "/bin/client" ]

  FROM scratch AS server
  COPY --from=build-server /bin/server /bin/
  ENTRYPOINT [ "/bin/server" ]

总结

本节展示了如何使用缓存挂载和绑定挂载提高构建速度。

相关信息:

下一步

本指南的下一部分是介绍如何使用构建参数使您的构建可配置。