コンテナサイズ最小化のためのベースイメージ再考
Author : 山口 能迪
AWS では Amazon ECS、Amazon EKS、AWS Lambda など多くのコンテナランタイムサービスが存在します。そうしたコンテナランタイムを利用する際の最初の一歩として行うのがコンテナイメージの作成です。AWS からも amazonlinux のイメージを提供していますが、デフォルトで 50MB 程度あります。コンテナイメージの起動を極力早く行うために、イメージサイズの削減を行っている方もいらっしゃることでしょう。
コンテナサイズの削減に関しては多くのプラクティスが存在しますが、今回はベースイメージに着目して、それぞれにどういった特徴があるのかを整理していきたいと思います。なお本記事は Dockerfile ベースでイメージをビルドすることを想定した解説になっています。また以下で特に記載がなければイメージサイズは 2025 年 1 月 9 日現在での arm64 用の latest タグのものです。
TL;DR
コンテナサイズ最小化のためのベースイメージとして scratch、busybox、 alpine、Debian slim、UBI、Distroless、Chainguard Image を紹介しました。各ユースケースに合わせて、良いベースイメージを選択したいですね。
scratch
最小のコンテナイメージを作成する場合、もっともプリミティブな方法は scratch と呼ばれる Docker 公式イメージを利用する方法があります。
この scratch イメージは多くのコンテナイメージの最下層のレイヤーで使われていて、たとえば Docker が正しく動作しているか確認するために利用する hello-world イメージは次のような Dockerfileで 作成されています。(hello はイメージを作っているレポジトリ内に同梱された実行バイナリです)
FROM scratch
COPY hello /
CMD ["/hello"]
scratch は誤解を恐れずに言えばホスト OS のカーネルだけが見える状態なので、それ以外に必要なファイルはすべて自分で用意する必要があります。自分が必要なファイルだけ追加できるという自由と引き換えに、非常にプリミティブなレベルでのファイルのコピーが必要となります (当然パッケージマネージャーなどはありません)。アプリケーション環境を作成したい場合には依存ライブラリなど含めて、大量のファイルをコピーする必要があり、Dockerfile にすべてを記述するのは困難でしょう。そこで多くの場合、ある程度一般的な依存ライブラリが同梱されたコンテナイメージを利用することになります。
busybox
busybox コンテナは名前の通り BusyBox が動作するだけの最低限の環境が作られたベースイメージです。これも Docker 公式イメージの 1 つです。このイメージ作成のための実際の Dockerfile は次のとおりです。
FROM scratch
ADD busybox.tar.gz /
CMD ["sh"]
先ほど紹介した scratch イメージの上に、busybox.tar.gz が展開されただけのものであることがわかります。*1
busybox は ash ではありますが、シェルも利用でき、かつサイズも 1.76MB と小さく、また標準 C ライブラリ (libc) も glibc、musl の選択肢があるため *2、単純なシェルスクリプトや、単独で動作するバイナリを動作させるだけであれば選択肢に入るでしょう。
しかしパッケージマネージャーなどは一切入っていないため、ライブラリー等を入れたい場合には scratch と同様にコピーしてくるしかありません。Dockerfile ベースで記述するためには多くの作業が必要になるかもしれません。
*1 : 正確には 各 CPU プラットフォームに合わせて修正したもの を展開しています。
*2 : 一応 uClibc もありますが利用者は少ないでしょう
alpine
alpine は Alpine Linux という非常に軽量な Linux ディストリビューションを使っているベースイメージで、BusyBox と musl という環境に加え、APK (Alpine Package Manager) というパッケージ管理ツールが入っています。その状態でイメージサイズが 3.81MB と、非常に軽量です。みなさんの多くもコンテナイメージの作成に alpine をベースイメージに使っているのではないでしょうか。
alpine は軽量ですし、APK のおかげで環境も作りやすいのですが、libc に musl を使っているため、不都合な状況が多々あります。たとえば次に挙げるような不都合があります。
- glibc で提供されている拡張機能に対応していない
- musl で実行するとスレッドセーフでない場合がある
- 共有ライブラリの扱いに難がある (musl は Lazy binding ができない、アンロードできない)
- スレッドのスタックサイズが小さく、セグメンテーション違反に遭遇しやすい
こうしたトラブルから、現在はベースイメージの指向が次のように別れているというのが私の認識です。libc を使う場合には glibc であることを前提として
- 少しサイズは大きくなるものの、シェルやパッケージマネージャーを内包し、Dockerfile でイメージを記述しやすいイメージ
- 個々の用途に合わせて必要な設定のみがされた極小サイズのイメージ
Debian slim
debian:xxx-slim は Debian の Docker 公式イメージの一部で、名前の通りイメージサイズが小さい Debian ベースのイメージです。シェルはデフォルトで bash が入っていますし、apt で Debian が公開している安定したパッケージを自由にインストールできます。
イメージサイズが 26.76MB ある (stable-slim) ため、他の極小サイズのイメージと比較するとだいぶ大きいですが、各種言語のランタイム自体が数十MB ~ 数百MB あることを考えると、アプリケーション用のイメージとしては誤差と考えることもできるでしょう。
Debian slim イメージはその安定性から、多くの言語の公式イメージの slim ベースイメージとして利用されています。*3
*3 : Python 3.13.1 での例
Red Hat Universal Base Image
OSS プロジェクトが公開する公式イメージでは Debian slim が多い一方で、RHEL 系のディストリビューションでイメージを作りたいという需要もあると思います。そのような需要のために Red Hat 社が Universal Base Image (UBI) というイメージを提供しています。UBI には用途に合わせて数種類のイメージがあります。
- redhat/ubi9-micro : busybox と同様の趣旨で bash と glibc 以外ほぼ入っていない最低限の環境。6.33MB。
- redhat/ubi9-minimal : Debian slim と同様の趣旨で microdnf が使える。35.84MB。
この他に Standard と Init というイメージがありますが、最小イメージの主旨からは外れるので紹介は省略します。
Distroless
Debian slim と UBI はパッケージマネージャーの利用を考慮しつつ極力小さいイメージを作成するという方向で使いやすくなっていますが、一方で特定の言語環境さえ使えれば良いという需要を満たすには大きすぎます。そういった需要を満たしたい場合には、Distroless イメージ が適しているでしょう。
Distroless は 2025 年 1 月現在、最新版は Debian 12.4 をベースにしています。これは Debian slim 等をベースに使っているということではなく、scratch に対して必要なパッケージのみを Debian のレポジトリから deb ファイルを取得して展開しているだけです。
入れられているパッケージによってイメージが異なりますが、基本的なものはこの 2 つです。
- gcr.io/distroless/static-debian12 (800KB 弱) : ca-certificate のファイルや base-files や tzdata など最低限のパッケージのみが入っている
- gcr.io/distroless/base-debian12 (8.5MB) : static-debian12 に加えて glibc や libssl が入っている
たとえば Go 製の実行バイナリのように libc に依存せずに単一バイナリで動作できるものは static をベースにすれば非常に小さいイメージが作成できます。
これをベースに作られたよく使われる言語用のイメージもあって、たとえば Java や Python であれば次のようなイメージがあります。
- gcr.io/distroless/java17-debian12 (88MB): base-debian12 に加えて Debian が配布する OpenJDK 17 とその依存パッケージが入っている
- gcr.io/distroless/java21-debian12 (68MB): base-debian12 に加えて Temurin OpenJDK 21 とその依存パッケージが入っている
- gcr.io/distroless/python3-debian12 (21MB): base-debian12 に加えて python3.11-minimal とその依存パッケージが入っている
これらのイメージにはシェルが入っていないため、たとえば開発時はどうしてもシェルが使いたいという場合には、これらそれぞれに対して debug イメージと呼ばれるものがあります。これは上記のイメージに対しタグで debug (例 : gcr.io/distroless/base-debian12:debug) を指定すると、その環境に追加して busybox がインストールされたイメージが取得できます。
Chainguard Images
これは名前の通り Chainguard 社が一般公開しているコンテナイメージ です。Chainguard 社はコンテナセキュリティやサプライチェーンセキュリティを専門としている企業なので、これらのコンテナイメージはセキュリティ対策がさまざまな点で施されていて、彼らが配布しているイメージは “low-to-no CVEs" を標榜しています。
そんな Chainguard Images ですが、上で紹介した Distroless に多大な影響を受けており、そのエッセンスを踏襲しつつ、それらを独自発展させています。たとえば
- Wolfi OS というコンテナイメージ作成用の独自ディストリビューションを OSS で運用
- apko という OCI コンテナイメージビルダーを独自開発
- melenge という apk パッケージビルダーを独自開発
これらをフル活用することで、常に SBOM が提供されセキュリティパッチが即座に当たったパッケージのみを使った最小サイズのコンテナを作成しています。彼らが提供している一般公開されたイメージは、たとえば busybox や Distroless の base のようなものがほしいのであれば次のようなものがあります。
- cgr.dev/chainguard/glibc-dynamic (4.13MB)
- cgr.dev/chainguard/busybox (3.59MB)
また alpine のように小さいけれどパッケージを追加で入れるベースがほしい、あるいは Distroless のように各言語用の最小イメージがほしいということであれば次のようなイメージが使えるでしょう。
- cgr.dev/chainguard/wolfi-base (5.40MB)
- cgr.dev/chainguard/python (20.9MB)
- cgr.dev/chainguard/jdk (104.1MB)
Chainguard Images は非常に良いのですが、一点注意としては自由に利用できるのは Developer Image に分類されるイメージのみです。たとえば上に挙げたイメージも自由に使えるのは latest タグのもののみで、特定のバージョンに固定したい場合は Chainguard の有償サービスを利用する必要があります。
まとめ
今回はコンテナイメージ最小化のためのさまざまなベースイメージを紹介しました。アプリケーションを動作させる場合、さまざまな条件を考慮する必要があるため、コンテナイメージの作成に必要な設定が多岐に渡ります。また Docker に限らず、Buildah、Buildpacks、Bazel、ko、kaniko など、さまざまな用途に沿ったコンテナ作成ツールが存在しています。みなさんの開発プロセスや用途に合わせた、最適なベースイメージを決定するための参考になればと思います。
筆者プロフィール

山口 能迪 (Yoshi Yamaguchi / @ymotongpoo)
アマゾン ウェブ サービス ジャパン合同会社
シニアデベロッパーアドボケイト
AWS製品の普及と技術支援を担当し、特にオブザーバビリティ、SRE、DevOpsといった領域を専門とする。
AWS を無料でお試しいただけます