【Docker使えるようになりたい】【#11 イメージレイヤー】イメージのレイヤー構造について

Dockerのレイヤー構造とは

イメージは、レイヤーと呼ばれるファイルシステムの差分の積み重ねで構成されています。

Dockerfileで記述している各命令(RUNCOPYADDなど)が新しいレイヤーを作成します。

レイヤー構造を使うことによる利点としては、以下の点があげられます。

  • キャッシュの利用:同じ命令が再度実行される場合、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の命令とレイヤーの関係を確認する

最初の説明にもあったように、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では、以下の命令がそれぞれレイヤーを作成します。

  1. FROM ubuntu:20.04
  2. RUN apt update
  3. RUN apt install -y curl
  4. RUN curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash
  5. RUN apt install -y speedtest
  6. 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の実力を引き出せると思います。

以下の公式ドキュメントも参考にしてみてください

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA