mirror of https://github.com/docker/docs.git
226 lines
8.4 KiB
Markdown
226 lines
8.4 KiB
Markdown
---
|
||
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 you’re
|
||
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 it’s 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 weren’t 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, you’ll 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 container’s 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" >}}
|