docs/content/build/guide/mounts.md

226 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Mounts
description: Introduction to cache mounts and bind mounts in builds
keywords: build, buildkit, buildx, guide, tutorial, mounts, cache mounts, bind mounts
---
This section describes how to use cache mounts and bind mounts with Docker
builds.
Cache mounts let you specify a persistent package cache to be used during
builds. The persistent cache helps speed up build steps, especially steps that
involve installing packages using a package manager. Having a persistent cache
for packages means that even if you rebuild a layer, you only download new or
changed packages.
Cache mounts are created using the `--mount` flag together with the `RUN`
instruction in the Dockerfile. To use a cache mount, the format for the flag is
`--mount=type=cache,target=<path>`, where `<path>` is the location of the cache
directory that you wish to mount into the container.
## Add a cache mount
The target path to use for the cache mount depends on the package manager youre
using. The application example in this guide uses Go modules. That means that
the target directory for the cache mount is the directory where the Go module
cache gets written to. According to the
[Go modules reference](https://go.dev/ref/mod#module-cache), the default
location for the module cache is `$GOPATH/pkg/mod`, and the default value for
`$GOPATH` is `/go`.
Update the build steps for downloading packages and compiling the program to
mount the `/go/pkg/mod` directory as a cache mount:
```diff
# syntax=docker/dockerfile:1
FROM golang:{{% param "example_go_version" %}}-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" ]
```
The `-x` flag added to the `go mod download` command prints the download
executions that take place. Adding this flag lets you see how the cache mount is
being used in the next step.
## Rebuild the image
Before you rebuild the image, clear your build cache. This ensures that you're
starting from a clean slate, making it easier to see exactly what the build is
doing.
```console
$ docker builder prune -af
```
Now its time to rebuild the image. Invoke the build command, this time together
with the `--progress=plain` flag, while also redirecting the output to a log
file.
```console
$ docker build --target=client --progress=plain . 2> log1.txt
```
When the build has finished, inspect the `log1.txt` file. The logs show how the
Go modules were downloaded as part of the build.
```console
$ 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)
...
```
Now, in order to see that the cache mount is being used, change the version of
one of the Go modules that your program imports. By changing the module version,
you're forcing Go to download the new version of the dependency the next time
you build. If you werent using cache mounts, your system would re-download all
modules. But because you've added a cache mount, Go can reuse most of the
modules and only download the package versions that doesn't already exist in the
`/go/pkg/mod` directory.
Update the version of the `chi` package that the server component of the
application uses:
```console
$ docker run -v $PWD:$PWD -w $PWD golang:{{% param "example_go_version" %}}-alpine \
go get github.com/go-chi/chi/v5@v5.0.8
```
Now, run another build, and again redirect the build logs to a log file:
```console
$ docker build --target=client --progress=plain . 2> log2.txt
```
Now if you inspect the `log2.txt` file, youll find that only the `chi` package
that was changed has been downloaded:
```console
$ 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)
```
## Add bind mounts
There are a few more small optimizations that you can implement to improve the
Dockerfile. Currently, it's using the `COPY` instruction to pull in the `go.mod`
and `go.sum` files before downloading modules. Instead of copying those files
over to the containers filesystem, you can use a bind mount. A bind mount makes
the files available to the container directly from the host. This change removes
the need for the additional `COPY` instruction (and layer) entirely.
```diff
# syntax=docker/dockerfile:1
FROM golang:{{% param "example_go_version" %}}-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" ]
```
Similarly, you can use the same technique to remove the need for the second
`COPY` instruction as well. Specify bind mounts in the `build-client` and
`build-server` stages for mounting the current working directory.
```diff
# syntax=docker/dockerfile:1
FROM golang:{{% param "example_go_version" %}}-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" ]
```
## Summary
This section has shown how you can improve your build speed using cache and bind
mounts.
Related information:
- [Dockerfile reference](../../engine/reference/builder.md#run---mount)
- [Bind mounts](../../storage/bind-mounts.md)
## Next steps
The next section of this guide is an introduction to making your builds
configurable, using build arguments.
{{< button text="Build arguments" url="build-args.md" >}}