はじめに
こんにちは、開発課に所属している新卒 1 年目のke-suke0215です。 Dockerのイメージについて「なんとなく業務で使っているけど、いまいちどうなっているのかわかっていない」という状態だったので、今回はDockerイメージの中身がどのように構成されているのかについて調べてみました。
私のようなDockerをなんとなく使っている方が仕組みを理解する手助けになれば幸いです。
Dockerイメージの構成要素
そもそもDockerイメージとは
Dockerイメージは、アプリケーションの実行に必要な情報をまとめたパッケージです。これらのイメージはコンテナ実行の土台となっています。ポータビリティの高さやホストOSから環境を分離できるなどの利点があります。
Dockerイメージの中身を見る
ここでは以下のDockerfileをビルドしたDockerイメージの中身を見ていきます。
FROM ubuntu:latest
最新のubuntuのイメージをベースにするだけの簡単なものです。 中身を見る手順は以下になります。 各コマンドの説明は省略させていただきます。
1. イメージをビルドする
今回はmy-ubuntu
という名前のイメージでタグは1.0
としてビルドします。
docker build -t my-ubuntu:1.0 .
2. イメージをtarファイルとして保存する
docker save
コマンドを使ってtarファイルにします。
docker save -o my-ubuntu-1.0.tar my-ubuntu:1.0
3. tarファイルを展開する
保存したtarファイルを展開して中身を見れるようにします。
tar xf my-ubuntu-1.0.tar
これでdockerイメージの中身がどのようになっているのか確認できるようになりました。 どのようなものがあるのか確認してみます。
$ ls 0cf20f556e5f1e2fd508acc18bc53e95974c37b6c7304b6cdcbd5bb1bb52df40 e8b8228e36aef7aaaacedf7b10514683933b62424e35956c02e5659aefbcf3bd.json manifest.json my-ubuntu-1.0.tar repositories
my-ubuntu-1.0.tar
は展開元のファイルなので、それ以外について詳しく見ていきます。
ハッシュ名のディレクトリ
0cf20f556e5~~~
です。これはイメージを構成する上で核となっているレイヤーと呼ばれるものです。レイヤーについての詳しい説明は後述します。各レイヤーはユニークなハッシュ値によって識別されています。各ディレクトリにはそのレイヤーの内容と関連するメタデータが格納されます。
ハッシュ名のJSONファイル
e8b8228e36a~~~.json
です。中身は以下のようになっています。
{ "architecture": "amd64", "config": { "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/bash" ], "Labels": { "org.opencontainers.image.ref.name": "ubuntu", "org.opencontainers.image.version": "22.04" }, "OnBuild": null }, "created": "2023-06-28T08:37:42.319109064Z", "history": [ { "created": "2023-06-28T08:37:40.107416121Z", "created_by": "/bin/sh -c #(nop) ARG RELEASE", "empty_layer": true }, { "created": "2023-06-28T08:37:40.172787047Z", "created_by": "/bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH", "empty_layer": true }, { "created": "2023-06-28T08:37:40.235648065Z", "created_by": "/bin/sh -c #(nop) LABEL org.opencontainers.image.ref.name=ubuntu", "empty_layer": true }, { "created": "2023-06-28T08:37:40.292202878Z", "created_by": "/bin/sh -c #(nop) LABEL org.opencontainers.image.version=22.04", "empty_layer": true }, { "created": "2023-06-28T08:37:42.055763636Z", "created_by": "/bin/sh -c #(nop) ADD file:140fb5108b4a2861b5718ad03b4a5174bba03589ea8d4c053e6a0b282f439ff3 in / " }, { "created": "2023-06-28T08:37:42.319109064Z", "created_by": "/bin/sh -c #(nop) CMD [\"/bin/bash\"]", "empty_layer": true } ], "moby.buildkit.buildinfo.v1": "eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJhdHRycyI6eyJmaWxlbmFtZSI6IkRvY2tlcmZpbGUifSwic291cmNlcyI6W3sidHlwZSI6ImRvY2tlci1pbWFnZSIsInJlZiI6ImRvY2tlci5pby9saWJyYXJ5L3VidW50dTpsYXRlc3QiLCJwaW4iOiJzaGEyNTY6MGJjZWQ0N2ZmZmEzMzYxYWZhOTgxODU0ZmNhYmNkNDU3N2NkNDNjZWJiYjgwOGNlYTJiMWYzM2EzZGQ3ZjUwOCJ9XX0=", "os": "linux", "rootfs": { "type": "layers", "diff_ids": [ "sha256:59c56aee1fb4dbaeb334aef06088b49902105d1ea0c15a9e5a2a9ce560fa4c5d" ] } }
このjsonファイルにはイメージのメタデータなどが記述されています。また、Dockerfileの記述の中でレイヤーにならないものの情報も入っています。レイヤーにならない記述はCMD
,ENTRYPOINT
,ENV
などがあります。
また、イメージの情報を出力すると以下のようになります。
docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE my-ubuntu 1.0 e8b8228e36ae 1 days ago 77.8MB
IMAGE ID
がこのファイル名と一致していることがわかります。なのでハッシュ名のJSONファイルはdockerイメージのidにもなっているのです。
manifest.json
このファイルの中身は以下の通りです。
[ { "Config": "e8b8228e36aef7aaaacedf7b10514683933b62424e35956c02e5659aefbcf3bd.json", "RepoTags": [ "my-ubuntu:1.0" ], "Layers": [ "0cf20f556e5f1e2fd508acc18bc53e95974c37b6c7304b6cdcbd5bb1bb52df40/layer.tar" ] } ]
manifest.json
はDockerイメージの中核となるファイルの一つです。
各記述はそれぞれ以下を指しています。
Config
:ハッシュ名のJSONファイルRepoTags
:イメージ名とタグLayers
:使用しているレイヤー
このことからmanifest.json
はこのdockerイメージの設計図のような役割を果たしていると言えそうです。
repositories
repositories
はの中身は以下のようになっています。
{ "my-ubuntu": { "1.0": "0cf20f556e5f1e2fd508acc18bc53e95974c37b6c7304b6cdcbd5bb1bb52df40" } }
イメージ名とタグとをハッシュにマッピングすることで、具体的なイメージのバージョンや名前を特定する役割を果たします。
レイヤーについて
さきほど少し触れましたが、dockerイメージはいくつかの層(レイヤー)で成り立っています。基本的にDockerfileの記述ごとに1つまたは複数のレイヤーが作成されます。(ハッシュ名のJSONファイルの部分でも触れたように、一部レイヤーにならない記述もあります。)
レイヤー構成の何がいいかというと、差分のみのレイヤーを作成することでディスクスペースを節約できることです。具体的に見ていきましょう。
2つのDockerfileからレイヤー構造を理解する
まずFROM
とRUN
の記述がある下のようなDockerfile(Dockerfile①とする)からイメージを作成してみます。
FROM ubuntu:latest RUN apt-get update
すると以下のレイヤーが作成されます。
FROM ubuntu:latest
に基づいて生成されたレイヤー(レイヤーA)RUN apt-get update
に基づいて生成されたレイヤー(レイヤーB)
次に下のようなFROM
とCOPY
の記述があるDockerfile(Dockerfile②とする)からイメージを作成します。
FROM ubuntu:latest COPY sample.txt /tmp
このとき、生成されるのは以下のレイヤーです。
COPY sample.txt /tmp
に基づいて生成されたレイヤー(レイヤーC)
Dockerfile①と同じ記述であるFROM ubuntu:latest
のレイヤーは作成されず、すでにあるレイヤーAを使ってイメージを構成します。なのでDockerfile②から生成されるイメージはレイヤーAとレイヤーCからできています。
不足しているレイヤーのみを作成してイメージを生成するため、無駄なレイヤーを増やさずディスク容量を節約できるのです。
まとめ
Dockerイメージの内部構成について見てきました。興味がある方の参考になれば幸いです。最後までお読みいただきありがとうございました。