docker buildを--cache-to/--cache-fromで高速化したかったが、docker-containerが必要になるようだ

Dockerを使用した開発やCI/CDパイプラインでは、ビルド時間の短縮が重要な課題となります。本記事では、`docker buildx`コマンドの`--cache-to`および`--cache-from`オプションを使用して、ビルドプロセスを高速化する方法を詳しく紹介します。

## キャッシュオプションの概要

`--cache-to`と`--cache-from`オプションは、Dockerのビルドキャッシュを外部に保存し、再利用するための機能です。これらのオプションは、特にCI/CDパイプラインでの利用が有効で、異なるマシンや環境間でキャッシュを共有することでビルド時間を大幅に短縮できます。

## 実践：キャッシュオプションの使用

### 1. デフォルトドライバーでの試行

まず、デフォルトの`docker`ドライバーで`--cache-to`オプションを試してみましょう。

```bash
docker buildx build -t test --cache-to type=local,dest=/tmp/foo,mode=max .
```

このコマンドを実行すると、次のようなエラーが発生します：

> ERROR: 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`ドライバーがキャッシュエクスポートをサポートしていないことを示しています。

現在のドライバーの情報を確認してみましょう：

```bash
docker buildx inspect
```

<details>
<summary>docker buildx inspect の出力結果（クリックして展開）</summary>

```
Name:          default
Driver:        docker
Last Activity: 2024-07-16 04:12:23 +0000 UTC

Nodes:
Name:             default
Endpoint:         default
Status:           running
BuildKit version: v0.13.2
Platforms:        linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
Labels:
 org.mobyproject.buildkit.worker.moby.host-gateway-ip: 172.17.0.1
GC Policy rule#0:
 All:           false
 Filters:       type==source.local,type==exec.cachemount,type==source.git.checkout
 Keep Duration: 48h0m0s
 Keep Bytes:    4.145GiB
GC Policy rule#1:
 All:           false
 Keep Duration: 1440h0m0s
 Keep Bytes:    30GiB
GC Policy rule#2:
 All:        false
 Keep Bytes: 30GiB
GC Policy rule#3:
 All:        true
 Keep Bytes: 30GiB
```

</details>

dockerドライバーはDocker組み込みのシンプルなドライバーらしく、試したかぎり`--cache-to`オプションのうち`type=local`や`type=registry`などは使えないようです。

### 2. docker-containerドライバーの使用

キャッシュのインポート・エクスポートを行うには、`docker-container`ドライバーを使用する必要があります。

```bash
docker buildx create --name docker-container --driver docker-container --bootstrap --use
```

このコマンドで使用しているオプションの説明：

- `--name docker-container`: 新しく作成するビルダーインスタンスに名前を付けます。ここでは "docker-container" という名前を使用しています。
- `--driver docker-container`: ビルダーインスタンスに使用するドライバーを指定します。ここでは docker-container ドライバーを使用します。
- `--bootstrap`: ビルダーインスタンスを作成後、すぐに初期化します。これにより、ビルダーがすぐに使用可能な状態になります。
- `--use`: 新しく作成したビルダーインスタンスをデフォルトのビルダーとして設定します。これにより、以降の `docker buildx` コマンドでこのビルダーが使用されます。


:::note info

ちなみに、GitHub Actionsでは、`@docker/setup-buildx-action`を使えば、これが`docker buildx create ...`しておいてくれるので便利です。

https://github.com/docker/setup-buildx-action

:::

作成したビルダーの情報を確認します：

```bash
docker buildx inspect
```

<details>
<summary>docker-containerドライバーの詳細情報（クリックして展開）</summary>

```
Name:          docker-container
Driver:        docker-container
Last Activity: 2024-07-16 04:18:00 +0000 UTC

Nodes:
Name:                  docker-container0
Endpoint:              unix:///var/run/docker.sock
Status:                running
BuildKit daemon flags: --allow-insecure-entitlement=network.host
BuildKit version:      v0.14.1
Platforms:             linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
Labels:
 org.mobyproject.buildkit.worker.executor:         oci
 org.mobyproject.buildkit.worker.hostname:         8eb0e95840a7
 org.mobyproject.buildkit.worker.network:          host
 org.mobyproject.buildkit.worker.oci.process-mode: sandbox
 org.mobyproject.buildkit.worker.selinux.enabled:  false
 org.mobyproject.buildkit.worker.snapshotter:      overlayfs
GC Policy rule#0:
 All:           false
 Filters:       type==source.local,type==exec.cachemount,type==source.git.checkout
 Keep Duration: 48h0m0s
 Keep Bytes:    488.3MiB
GC Policy rule#1:
 All:           false
 Keep Duration: 1440h0m0s
 Keep Bytes:    16.76GiB
GC Policy rule#2:
 All:        false
 Keep Bytes: 16.76GiB
GC Policy rule#3:
 All:        true
 Keep Bytes: 16.76GiB
```

</details>

### 3. キャッシュを使用したビルド

新しいドライバーで`--cache-to`オプションを使用してビルドします：

```bash
docker buildx build -t test --cache-to type=local,dest=/tmp/foo,mode=max .
```

<details>
<summary>ビルドログ（クリックして展開）</summary>

TODO: ログを記入

</details>

ビルドが成功し、キャッシュが`/tmp/foo`ディレクトリにエクスポートされました。

エクスポートされたキャッシュの構造は以下のようになっています：

```
/tmp/foo
├── blobs
│  └── sha256
│     ├── 4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1
│     ├── 9c704ecd0c694c4cbdd85e589ac8d1fc3fd8f890b7f3731769a5b169eb495809
│     ├── 9e01090d6864bc14898fc02d68dfbc80442f934fbffa163f0fb29e8d24175f25
│     └── f5de93f62cd6110aaf32524087c96664340c9841e2980371cf1479d1ec6be2b6
├── index.json
├── ingest
└── oci-layout
```

### 4. キャッシュを利用したビルド

エクスポートしたキャッシュを使用してビルドを行います。まず、既存のビルドキャッシュをクリアします。

```bash
docker buildx prune
```

次に、`--cache-from`オプションを使用して、先ほどエクスポートしたキャッシュを利用しビルドします。

```bash
docker buildx build -t test \
    --cache-to type=local,dest=/tmp/foo,mode=max \
    --cache-from type=local,src=/tmp/foo .
```

<details>
<summary>キャッシュを利用したビルドログ（クリックして展開）</summary>

```
[+] Building 2.2s (7/7) FINISHED                                                             docker-container:docker-container
 => [internal] load build definition from Dockerfile                                                                      0.0s
 => => transferring dockerfile: 89B                                                                                       0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                          1.3s
 => [internal] load .dockerignore                                                                                         0.0s
 => => transferring context: 2B                                                                                           0.0s
 => importing cache manifest from local:7675087045324269413                                                               0.0s
 => => inferred cache manifest type: application/vnd.oci.image.index.v1+json                                              0.0s
 => [1/2] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30    0.0s
 => => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30    0.0s
 => CACHED [2/2] RUN sleep 3                                                                                              0.8s
 => => sha256:9c704ecd0c694c4cbdd85e589ac8d1fc3fd8f890b7f3731769a5b169eb495809 29.71MB / 29.71MB                          0.3s
 => => extracting sha256:9c704ecd0c694c4cbdd85e589ac8d1fc3fd8f890b7f3731769a5b169eb495809                                 0.4s
 => => sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 32B / 32B                                  0.0s
 => => extracting sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1                                 0.0s
 => exporting cache to client directory                                                                                   0.0s
 => => preparing build cache for export                                                                                   0.0s
 => => writing layer sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1                              0.0s
 => => writing layer sha256:9c704ecd0c694c4cbdd85e589ac8d1fc3fd8f890b7f3731769a5b169eb495809                              0.0s
 => => writing config sha256:8adbdd5a906d7945681aa5217f9289d3768af56835a578a0a6bf2de2c03a85db                             0.0s
 => => writing cache manifest sha256:adde5cd70236d07d0a4c14ba61d40b18b447727baf6e0c694f60dd4558d90de2  
```

</details>

ログを見るとコマンドに「CACHED」と出ていてキャッシュが効いているのがわかります。

## 注意点

1. `--load`オプション：
   ビルド結果をDockerイメージとして保存する場合は、`--load`オプションを追加します。

   ```bash
   docker buildx build -t test \
       --cache-to type=local,dest=/tmp/foo,mode=max \
       --cache-from type=local,src=/tmp/foo \
       --load .
   ```

## まとめ

`docker buildx`の`--cache-to`と`--cache-from`オプションを活用することで、Dockerビルドのパフォーマンスを大幅に向上させることができます。特にCI/CD環境での利用に適しており、ビルド時間の短縮とリソースの効率的な使用につながります。

ぜひ、あなたのDockerビルドプロセスにこの最適化テクニックを取り入れてみてください。

## 参考資料

https://docs.docker.com/build/cache/

https://docs.docker.com/build/drivers/docker-container/

https://zenn.dev/sasakiki/articles/d235590c7af41a