挂载
本节介绍如何在 Docker 构建中使用缓存挂载和绑定挂载。
缓存挂载允许您指定在构建期间使用的持久化包缓存。持久化缓存有助于加快构建步骤,尤其是涉及使用包管理器安装包的步骤。对包拥有持久化缓存意味着即使您重新构建层,您也只需下载新包或更改的包。
缓存挂载是使用 --mount
标志与 Dockerfile 中的 RUN
指令一起创建的。要使用缓存挂载,标志的格式为 --mount=type=cache,target=<path>
,其中 <path>
是您希望挂载到容器中的缓存目录的位置。
添加缓存挂载
用于缓存挂载的目标路径取决于您使用的包管理器。本指南中的应用程序示例使用 Go 模块。这意味着缓存挂载的目标目录是 Go 模块缓存写入到的目录。根据 Go 模块参考,模块缓存的默认位置是 $GOPATH/pkg/mod
,$GOPATH
的默认值为 /go
。
更新下载包和编译程序的构建步骤,将 /go/pkg/mod
目录作为缓存挂载进行挂载
# 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
标志打印发生的下载执行。添加此标志可以让你在下一步看到缓存挂载是如何使用的。
重新构建镜像
在您重新构建镜像之前,清除您的构建缓存。这确保您是从一个干净的状态开始,从而更容易地确切地看到构建正在做什么。
$ docker builder prune -af
现在是时候重新构建镜像了。调用构建命令,这次使用 --progress=plain
标志,并将输出重定向到日志文件。
$ docker build --target=client --progress=plain . 2> log1.txt
构建完成后,检查 log1.txt
文件。日志显示 Go 模块是如何作为构建的一部分下载的。
$ awk '/proxy.golang.org/' log1.txt
#11 0.168 # get https://proxy.golang.org/github.com/charmbracelet/lipgloss/@v/v0.6.0.mod
#11 0.168 # get https://proxy.golang.org/github.com/aymanbagabas/go-osc52/@v/v1.0.3.mod
#11 0.168 # get https://proxy.golang.org/github.com/atotto/clipboard/@v/v0.1.4.mod
#11 0.168 # get https://proxy.golang.org/github.com/charmbracelet/bubbletea/@v/v0.23.1.mod
#11 0.169 # get https://proxy.golang.org/github.com/charmbracelet/bubbles/@v/v0.14.0.mod
#11 0.218 # get https://proxy.golang.org/github.com/charmbracelet/bubbles/@v/v0.14.0.mod: 200 OK (0.049s)
#11 0.218 # get https://proxy.golang.org/github.com/aymanbagabas/go-osc52/@v/v1.0.3.mod: 200 OK (0.049s)
#11 0.218 # get https://proxy.golang.org/github.com/containerd/console/@v/v1.0.3.mod
#11 0.218 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.0.mod
#11 0.219 # get https://proxy.golang.org/github.com/charmbracelet/bubbletea/@v/v0.23.1.mod: 200 OK (0.050s)
#11 0.219 # get https://proxy.golang.org/github.com/atotto/clipboard/@v/v0.1.4.mod: 200 OK (0.051s)
#11 0.219 # get https://proxy.golang.org/github.com/charmbracelet/lipgloss/@v/v0.6.0.mod: 200 OK (0.051s)
...
现在,为了查看缓存挂载是否正在使用,更改程序导入的 Go 模块之一的版本。通过更改模块版本,您将强制 Go 在下次构建时下载依赖项的新版本。如果您没有使用缓存挂载,您的系统将重新下载所有模块。但由于您添加了缓存挂载,Go 可以重用大多数模块,并且只下载 /go/pkg/mod
目录中不存在的包版本。
更新应用程序服务器组件使用的 chi
包的版本
$ docker run -v $PWD:$PWD -w $PWD golang:1.21-alpine \
go get github.com/go-chi/chi/[email protected]
现在,运行另一个构建,并将构建日志再次重定向到日志文件
$ docker build --target=client --progress=plain . 2> log2.txt
现在,如果您检查 log2.txt
文件,您将发现只有更改的 chi
包被下载
$ awk '/proxy.golang.org/' log2.txt
#10 0.143 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.mod
#10 0.190 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.mod: 200 OK (0.047s)
#10 0.190 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.info
#10 0.199 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.info: 200 OK (0.008s)
#10 0.201 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.zip
#10 0.209 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.zip: 200 OK (0.008s)
添加绑定挂载
您可以在 Dockerfile 中实现一些小的优化,以提高构建速度。目前,它使用 COPY
指令在下载模块之前拉取 go.mod
和 go.sum
文件。您可以使用绑定挂载,而不是将这些文件复制到容器的文件系统中。绑定挂载使容器可以直接从主机访问这些文件。此更改完全消除了对额外 COPY
指令(和层)的需求。
# 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-client
和 build-server
阶段为挂载当前工作目录指定绑定挂载。
# 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" ]
总结
本节展示了如何使用缓存和绑定挂载来提高构建速度。
相关信息
下一步
本指南的下一部分将介绍如何使用构建参数使您的构建可配置。