はじめに
Docker のイメージやファイルシステムの形式について調べてみました。
Docker のイメージやファイルシステムの形式と言ったとき、次の3つのいずれかを指すと思います。
- Docker Hub(または Private Registry) からダウンロードされる Docker イメージの形式
- ダウンロードした Docker イメージをホスト上に保存する際の形式。または、docker build コマンドでビルドしたイメージの形式。
- コンテナのファイルシステムの形式
今回は 1、つまり docker pull した時に Docker Hub から何がどう落ちてくるのかをまとめてみます。
調査環境
- CentOS 7.1 (カーネル 4.2.1)
- Docker 1.8.2 (ストレージドライバは overlay)
Docker イメージを構成するもの
Docker イメージは次の2種類のファイルから構成されます。
- Manifest ファイル
イメージのメタデータを持つ JSON ファイル。 - レイヤーファイル
各レイヤーのディレクトリツリーを含んだ tar.gz ファイル。 1つのレイヤーに対して、1つの tar.gz が存在する。
例えば、1つの Docker イメージは以下のようなファイル群で表現されます。
Manifest ファイル
Manifest ファイルは、Docker イメージに関する次のような情報を含む JSON ファイルです。
(Manifest のフォーマットに関してはここを参照してください)。
- イメージ名 (例: centos)
- イメージのタグ名 (例: latest)
- イメージを構成するレイヤーのリスト
- 各レイヤーの ID
- 署名
docker pull <イメージ名>
コマンドを実行すると、Docker Engine はまず Manifest ファイルをダウンロードします。 Manifest には、イメージを構成するレイヤーファイルが書かれた fsLayers フィールドがあります。
レイヤーファイルの実体は Docker Hub の Blob という領域に置かれており、Docker Engine は fsLayers を参照し、Blob からレイヤーファイルをダウンロードしていきます。
以下の Manifest の例では、5つのレイヤーファイルからイメージが構成されていることになります。
{
"schemaVersion": 1,
"name": "library/centos",
"tag": "latest",
"architecture": "amd64",
"fsLayers": [
{ "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4" },
{ "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4" },
{ "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4" },
{ "blobSum": "sha256:c3bf6062f354b9af9db4481f24f488da418727673ea76c5162b864e1eea29a4e" },
{ "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4" }
],
"history": [
{ "v1Compatibility": "{\"id\":\"0f73ae75014f435e...\",\"parent\":\"f37e6a610a37349d...\", (中略...)" },
{ "v1Compatibility": "{\"id\":\"f37e6a610a37349d...\",\"parent\":\"f9a8cbc8dd13fb5b...\", (中略...)" },
{ "v1Compatibility": "{\"id\":\"f9a8cbc8dd13fb5b...\",\"parent\":\"f6f39725d938c8ad...\", (中略...)" },
{ "v1Compatibility": "{\"id\":\"f6f39725d938c8ad...\",\"parent\":\"47d44cb6f252ea4f...\", (中略...)" },
{ "v1Compatibility": "{\"id\":\"47d44cb6f252ea4f...\", …" }
],
"signatures": [ "中略..." ]
}
なお、レイヤー間の親子関係は、Manifest の history に書かれています。
また、Manifest とレイヤーファイルの URL は以下のようになります(Docker Registry HTTP API V2 の場合)。
- Manifest ファイル
https://registry-1.docker.io/v2/library/<イメージ名>/manifests/<タグ名>
(例) https://registry-1.docker.io/v2/library/centos/manifests/latest
- レイヤーファイル
https://registry-1.docker.io/v2/<イメージ名>/blobs/<tarsum>
(例) https://registry-1.docker.io/v2/library/centos/blobs/sha256:c3bf6062f354b9af9db4481f24f488da418727673ea76c5162b864e1eea29a4e
レイヤーファイル
Docker Engine はダウンロードしたレイヤーファイルを /var/lib/docker/tmp に一時的に置きます。
今回試した centos:latest のイメージは 5つのレイヤーから構成されているため、以下のように 5つのレイヤーファイルがダウンロードされています。
[root@centos7 tmp]# pwd
/var/lib/docker/tmp
[root@centos7 tmp]# ls -al
total 61460
drwx------. 2 root root 4096 Oct 3 06:32 .
drwx------. 9 root root 4096 Oct 3 06:32 ..
-rw-------. 1 root root 32 Oct 3 06:32 GetImageBlob068882538
-rw-------. 1 root root 32 Oct 3 06:32 GetImageBlob561232172
-rw-------. 1 root root 32 Oct 3 06:32 GetImageBlob568501953
-rw-------. 1 root root 62906573 Oct 3 06:32 GetImageBlob621683063
-rw-------. 1 root root 32 Oct 3 06:32 GetImageBlob747002488
(補足) これらのファイルは通常はすぐに削除されるため、削除されないように Docker のソースを修正して調査しています。
GetImageBlobXXX ファイルの実体は tar.gz です。中身は何かというと、ディレクトリツリーが含まれています。GetImageBlob621683063~~という0バイトのファイルが複数含まれてますが理由は分かりません。
Docker Engine は、このように /var/lib/docker/tmp にレイヤーファイルをダウンロードした後は、ストレージドライバ(AUFS, devicemapper, overlay など)を使いイメージをホストに保存していきます。
最後に
今回は、docker pull する際に、何がどうダウンロードされるかを調べてみました。あくまで Docker Hub からダウンロードされる際の形式なので、まだ Docker Engine のストレージドライバからは独立した段階です。
次回は、ダウンロードした Docker イメージが、Linux ホスト上でどのような形式で保存されるかをまとめてみます。