【Dockerfile】RUN命令の`--mount`オプションを活用して、コンテナイメージのサイズを削減する

DockerfileのRUN命令には、`--mount`というオプションがあります
https://docs.docker.com/reference/dockerfile/#run---mount

このオプションで指定したディレクトリで行った作業は、最終的なコンテナイメージには反映されません
また、このディレクトリはマルチステージビルド時にコンテナ間で共有することも可能です
この記事では、`--mount`オプションを利用してこのようなリソースを、最終的なコンテナのイメージサイズに反映させないようにする方法を紹介します
- aptなどパッケージマネージャーのキャッシュ
- ビルドに必要で、実行時には不要なソースコードやファイル
- gitやwgetなどの、ファイルダウンロード用のパッケージ

## 1. aptなどパッケージマネージャのサイズ削減
ドキュメントに載っている例となりますが、実際にどのくらいの削減を見込めるのか、見てみます
https://docs.docker.com/reference/dockerfile/#run---mounttypecache


### ベースイメージ
今回はubuntu 24.04で検証します
容量は136MBです
```bash
$ docker image ls ubuntu:24.04
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
ubuntu       24.04     3f85b7caad41   4 weeks ago   136MB
```

### apt update
パッケージを更新するだけでどのくらいサイズが増えるか確認します
```Dockerfile
FROM ubuntu:24.04
RUN apt-get update
```

```bash
$ docker build -t dockerfile-run-mount:apt .
$ docker image ls dockerfile-run-mount:apt
REPOSITORY             TAG       IMAGE ID       CREATED       SIZE
dockerfile-run-mount   apt       8b9733505c07   3 hours ago   197MB
```

61MB増加の197MBでした

### パッケージをインストール
`make`をインストールしてみます

```Dockerfile
FROM ubuntu:24.04
RUN apt-get update && apt-get install --no-install-recommends -y make
```

```bash
$ docker build -t dockerfile-run-mount:make .
$ docker image ls dockerfile-run-mount:make
REPOSITORY             TAG       IMAGE ID       CREATED       SIZE
dockerfile-run-mount   make      6b118565e1b4   2 hours ago   198MB
```

`make`をインストールすると1MB増えました

### cacheを使用してパッケージをインストール
公式ドキュメントの通り、RUN命令にオプションを追加します
```Dockerfile
FROM ubuntu:24.04
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
	--mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update && apt-get install --no-install-recommends -y make
```

二つのディレクトリに対して、オプションが設定されています
- `/var/cache/apt`
- `/var/lib/apt`

オプション内容は同一です
```
--mount=type=cache,target={ディレクトリ名},sharing=locked
```

- `target={ディレクトリ名}`
マウント先ディレクトを指定します
このディレクトリに対して行った作業は、コンテナイメージ内に残りません
- `sharing=locked`
複数の命令から書き込みできないようにする設定です
今回はapt用のキャッシュは他では使用しないですが、マルチステージビルド内でキャッシュを共有する場合には設定が必要になります

この状態でコンテナをビルドします

```bash
$ docker build -t dockerfile-run-mount:cache .
$ docker image ls dockerfile-run-mount:cache
REPOSITORY             TAG       IMAGE ID       CREATED       SIZE
dockerfile-run-mount   cache     ad9befef2bfd   2 hours ago   137MB
```

ビルド後のサイズは137MBと、ubuntu:24.04から`make`分の1MB増えただけです
cacheオプションの効果で、`apt update`の分だけサイズ削減できました

## 2. ソースコードからビルドする際のサイズ削減
コンテナ内で使うライブラリを、ソースコードからビルドしないといけないケースでの活用例です
モダンなプログラミング言語のライブラリであれば気にしなくていいですが、makeのプロジェクトではサイズ削減が見込める手法です
※必要なパッケージが足りていないので、下記のコードではopensslはビルドできません
```Dockerfile
FROM ubuntu:24.04 as src
WORKDIR /usr/local/src
RUN apt-get update && apt-get install --no-install-recommends -y git
RUN git clone --depth 1 https://github.com/openssl/openssl.git

FROM ubuntu:24.04
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
	--mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update && apt-get install --no-install-recommends -y \
    make
RUN --mount=type=cache,target=/usr/local/src,from=src,source=/usr/local/src cd /usr/local/src/openssl \
    && ./Configure \
    && make \
    && make install
```

マルチステージビルドを使用し、二つの点でサイズ削減を行なっています
### ソースコードフォルダ自体を、本番コンテナから分離する
`--mount`オプションの`--type=cache`では、マルチステージビルドの別コンテナからディレクトリをマウントすることが可能です
上記のDockerfileでは、ソースコード用のコンテナを実行用コンテナとは別に用意し、ソースコードはそちらに展開するようにしています
```bash
--mount=type=cache,target=/usr/local/src,from=src,source=/usr/local/src
```
これにより、実行用コンテナにソースコードをコピーすることなく、実行用コンテナ内でビルドが可能です

### ソースコードを取得するためのパッケージ
gitやwgetなど、ソースコードをコンテナ内に持ってくるためのコマンドは、実行用のコンテナ内では不要なことが多いと思います
マルチステージビルドで、ソースコードを取得するためのコンテナを別で定義し、実行用コンテナではソースコードはマウントするのみとしています
```Dockerfile
FROM ubuntu:24.04 as src
WORKDIR /usr/local/src
RUN apt-get update && apt-get install --no-install-recommends -y git
RUN git clone --depth 1 https://github.com/openssl/openssl.git

FROM ubuntu:24.04
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
	--mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update && apt-get install --no-install-recommends -y \
    make
RUN --mount=type=cache,target=/usr/local/src,from=src,source=/usr/local/src cd /usr/local/src/openssl \
    && ./Configure \
    && make \
    && make install
```

## まとめ
コンテナのイメージサイズを削減する手段として、RUN命令の`--mount`オプションを紹介しました
今回マウントタイプは`cache`のみ例示しましたが、他には`tmpfs`も有用です
https://docs.docker.com/reference/dockerfile/#run---mounttypetmpfs

dockerコンテナはその仕組み上、一度ダウンロードしたソースコードを以降のRUN命令で削除しても、最終的なイメージサイズの削減にはなりません
実行用のコンテナにいかに不要ファイルを入れないかが重要です