Dockerイメージの徹底分析
James Reed
Infrastructure Engineer · Leapcell

Dockerイメージの徹底分析
I. Dockerイメージの概要
コンテナの基盤として、Dockerイメージは本質的にコンテナのファイルシステムの内容を表します。これは、Dockerコンテナを作成するために使用される読み取り専用のテンプレートです。技術的な観点から見ると、Dockerイメージはレイヤー化された構造設計を採用しています。ベースイメージを除いて、他のイメージは既存のイメージの上に新しいコンテンツを重ねることで生成されます。イメージの各レイヤーのメタデータは、json
ファイルに保存されます。このメタデータは、ファイルシステムの静的なコンテンツを記述するだけでなく、イメージの作成時間、ビルドの指示など、動的なデータ情報も含まれています。
1.1 Dockerイメージの構築
Dockerイメージは通常、Dockerfile
を通じて構築されます。Dockerfile
は、イメージの基本環境を定義し、ソフトウェアパッケージをインストールし、ファイルをコピーするなどの一連の指示を含むテキストファイルです。構築プロセス中、DockerはDockerfile
内の指示の順序に従って、イメージの各レイヤーを段階的に作成します。各指示が実行されると、新しいイメージレイヤーが生成され、これらのレイヤーは後続のビルドで再利用するためにキャッシュされます。
1.2 イメージ構築効率を向上させる方法
- キャッシュメカニズムの合理的な利用: イメージを構築する際、Dockerは現在の指示によって生成されたイメージレイヤーがローカルキャッシュに既に存在するかどうかを確認します。存在し、特定の条件(同じ指示、変更されていないファイルコンテンツなど)を満たしている場合、キャッシュされたイメージレイヤーが直接使用され、繰り返しの構築が回避されます。たとえば、
RUN
指示を実行する場合、この指示の実行結果が以前にキャッシュされた結果と同じである場合(指示とファイルシステムの変更などを比較して判断)、イメージレイヤーは再利用されます。 - Dockerfileの構造を最適化する: 頻繁に変更されない指示(基本的なソフトウェアパッケージのインストールなど)を先頭に配置します。このようにすることで、
Dockerfile
が後で変更された場合でも、これらの基本的な指示が変更されない限り、既存のイメージレイヤーを再利用でき、構築時間を短縮できます。 - レイヤーで構築する: 複雑な構築プロセスを複数の段階に分割し、各段階は特定のタスクのみを担当します。たとえば、コンパイルプロセスを含むイメージを構築する場合、コンパイルを最初に1つの段階で完了し、次にコンパイルされた結果を別の段階で最終イメージにコピーして、最終イメージのサイズを縮小できます。
II. Dockerイメージに関連するコマンド
Dockerクライアントは、Dockerデーモンと対話してイメージに関連するさまざまなタスクを完了するための一連の豊富なコマンドを提供します。
- イメージのリスト表示:
docker images
コマンドは、Dockerホスト上のすべてのイメージをリスト表示するために使用されます。-f
パラメータを使用してフィルタリングできます。たとえば、docker images -f dangling=true
は、タグのないすべてのイメージをリスト表示できます。 - イメージの構築:
docker build
コマンドは、Dockerfile
から新しいイメージを構築します。たとえば、docker build -t myimage:latest .
とすると、-t
はイメージのタグを指定するために使用され、.
は現在のディレクトリのDockerfile
を示します。 - イメージ履歴の表示:
docker history
コマンドは、特定のイメージのビルド履歴をリスト表示し、イメージの各レイヤーの作成時間、実行された指示、イメージのサイズなどの情報を表示できます。 - イメージのインポート:
docker import
は、tarball
ファイルから新しいファイルシステムイメージを作成します。 - イメージのプル:
docker pull
は、指定されたイメージをDockerイメージレジストリからローカルにプルします。 - イメージのプッシュ:
docker push
は、ローカルイメージを指定されたイメージレジストリにプッシュします。 - イメージの削除:
docker rmi
は、ローカルイメージを削除するために使用されます。イメージが複数のタグによって参照されている場合は、最初にすべてのタグを削除するか、-f
パラメータを使用して強制的に削除する必要があります。 - イメージの保存:
docker save
は、イメージをtar
ファイルとして保存します。これは、異なる環境でイメージを移行するのに便利です。 - イメージの検索:
docker search
は、Docker Hubで条件を満たすイメージを検索します。 - イメージのタグ付け:
docker tag
は、イメージにタグを付けます。これは、イメージのバージョン管理と識別に便利です。
III. Dockerイメージのダウンロードプロセス(pull操作)
Dockerは、典型的なC/S(クライアント/サーバー)アーキテクチャを採用しています。docker pull
などのクライアントコマンドは、最終的に処理のためにDockerデーモン(サーバー側)に送信されます。docker pull
が実行されると、具体的なプロセスは次のようになります。
- Dockerクライアントは構成とパラメータを整理し、
pull
指示をDockerサーバーに送信します。 - サーバー側が指示を受信すると、対応するハンドラーに渡します。ハンドラーは、Dockerデーモンの起動時に登録された
CmdPull
タスクを開始します。 - 受信したイメージレジストリアドレス(registry address)、リポジトリ名(repo name)、イメージ名、およびタグ(tag)に従って、Dockerデーモンは次の手順でイメージを検索してダウンロードします。
- リポジトリの下にあるすべてのイメージIDを取得する:
GET /repositories/{repo}/images
インターフェースを使用。 - リポジトリの下にあるすべてのタグの情報を取得する:
GET /repositories/{repo}/tags
インターフェースを使用。 - タグに従って対応するイメージUUIDを見つけ、イメージをダウンロードします。
- イメージの履歴情報を取得し、これらのイメージレイヤーを1つずつダウンロードする:
GET /images/{image_id}/ancestry
インターフェースを使用。イメージレイヤーがローカルに既に存在する場合、ダウンロードはスキップされます。存在しない場合は、ダウンロードが続行されます。 - イメージレイヤーの
json
情報を取得する:GET /images/{image_id}/json
インターフェースを使用。 - イメージコンテンツをダウンロードする:
GET /images/{image_id}/layer
インターフェースを使用。
- リポジトリの下にあるすべてのイメージIDを取得する:
- ダウンロードが完了したら、イメージコンテンツをローカルのUnionFS(Union File System)に保存し、新しくダウンロードしたイメージの情報をTagStoreに追加します。
IV. Dockerイメージのストレージ
4.1 UnionFSとaufs
UnionFSは、Dockerが階層化されたイメージを実装するための基礎です。これは、Linux、FreeBSD、NetBSDなどのシステムで複数のファイルシステムのブランチを透過的にオーバーレイして、統一されたファイルシステムを形成することをサポートするファイルシステムサービスです。Dockerでは、イメージはレイヤー形式で保存されます。アプリケーションレイヤーは完全なファイルシステムを表示し、基になるレイヤーはUnionFSを介して各イメージレイヤーのコンテンツと関係を管理します。
aufs
(Another UnionFS)は、Dockerで一般的に使用されるストレージドライバの1つです。さらに、devicemapper
などがあります。ユーザーはニーズに応じて適切なストレージドライバを選択したり、独自のドライバを実装したりすることもできます。
4.2 aufsイメージのストレージ構造
ubuntu:20.04
イメージを例にとると(現在のDockerバージョンが20.10.0で、イメージドライバがaufs
であると仮定)、docker history
を使用してイメージ履歴を表示します。
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE myregistry/ubuntu 20.04 8b24f7a1cb23 2 months ago 256.3 MB $ docker history 8b24 IMAGE CREATED CREATED BY SIZE 8b24f7a1cb23 2 months ago /bin/sh -c #(nop) CMD ["bash"] 0 B b17ee223aa89 2 months ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.9 kB c18294cc5170 2 months ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 195.5 kB d4fd76b09ce9 2 months ago /bin/sh -c #(nop) ADD file:0018ff77d038472f52 256.1 MB 511136ea3c5a 3 years ago 0 B
ubuntu:20.04
イメージには複数のレイヤーが含まれていることがわかります。aufs
データは/var/lib/docker/aufs
ディレクトリに保存され、これには3つの主要なフォルダが含まれています。
- layers: 各イメージがどのレイヤーで構成されているかを記録します。
- diff: 各イメージと前のイメージとの間の差分コンテンツ、つまり現在のイメージレイヤーの実際のデータを保存します。
- mnt: UnionFSが外部に提供するマウントポイントとして、各実行中のコンテナにはこのディレクトリに対応するフォルダがあり、これは統一されたファイルアクセスインターフェースを提供するために使用されます。
さらに、Dockerは各イメージレイヤーのメタデータをjson
形式で保存します。このメタデータは/var/lib/docker/graph/<image_id>/json
に保存されます。例:
{ "id": "8b24f7a1cb23146e20erewtewtertewrwc0f82943f4ab8c097e7", "parent": "b17ee223aa89d1b136ea55eqweqweqwrewra6c88d93e1ad7c", "created": "2024-12-21T02:11:06.735146646Z", "container": "c9a3eda5951d28aa8dbe5qwrqwrewrtw886d0a8e7a710132a38ec", "container_config": { "Hostname": "43bd710ec89a", "Domainname": "", "User": "", "Memory": 0, "MemorySwap": 0, "CpuShares": 0, "Cpuset": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "PortSpecs": null, "ExposedPorts": null, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/sh", "-c", "#(nop) CMD [\"bash\"]" ], "Image": "b17ee223aa89d1b136ea55e4421f4ce413dfc6c0cc6b2186dea6c88d93e1ad7c", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "NetworkDisabled": false, "MacAddress": "", "OnBuild": [], "Labels": null }, "docker_version": "20.10.0", "config": { "Hostname": "43bd710ec89a", "Domainname": "", "User": "", "Memory": 0, "MemorySwap": 0, "CpuShares": 0, "Cpuset": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "PortSpecs": null, "ExposedPorts": null, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "bash" ], "Image": "b17ee223aa89qwrewtretgertwerewrq6dea6c88d93e1ad7c", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "NetworkDisabled": false, "MacAddress": "", "OnBuild": [], "Labels": null }, "architecture": "amd64", "os": "linux", "Size": 0 }
同時に、/var/lib/docker/graph/<image_id>/layersize
ファイルはイメージレイヤーのサイズ情報を保存します。
V. Dockerイメージの作成とキャッシュメカニズム
docker build
を使用してイメージを作成する場合、Dockerはキャッシュメカニズムを使用して構築効率を向上させます。次のDockerfile
を例にとります。
FROM ubuntu:20.04 RUN apt-get update ADD run.sh / VOLUME /data CMD ["./run.sh"]
構築プロセス中、Dockerは指示の順序に従って実行します。
FROM
指示の処理: Dockerデーモンは最初にローカルのubuntu:20.04
イメージを検索します。存在しない場合は、イメージレジストリからプルし、メタデータを含むjson
ファイルを取得します。RUN
指示の処理: キャッシュが利用できない場合、この指示はapt-get update
を実行し、ファイルシステムの変更(更新されたソフトウェアパッケージリストなど)は/var/lib/docker/aufs/diff/<image_id>/
ディレクトリに保存されます。同時に、json
ファイルのcontainer_config.Cmd
フィールドは、実行された指示を記録します。次回構築するときに、新しいイメージレイヤーのparent
がまだubuntu:20.04
であり、json
ファイルのcmd
で変更される内容が同じである場合、イメージレイヤーは同じと見なされ、再構築せずに直接再利用されます。ADD
およびCOPY
指示の処理:ADD
またはCOPY
コマンドの場合、Dockerはファイルのハッシュ値を計算してイメージが同じかどうかを判断します。json
ファイルでは、ADD
指示に対応するCmd
フィールドはファイルのハッシュ文字列を記録します。ファイルの内容、ファイル名などが完全に同じである場合にのみ、イメージレイヤーが再利用されます。
ただし、キャッシュメカニズムには制限があります。外部リソースに依存するコマンド(外部ソフトウェアソースから更新を取得するapt-get update
、外部ファイルをダウンロードするcurl
など)の場合、外部のコンテンツが変更されても、Dockerは自動的に検出できません。この場合、--no-cache
パラメータを使用してキャッシュを強制的に無効にし、イメージを再構築できます。したがって、Dockerfile
を作成するときは、開発者はキャッシュメカニズムを十分に考慮し、イメージ構築の精度と効率を確保するために、公式が提供するベストプラクティスに従う必要があります。
VI. Dockerイメージとコンテナの関係
Dockerコンテナは、イメージの実行中のインスタンスです。イメージには静的なファイルシステムコンテンツが含まれており、コンテナはこれに基づいて動的なランタイム状態を追加します。コンテナの実行中に関連する情報(ファイルシステムの内容を除く)は、イメージのjson
ファイルに保存されます。例:
- 環境変数:
ENV FOO=BAR
など。これはコンテナの実行中に環境変数を定義します。 - データボリューム:
VOLUME /some/path
によって宣言されたコンテナデータボリュームは、コンテナの実行中に動的に追加され、イメージレイヤーの固定コンテンツではありません。 - 公開されたポート:
EXPOSE 80
は、コンテナが実行中に外部に公開する必要があるポートを記録します。 - 実行エントリ:
CMD ["./myscript.sh"]
は、コンテナの起動時に実行されるコマンドを定義します。
コンテナを起動すると、Dockerデーモンはイメージ情報をコンテナのルートファイルシステム(rootfs)として読み取り、同時にjson
ファイル内の動的情報を読み取って、コンテナのランタイム状態を構成します。実行中の各コンテナはDockerデーモンの子プロセスであり、Dockerデーモンはコンテナのライフサイクルとリソース割り当てを管理する責任があります。
VII. Dockerイメージの削除
イメージはローカルにUnionFS形式で保存され、docker rmi
コマンドを使用してイメージを削除できます。削除する際には、次の点に注意が必要です。
- イメージ参照関係: イメージには「参照」の概念があります。つまり、1つのイメージを複数のタグで参照できます。タグ付きイメージを削除する場合、タグは最初に削除されます(タグ解除操作)。イメージが他のタグによって参照されている場合は、最初にすべてのタグを削除するか、
-f
パラメータを使用して強制的に削除する必要があります。 - 多層イメージの削除: イメージに複数のレイヤーが含まれており、中間レイヤーが他のイメージによって参照されていない場合、このイメージを削除すると、参照されていないすべてのイメージレイヤーも一緒に削除されます。
【Leapcell:最高のサーバーレスウェブホスティング】(https://leapcell.io/)
最後に、Webサービスのデプロイに最適なプラットフォームをお勧めします:Leapcell
🚀 お気に入りの言語で構築する
JavaScript、Python、Go、またはRustで簡単に開発できます。
🌍 無制限のプロジェクトを無料でデプロイする
使用した分だけ支払う—リクエストなし、料金なし。
⚡ 従量課金制、隠れたコストなし
アイドル料金は不要、シームレスなスケーラビリティ。
🔹 Twitterでフォローしてください:@LeapcellHQ