掛載

本節介紹如何在 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/v5@v5.0.8

現在,執行另一個構建,並將構建日誌再次重定向到日誌檔案

$ 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.modgo.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-clientbuild-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" ]

總結

本節展示瞭如何使用快取和繫結掛載來提高構建速度。

相關資訊

下一步

本指南的下一部分將介紹如何使用構建引數使您的構建可配置。