イメージは、レイヤーと呼ばれるファイルシステムの差分の積み重ねで構成されています。
Dockerfileで記述している各命令(RUN
、COPY
、ADD
など)が新しいレイヤーを作成します。
レイヤー構造を使うことによる利点としては、以下の点があげられます。
- キャッシュの利用:同じ命令が再度実行される場合、Dockerはキャッシュされたレイヤーを再利用します。これにより、ビルド時間が短縮されます。
- レイヤーの共有:複数のイメージ間で共通のレイヤーがある場合、ディスクスペースを節約できます。
いつもDocker Hub
などからPullしてきているイメージもレイヤーの積み重ね
でできています。
Dockerにはそのレイヤーを可視化するコマンドdocker image history
が用意されています。
docker image history {イメージ名}
このコマンドを使うことで、
url: https://docs.docker.com/reference/cli/docker/image/history/
title: "docker image history"
host: docs.docker.com
favicon: https://docs.docker.com/favicons/docs@2x.ico
image: https://docs.docker.com/assets/images/thumbnail.webp
以下が、ubuntu:latestのイメージのレイヤーを表示した例です。
docker image history ubuntu:latest
以下が、ubuntu:latest
イメージをレイヤー構造になります。
docker image history ubuntu:latest
IMAGE CREATED CREATED BY SIZE COMMENT
8a37d68f4f73 7 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 7 weeks ago /bin/sh -c #(nop) ADD file:c2e78eb585ec4e503… 87.5MB
<missing> 7 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 7 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 7 weeks ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B
<missing> 7 weeks ago /bin/sh -c #(nop) ARG RELEASE 0B
とりあえずこんなものなんだなぐらいの感覚でいいと思います。
デフォルト実行コマンドが最初の行に記述してありますが、これをイメージの段階で手元で確認したい時などに私は使っています。
SIZE
となっている列が、イメージのサイズになるものです。SIZE
の数値を合計したものが、イメージそのもののサイズになります。
最初の説明にもあったように、Dockerfileの各命令は、新しいレイヤーを生成します。
FROM ubuntu:20.04
RUN apt update
RUN apt install -y curl
RUN curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash
RUN apt install -y speedtest
CMD ["speedtest","--accept-license"]
上記のDockerfileでは、以下の命令がそれぞれレイヤーを作成します。
FROM ubuntu:20.04
RUN apt update
RUN apt install -y curl
RUN curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash
RUN apt install -y speedtest
CMD ["speedtest","--accept-license"]
(実行時の命令なのでレイヤーは作成しません)
このイメージを作成して、実際にレイヤーが追加されているか確認して見ましょう。
まずはdockerfileのある場所に移動してbuildをして、イメージを生成します。
docker image build -t speed-test .
その後、イメージのレイヤーを確認します。
docker image history speed-test
IMAGE CREATED CREATED BY SIZE COMMENT
2e717f3ae565 4 days ago CMD ["speedtest" "--accept-license"] 0B buildkit.dockerfile.v0
<missing> 4 days ago RUN /bin/sh -c apt install -y speedtest # bu… 3.21MB buildkit.dockerfile.v0
<missing> 4 days ago RUN /bin/sh -c curl -s https://packagecloud.… 41.2MB buildkit.dockerfile.v0
<missing> 4 days ago RUN /bin/sh -c apt install -y curl # buildkit 18.2MB buildkit.dockerfile.v0
<missing> 6 days ago RUN /bin/sh -c apt update # buildkit 53.9MB buildkit.dockerfile.v0
<missing> 5 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ADD file:e7cff353f027ecf0a… 81.7MB
<missing> 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ARG RELEASE 0B
確かにコマンドの数だけレイヤーが増えていました。
CMD命令はCMD命令の中で一番上のレイヤーにあるものが優先されるので、今回の場合は、Dockerfileに自分で設定した命令が優先されることになります。
基本的にイメージのサイズは、レイヤー数
がすくなくればなるほど小さくなります。
レイヤーが少なければ、イメージサイズが少なくなることを確認するために先ほどのdockerfileのRUN命令を一つにまとめて以下のようにして、イメージを生成してみます。
FROM ubuntu:20.04
RUN apt update && \
apt install -y curl && \
curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash && \
apt install -y speedtest && \
apt clean && rm -rf /var/lib/apt/lists/*
CMD ["speedtest", "--accept-license"]
docker image la
コマンドを実行してサイズを確認して見ると、以下のような違いがありました。
IMAGE CREATED CREATED BY SIZE COMMENT
baa8881d2181 7 seconds ago CMD ["speedtest" "--accept-license"] 0B buildkit.dockerfile.v0
<missing> 7 seconds ago RUN /bin/sh -c apt update && apt install… 28.5MB buildkit.dockerfile.v0
<missing> 5 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ADD file:e7cff353f027ecf0a… 81.7MB
<missing> 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ARG RELEASE 0B
命令を1つにまとめなかった場合と比較すると、明らかに少なくなっていることがわかると思います。
細かい命令SIZE | 圧縮命令SIZE
0B | 0B
3.21MB | 28.5MB
41.2MB |
18.2MB |
53.9MB |
0B | 0B
81.7MB | 81.7MB
0B | 0B
0B | 0B
0B | 0B
0B | 0B
動作が同じであるのなら、サイズは小さいに越したことはないのですが、他にどのようなメリットデメリットがあるのが理解する必要があります。
メリット
- イメージサイズの削減:レイヤーをまとめることで、重複や不要なファイルを減らし、イメージサイズを小さくできます。
- デプロイ効率の向上:小さなイメージはネットワーク転送が速くなり、デプロイ時間を短縮します。
デメリット
- ビルドキャッシュの無効化:命令をまとめて1行にすると、その命令の一部が変更された場合、キャッシュが無効になり、ビルド時間が長くなる可能性があります。
- デバッグの困難さ:複数の命令を1行にまとめると、どのステップでエラーが発生したのか特定しにくくなります。
開発初期や頻繁に変更がある場合は、命令を分けてキャッシュを活用することが推奨されます。
開発中は命令を分ける
- キャッシュを活用してビルド時間を短縮します。
- エラーの特定が容易になります。
本番用イメージでは命令をまとめる
- 不要なレイヤーを減らし、イメージサイズを小さくします。
- デプロイ効率を向上させます。
不要なファイルを削除する
- 一時ファイルやキャッシュを
RUN
命令の中で削除します。
RUN apt-get update && \
apt-get install -y package && \
rm -rf /var/lib/apt/lists/*
軽量なベースイメージを使用する
alpine
などの軽量イメージを検討します。
マルチステージビルドを活用する
- ビルド用の依存関係を最終イメージに含めないようにします。
Dockerのレイヤー構造を理解することで、イメージのビルド時間やサイズを効率的に最適化できます。
開発フェーズと本番環境でのニーズに応じて、命令の書き方を最適化することで真のDockerの実力を引き出せると思います。
以下の公式ドキュメントも参考にしてみてください