Dockerイメージのslimとは何か

こちらは[PONOS Advent Calendar 2024](https://qiita.com/advent-calendar/2024/ponos) 16日目の記事です。

## はじめに

Docker Hubで配布されているイメージには、色んなバリエーションがあります。

イメージを単純に`ruby`のように指定すると、最新安定版を指す'latest'が暗黙的に指定されます。（`ruby:latest`と同じ）
`latest`はDockerのタグで、ある特定のコンテナイメージ（のハッシュ）を指しています。
同じイメージを指すタグは複数存在できて、`3.3.6-bookworm`や`3.3.6`でも指定できます。

利用できるタグは、Docker Hubの[Overview](https://hub.docker.com/_/ruby)から確認できるわけですが、これらのタグの中に`3.3.6-slim`のように「slim」が付いたものがあります。

字面から「通常版よりもサイズが小さいのだろう」と想像はできるのですが、具体的に何がどうなって小さくなっているのかは分かりません。
特に、Slim版だと使えない機能があったりするなら後々困ることにもなりますし、調べてみることにしました。


## 各言語が使っているベースイメージ

まずはベースイメージがどこ由来なのかを調べてみます。

たとえばRubyの公式Dockerイメージだと、執筆時点の`latest`は`3.3.6-bookworm`を指しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2492/ea5b24d3-5049-c86c-663b-9b43c93990c6.png)
他にもバージョンごと・ベースイメージごとのバリエーションはありますが、`latest`が指しているものが公式のデフォルトと言えそうです。

各言語の公式イメージがどのベースイメージを採用しているか調べてみました。

| 言語 | ベースイメージ |
|:-:|:-:|
| [Go](https://hub.docker.com/_/golang)  | bookworm-scm  |
| [Node.js](https://hub.docker.com/_/node)  | bookworm  |
| [Perl](https://hub.docker.com/_/perl)  | bookworm |
| [Python](https://hub.docker.com/_/python)  | bookworm |
| [Ruby](https://hub.docker.com/_/ruby)  | bookworm |
| [Rust](https://hub.docker.com/_/rust)  | bookworm  |

みんな`bookworm`、つまりDebianの安定版をベースイメージとして使っているようです。
（Goだけ`scm`とついているのは、Git・Mercurialなどのソースコード管理ツールを入れたいから。）



## 通常版とSlim版の違い

次に、通常版とSlim版がどう違って、どうやって作られているのかを調べました。

[Debian公式イメージ](https://hub.docker.com/_/debian)の「Image Variants」にこんな記載があります。

> These tags are an experiment in providing a slimmer base (removing some extra files that are normally not necessary within containers, such as man pages and documentation), and are definitely subject to change.
これらのタグは、よりスリムなベースを提供するための実験 (マニュアル ページやドキュメントなど、コンテナ内で通常は必要のないいくつかの余分なファイルを削除) であり、必ず変更される可能性があります。<br>
> See the debuerreotype-slimify script (debuerreotype linked above) for more details about what gets removed during the "slimification" process.
「スリム化」プロセス中に削除されるものの詳細については、debuerreotype-slimify スクリプト (上にリンクされている debuerreotype) を参照してください。

具体的に何が削除されるのかは分からないけど、manページやドキュメントが対象とのこと。
`debuerreotype-slimify`を見れば詳細が分かるそうなので、そちらを見てみます。


## debuerreotype-slimify

いきなり読めません（笑）。
`debuerreotype`リポジトリの[README](https://github.com/debuerreotype/debuerreotype?tab=readme-ov-file)によると、[ダゲレオタイプ](https://ja.wikipedia.org/wiki/%E3%83%80%E3%82%B2%E3%83%AC%E3%82%AA%E3%82%BF%E3%82%A4%E3%83%97)という写真用語をDebianとくっつけた造語とのこと。
デブレオタイプって読むんですかね？

ある時点のDebianのスナップショット＝写真ってことで、写真用語を使っているそうです。

[debuerreotype-slimify](https://github.com/debuerreotype/debuerreotype/blob/master/scripts/debuerreotype-slimify)を見てみると、削除対象ファイルの情報元として[.slimify-excludes](https://github.com/debuerreotype/debuerreotype/blob/master/scripts/.slimify-includes)を参照しています。

``` bash:.slimify-excludes
#   This file contains the list of files/directories which will be removed for "slim" image variants.
#  https://github.com/tianon/docker-brew-debian/issues/48

# https://wiki.ubuntu.com/ReducingDiskFootprint#Drop_unnecessary_files
/usr/share/doc/*
/usr/share/info/*
/usr/share/linda/*
/usr/share/lintian/overrides/*
/usr/share/locale/*
/usr/share/man/*
#/usr/share/groff/* (https://github.com/debuerreotype/debuerreotype/issues/87)

# https://salsa.debian.org/elmig-guest/localepurge/-/blob/176446028ca719d65993eb01e39d7040fbbcf12d/usr/share/localepurge/gen-dpkg-cfg.pl#L9-20
/usr/share/doc/kde/HTML/*/*
/usr/share/gnome/help/*/*
/usr/share/locale/*
/usr/share/omf/*/*-*.emf

# see also .slimify-includes
```

あくまでもドキュメント類やパッケージ関係のツールなどの、動作に必須じゃないものを削除しているみたいですね。
逆に、コマンドなどはSlimであってもベースイメージと同等と言えそうです。


## Debianベースイメージはどうやって作るの？

ではベースイメージはどうやって作るかというと、これまた`debuerreotype`を使って作られます。

[docker-debian-artifacts](https://github.com/debuerreotype/docker-debian-artifacts)に、Dockerから利用するためのrootfsが集約されていて、各バージョンのDockerfileは、rootfsを展開するだけのシンプルな作りになっています。

``` Dockerfile
FROM scratch
ADD oci/blobs/rootfs.tar.gz /
CMD ["bash"]
```


## rootfsはどうやって作るの？

さっき出てきたrootfsは、Debianを実行するための最低限のファイル群が格納されたもので、`/`に展開して使います。
そして、これも`debuerreotype`内のスクリプトで生成されています。

> The examples/debian.sh script in that debuerreotype repository⁠ (and the debian-all.sh companion/wrapper) is the canonical entrypoint used for creating the artifacts published in this image (via a process similar to the docker-run.sh included in the root of that repository).
> debuerreotype リポジトリ内の example/debian.sh スクリプト (および debian-all.sh コンパニオン/ラッパー) は、このイメージで公開されるアーティファクトを作成するために使用される正規のエントリポイントです (含まれている docker-run.sh と同様のプロセスを介して)そのリポジトリのルートにあります)。

rootfsの色んなバリエーションを作っていて、その中で通常版とSlim版も含まれる形です。

[examples/debian.sh](https://github.com/debuerreotype/debuerreotype/blob/master/examples/debian.sh)の中に、そのあたりの処理が書かれています。

``` bash:examples/debian.sh
# copy the rootfs to create other variants
mkdir "$rootfsDir"-slim
tar -cC "$rootfsDir" . | tar -xC "$rootfsDir"-slim

（中略）

debuerreotype-slimify "$rootfsDir"-slim
```

作成した通常版rootfsをコピーして、コピー先に対して`debuerreotype-slimify`を適用しているわけですね。（その中で、少し前に出てきた`.slimify-excludes`のリストを参照している）


## まとめ

Slimって何なんだろう？ からスタートし、思わぬ方向に掘り進んでいってしまいました。

- Dockerの各言語公式イメージは、ベースイメージとしてDebianを使っている
    - その時点の最新安定版　今日時点だとbookworm
- Debianイメージには通常版とSlim版がある
    - どちらも`debuerreotype`で作成したrootfsを使ってビルドされている
    - Slim版は、通常版をコピーしたものに対して`debuerreotype-slimify`を適用することで作成される
        - slimifyの中で、`.slimify-excludes`に定義されたファイル群を削除している
        - コマンドなどは削除されないため、通常版と差異はない

ぼんやりした認識を確信に変えることができて良かったと思います！


次回は@iixd_pogさんの記事です。お楽しみに〜。