DockerイメージをCIでキャッシュするなら知っておきたいDocker Buildの基礎知識
TL;DR
CIでビルドキャッシュを利用するにはファイルシステムの適当な箇所にビルドキャッシュを入出力する必要がある。それをするには、Buildx + BuildKit + docker-container driver の3点セットが必要になる。しかし2024年8月現在Docker Compose Buildはこれらを利用していないので、明示的に利用させなければならない。
Docker 23以降のdocker buildの仕組み
Docker Engineは2023年2月にv23を迎えた。
ここで、ビルドのアーキテクチャ標準が変更になり、ビルドクライアントのBuildxとビルドバックエンドのBuildKitが標準化された。
これはつまり、 docker build
コマンドを実行したときデフォルトで上記らが利用されるようになったということだ。Docker 23以前とそのあとで、 docker build
で実行されるビルド体系はリプレイスされたのである。
Docker Composeではどうか?
一方、 docker compose build
コマンドでビルドする場合には、これらは利用されず、従来形でビルドされる。
ビルドキャッシュの入出力の指定
なぜビルドアーキテクチャを知る必要があるかを理解するために、一旦ビルドキャッシュをファイルシステムに出力する方法について説明する。
ビルドキャッシュの入出力の指定はdocker buildのオプションでできる。
% docker build --help
...
--cache-from stringArray External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")
--cache-to stringArray Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir")
...
/path/to/dir
でキャッシュの入出力を行う場合は下記の通り。
docker build --cache-from=type=local,src=/path/to/dir --cache-to=type=local,dest=/path/to/dir ...
ローカル環境 & docker buildでやってみる
ということでまずはdocker buildコマンドでビルドしてみる。
しかし筆者の環境で徐に実行したところ以下のエラーが出た。
Cache export is not supported for the docker driver.
Switch to a different driver, or turn on the containerd image store, and try again.
Learn more at https://docs.docker.com/go/build-cache-backends
このオプションは docker
driverは利用できないとのこと。よって、別のdriverでビルドする。
driverの詳細な説明は公式サイトに任せるが、cache exportを使うには docker
ドライバではなく docker-container
ドライバを利用する必要がある。 docker
は Docker デーモンに含まれている従来型のドライバで、 docker-container
は BuildKitをフルに活用する新しい形のドライバである。
ビルダの一覧を確認してみる。
% docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
default docker
\_ default \_ default running v0.15.0 linux/arm64, linux/amd64, linux/amd64/v2, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
desktop-linux* docker
\_ desktop-linux \_ desktop-linux running v0.15.0 linux/arm64, linux/amd64, linux/amd64/v2, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
2つあるビルダはどちらも docker
ドライバを利用している。
これでは cache-from
cache-to
オプションが利用できないので、ビルダを新規に作成する。
$ docker buildx create --name mybuilder --driver docker-container
...
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
mybuilder docker-container
\_ mybuilder0 \_ desktop-linux running v0.15.2 linux/arm64, linux/amd64, linux/amd64/v2, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
default docker
\_ default \_ default running v0.15.0 linux/arm64, linux/amd64, linux/amd64/v2, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
desktop-linux* docker
\_ desktop-linux \_ desktop-linux running v0.15.0 linux/arm64, linux/amd64, linux/amd64/v2, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
docker-containerをドライバとして利用するビルダを作成できた。
これで改めてビルドするとキャッシュを出力することができた。
docker buildx build --builder mybuilder --cache-from=type=local,src=/path/to/dir --cache-to=type=local,dest=/path/to/dir ...
docker compose ではどうか
ここまでで説明している通り、 docker compose build
ではbuildx等を使っておらず、ビルダーのドライバも従来型で、そのままではキャッシュのファイル出力ができない。
ここで代わりに登場するのが docker buildx bake
コマンドである。
bake は buildx を使ったコンテナオーケストレーション用のコマンドである。
bakeの設定はHCL形式で記述するのだが、docker-compose.ymlとも互換性があり、docker compose buildコマンドを代替することができる。
その際のコマンドは以下のようになる。
docker buildx build \
--builder mybuilder \
--cache-from=type=local,src=/path/to/cache \
--cache-to=type=local,dest=/path/to/cache,mode=max \
-f docker-compose.yml
GitHub Actions & Docker Composeで利用してみる
以下のような流れとなる。
- action/cache で特定のディレクトリをキャッシュするようにする
- buildx をセットアップする
- docker buildx bake を実行する。その時のキャッシュの入出力先を(1)で指定したディレクトリとする。
これを実現するために、GitHub Actionsでは docker/setup-buildx-action
というカスタムアクションが提供されている。これに則ることでビルドキャッシュを利用できる。
また、キャッシュの設定をdocker-compose.ymlに記述する。
services:
app:
build:
context: .
cache_from:
- type=local,src=/tmp/.buildx-cache
cache_to:
- type=local,dest=/tmp/.buildx-cache
...
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: docker-build-cache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
docker-build-cache-${{ github.ref }}
docker-build-cache-
- name: Setup Apps
run: |
# docker compose build を以下のコマンドに置換
docker buildx bake --file docker-compose.yml --builder="${{ steps.buildx.outputs.name }}"
# 以下、docker compose up などコンテナを起動するコマンドに続く
...
[補足]キャッシュの内容を見てみる
ローカル環境で実行したので、キャッシュの内容を見てみる。
blobsディレクトリにイメージのレイヤーごとのキャッシュが保存されているのがわかる。
index.jsonは以下のようなメタデータが記述されている。
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:c6fc9d593e92059b058b0b147be8e51...",
"size": 4193,
"annotations": {
"org.opencontainers.image.ref.name": "latest"
}
}
]
}
[蛇足]おわりに
本記事ではGitHub Actions で docker compose build で生成されるイメージをキャッシュしてみたくなった筆者が、実際にキャッシュできるようになるまでに最低限身に付けた知識である。結論から言うとキャッシュしてもコンテナの起動までの時間が早くなることはなかったのでこのアイデアは不採用に終わったが、またどこかで使うことがあるかもしれないので備忘録として残しておく。
株式会社SODAの開発組織がお届けするZenn Publicationです。 是非Entrance Bookもご覧ください! → recruit.soda-inc.jp/engineer
Discussion