マウント¶
このセクションでは、Docker の構築( docker build )で
キャッシュマウントでは、構築中に使用する固定されたパッケージのキャッシュを指定できます。
キャッシュマウントを作成するには、Dockerfile 内の RUN
命令で --mount
フラグと一緒に使います。キャッシュマウントを使うには、フラグの書式は --mount=type=cache,target=<path>
であり、 <path>
にはコンテナ内にマウントしたいキャッシュディレクトリの場所です。
キャッシュマウントの追加¶
キャッシュマウントの対象として使用するパスは、使用するパッケージマネージャに依存します。このガイドのアプリケーション例では Go モジュールを使います。つまり、キャッシュマウント対象のディレクトリとは、Go モジュールキャッシュが書き込む場所です。 Go モジュール リファレンス に従うと、モジュールキャッシュに使うデフォルトの場所は $GOPATH/pkg/mod
であり、 $GOPATH
用のデフォルト値は /go
です。
パッケージのダウンロードとプログラムをコンパイルする構築ステップを書き換え、キャッシュマウントとして /go/pkg/mod
ディレクトリをマウントします。
# syntax=docker/dockerfile:1
FROM golang:{{site.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" ]
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:{{site.example_go_version}}-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.mod
と go.sum
ファイルを取得します。これらのファイルをコンテナのファイルシステムを通してコピーするのではなく、バインドマウントが利用できます。バインドマウントはホストから直接コンテナでファイルを利用できるようにします。この変更は COPY
命令(とレイヤ)を追加する必要を完全に除去します。
# syntax=docker/dockerfile:1
FROM golang:{{site.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" ]
同様に、同じテクニックを使い2つめの COPY
命令も同じく不要にできます。 build-client
と build-server
ステージ内で、現在の作業ディレクトリをバインドマウントとして指定します。
# syntax=docker/dockerfile:1
FROM golang:{{site.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" ]