Dockerを使い始めると、しばしば耳にするのが「Dockerイメージのレイヤ」という言葉です。
しかし、具体的にレイヤとは何なのか、なぜ重要なのか、初心者にとっては理解が難しいかもしれません。
本記事では、Dockerイメージのレイヤについて、初心者でもわかりやすく解説します。
Dockerイメージレイヤの理解
Dockerイメージの基礎知識
Dockerイメージとは、コンテナを動かすためのファイルシステムと構成情報を持ったテンプレートのようなものです。
このイメージを元に、実際に動作するコンテナが作られます。
イメージの特徴の一つが「レイヤ構造」です。
Dockerイメージは複数のレイヤが積み重なったもので構成されています。
この構造により、効率的なデータ管理と再利用が可能になります。
レイヤとは?
レイヤ(Layer) とは、Dockerイメージを構成する1つ1つの積み重ねのことを指します。
レイヤは基本的に以下のような特性を持っています:
- 不変性:
レイヤは一度作成されると変更されません。変更が必要な場合は新しいレイヤが作成されます。 - キャッシュ性:
同じレイヤが既に存在する場合、それを再利用するためビルドが高速化します。 - 読み取り専用:
各レイヤは読み取り専用であり、コンテナが実行される際には書き込み可能な新しいレイヤ(コンテナレイヤ)が上に追加されます。
Dockerイメージのレイヤ構造の例
例えば、以下のDockerfileを考えてみましょう。
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y python3
COPY . /app
CMD ["python3", "/app/main.py"]
このDockerfileから作成されるイメージは以下のようなレイヤ構造になります。
- ベースイメージレイヤ:
FROM ubuntu:20.04- Ubuntu 20.04のベースイメージ。
- 更新とインストールレイヤ:
RUN apt-get update && apt-get install -y python3- パッケージの更新とPython3のインストール内容。
- アプリケーションのコピー:
COPY . /app- ホストマシンからコンテナ内の
/appディレクトリへのファイルコピー。
- ホストマシンからコンテナ内の
- デフォルトコマンド設定:
CMD ["python3", "/app/main.py"]- 実行時のデフォルトコマンド。
+----------------------------------------------------+
| ベースイメージ (FROM ubuntu:20.04) |
+----------------------------------------------------+
| パッケージインストール |
| (RUN apt-get update && apt-get install -y |
| build-essential nasm) |
+----------------------------------------------------+
| 作業ディレクトリ設定 |
| (WORKDIR /usr/src/assembly) |
+----------------------------------------------------+
| ソースコードコピー |
| (COPY hello.asm .) |
+----------------------------------------------------+
| アセンブリビルド |
| (RUN nasm -f elf64 -o hello.o hello.asm && |
| ld -o hello hello.o) |
+----------------------------------------------------+
| デフォルトコマンド設定 |
| (CMD ["./hello"]) |
+----------------------------------------------------+
レイヤ構造が重要な理由
Dockerイメージのレイヤ構造は、以下の理由で非常に重要です。
- 効率的なストレージ使用:
- 同じベースイメージを使用する他のイメージとレイヤを共有できます。
これにより、ストレージ容量を節約できます。
- 同じベースイメージを使用する他のイメージとレイヤを共有できます。
- ビルドの高速化:
- キャッシュされたレイヤを再利用することで、再ビルド時に必要な処理を最小限に抑えられます。
- トラブルシューティングが簡単:
- 各レイヤが独立しているため、問題が発生した際に特定のレイヤを調査しやすくなります。
コンテナレイヤとイメージレイヤ
Dockerには2種類のレイヤが存在します。
- イメージレイヤ(Image Layer)
- Dockerイメージを構成する読み取り専用のレイヤです。
- 例えば、
ubuntu:20.04をベースイメージに使う場合、このレイヤはキャッシュされ、他のDockerfileでも再利用できます。 - ベースイメージや中間イメージとして使われ、複数のDockerイメージで共有可能です。
- コンテナレイヤ(Container Layer)
- コンテナが起動された際に作成される読み書き可能なレイヤです。
- コンテナ内で行われた変更(新しいファイルの作成や削除、ファイルの編集など)はこのレイヤに保存されます。
- コンテナを停止または削除すると、このレイヤは失われます。
構成イメージ
イメージレイヤ(元のDockerイメージ)
+----------------------------------------------------+
| ベースイメージ (例: FROM ubuntu:20.04) |
| --> Ubuntu 20.04 の基本ファイル構成が含まれる |
+----------------------------------------------------+
| Python3インストールレイヤ |
| (例: RUN apt-get update && apt-get install -y python3)
| --> Python3のバイナリと関連ライブラリが含まれる |
+----------------------------------------------------+
| アプリケーションコードコピー |
| (例: COPY . /app) |
| --> /app ディレクトリにローカルファイルが含まれる |
+----------------------------------------------------+
コンテナレイヤ(実行中のコンテナ)
+----------------------------------------------------+
| ベースイメージ (ubuntu:20.04) | <-- イメージから継承
+----------------------------------------------------+
| Python3インストール状態 (イメージの内容) | <-- イメージから継承
+----------------------------------------------------+
| アプリケーションコード (/app の内容) | <-- イメージから継承
+----------------------------------------------------+
| 実行時の変更 (例: 一時ファイルの生成など) |
| --> 実行中のコンテナで発生した変更がこのレイヤに保存される
+----------------------------------------------------+
- イメージレイヤ
Apacheのインストール状態が固定され保存されます。
このイメージを基に新しいコンテナが作成されます。 - コンテナレイヤ
コンテナレイヤはイメージレイヤの上に積み重ねられます。
設定ファイルを編集すると、変更内容はコンテナレイヤにのみ保存され、元のイメージには影響しません。
Dockerイメージの各レイヤがコンテナレイヤに継承され、コンテナが実行される際にさらに変更や操作が反映される。
キャッシュされたレイヤについて
Dockerは、ビルドプロセス中に以前のビルドで作成されたレイヤを再利用することで、時間とリソースを節約します。
この仕組みを支えるのが「キャッシュされたレイヤ」です。
キャッシュが再利用される条件
キャッシュされたレイヤが再利用されるためには、Dockerfile内の命令が完全に一致する必要があります。
例えば:
- ベースイメージが同じであること(
FROM命令)。 - ファイルの内容が一致していること(
COPYやADD命令)。 - 実行されたコマンドが同一であること(
RUN命令)。
もし変更があれば、その変更以降のすべてのレイヤが再構築されます。
キャッシュのメリット
- ビルド時間の短縮:
変更されていない部分の再ビルドを回避できます。 - リソースの節約:
不要な再構築を防ぐことで、CPUやディスクI/Oの使用を削減します。
キャッシュが無効化される場合
特定の操作によってキャッシュが無効になることがあります。
- 変更されたファイル:
COPYやADDで新しいファイルが追加された場合。 - 非決定的なコマンド:
例えば、RUN apt-get updateのように、外部の状態に依存するコマンド。
キャッシュを無効化したい場合は、--no-cacheオプションを使用します。
$ docker build --no-cache -t my-image .
レイヤを確認する方法
Dockerイメージのレイヤは、docker history コマンドを使用して確認できます。
$ docker history イメージ名
例:
$ docker history my-image
IMAGE CREATED CREATED BY SIZE
<hash1> 2 days ago /bin/sh -c #(nop) CMD ["python3" "/app/mai... 0B
<hash2> 2 days ago /bin/sh -c #(nop) COPY dir:1bb0d27c1f3e6797... 45kB
<hash3> 2 days ago /bin/sh -c apt-get update && apt-get instal... 122MB
<hash4> 2 weeks ago /bin/sh -c #(nop) FROM ubuntu:20.04 29MB
Dockerイメージをファイルとして保存して情報を確認する
Dockerイメージをファイルとして保存し、その中身を確認する方法について説明します。
Dockerイメージの保存
docker saveコマンドを使用すると、Dockerイメージをtarファイルとして保存できます。
$ docker save -o イメージ名.tar イメージ名
これにより、イメージ全体がmy-image.tarとして保存されます。
保存したイメージの中身を確認する
保存されたtarファイルの内容を確認するには、tarコマンドを使用します。
$ tar -tf my-image.tar
出力例:
manifest.json
config.json
layer.tar
...
- manifest.json: イメージに関するメタデータが記載されたファイル。
- config.json: イメージの設定やレイヤ構造の情報。
- layer.tar: 各レイヤのデータ。
特定のレイヤ内容を確認する
layer.tarを展開すると、各レイヤのファイルシステムを確認できます。
$ tar -xf layer.tar -C 出力先ディレクトリ
これにより、レイヤ内にどのようなファイルやディレクトリが含まれているかを詳細に確認できます。
layer.tar 展開結果の例
最上位レイヤ (<hash1>)
- 内容: CMD 命令
- 展開結果:
<展開先ディレクトリ>/
└── 無し (CMD命令はメタデータのみでファイルシステムの変更はなし)
2番目のレイヤ (<hash2>)
- 内容: COPY 命令で追加されたファイルやディレクトリ
- 展開結果
<展開先ディレクトリ>/
├── app/
├── main.py
└── requirements.txt
詳細: COPY dir:1bb0d27c1f3e6797... でコンテナ内にコピーされたディレクトリやファイル。
3番目のレイヤ (<hash3>)
- 内容: apt-get install によるパッケージインストール
- 展開結果
<展開先ディレクトリ>/
├── bin/
│ ├── python3
│ ├── pip3
│ └── apt
├── lib/
│ ├── x86_64-linux-gnu/
│ │ ├── libc.so.6
│ │ ├── libpython3.8.so.1.0
│ │ └── libssl.so.1.1
└── var/
├── cache/
│ └── apt/
│ └── archives/
│ ├── python3.8.deb
│ ├── pip.deb
│ └── ...
└── log/
└── apt/
└── history.log
詳細: apt-get によるインストールで生成されたファイル群が含まれています。
最下位レイヤ (<hash4>)
- 内容: ベースイメージ (FROM ubuntu:20.04)
- 展開結果
<展開先ディレクトリ>/
├── bin/
│ ├── bash
│ ├── ls
│ └── cat
├── lib/
│ ├── x86_64-linux-gnu/
│ │ ├── libc.so.6
│ │ ├── ld-2.31.so
│ │ └── libpthread.so.0
├── etc/
│ ├── hosts
│ ├── resolv.conf
│ └── ...
└── var/
├── log/
└── tmp/
レイヤのつながりの仕組み
Dockerの各レイヤは Union File System を使用してつながっています。
以下にその仕組みと動作について詳しく説明します。
各レイヤは差分のみを記録
- 各レイヤには、Dockerfileの命令で追加、変更、削除された部分だけが保存されます。
- レイヤ自体は独立しており、他のレイヤの内容を直接変更することはありません。
Union File Systemによるマージ
- Dockerはレイヤを下から順に「積み重ねる」ことで、コンテナ内の最終的なファイルシステムを構成します。
- Union File Systemでは、下位レイヤの内容に対して上位レイヤの変更や追加が適用され、最終的な統一されたファイルシステムが提供されます。
変更の適用順序
- 下位レイヤが先に適用され、上位レイヤがそれを上書き、もしくは新しい内容を追加します。
以下にレイヤのつながりを示す図を示します。
ベースイメージレイヤ (<hash4>)
+----------------------------------------------------+
| bin/ |
| ├── bash |
| ├── ls |
| └── cat |
| etc/ |
| ├── hosts |
| └── resolv.conf |
| ... |
+----------------------------------------------------+
パッケージインストールレイヤ (<hash3>)
+----------------------------------------------------+
| bin/ |
| ├── python3 | <-- 新規追加
| ├── pip3 | <-- 新規追加
| ... |
| var/cache/apt/archives/ |
| ├── python3.8.deb | <-- 新規追加
| ├── pip.deb | <-- 新規追加
| ... |
+----------------------------------------------------+
ファイルコピー (COPY命令) レイヤ (<hash2>)
+----------------------------------------------------+
| app/ |
| ├── main.py | <-- 新規追加
| └── requirements.txt | <-- 新規追加
+----------------------------------------------------+
最上位レイヤ (CMD命令) (<hash1>)
+----------------------------------------------------+
| (ファイル変更なし。メタデータのみ) |
+----------------------------------------------------+
最終的なファイルシステム(Union File Systemの結果)
+----------------------------------------------------+
| bin/ |
| ├── bash |
| ├── ls |
| ├── cat |
| ├── python3 | <-- 上位レイヤから追加
| ├── pip3 | <-- 上位レイヤから追加
| etc/ |
| ├── hosts |
| └── resolv.conf |
| app/ |
| ├── main.py | <-- 上位レイヤから追加
| └── requirements.txt | <-- 上位レイヤから追加
| var/cache/apt/archives/ |
| ├── python3.8.deb | <-- 上位レイヤから追加
| ├── pip.deb | <-- 上位レイヤから追加
+----------------------------------------------------+
レイヤを活用するベストプラクティス
- キャッシュを意識したDockerfileの記述
- 頻繁に変更される命令(例:
COPYやADD)は、できるだけ後に書く。
- 頻繁に変更される命令(例:
- 不要なレイヤを減らす
- 複数の
RUN命令を1つにまとめることで、レイヤ数を削減できます。
- 複数の
小さなベースイメージを選択
- 必要な機能だけを含む軽量のベースイメージ(例:
alpine)を選ぶ。
まとめ
Dockerイメージのレイヤは、効率的なコンテナ運用の鍵です。その仕組みを理解することで、イメージのサイズを小さくし、ビルドを高速化し、トラブルシューティングを容易にすることができます。
初心者のうちからレイヤの概念を意識してDockerを活用しましょう。

