めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

Dockerイメージのレイヤー構造について

何の話かというと

Dockerイメージは複数のレイヤーが重なった形になっています。このあたりを内部構造とあわせて解説します。前提の環境は、CentOS7です。(つまり、ローカルのイメージ管理は、dm-thinが前提。)

# rpm -q docker
docker-0.11.1-22.el7.centos.x86_64

ローカルにイメージをpullする時の動作

まず、ローカルのイメージをすべて消してキレイな体にしておきます。

# systemctl stop docker.service
# rm -rf /var/lib/docker/*
# systemctl start docker.service

CentOSの公式イメージをpullします。この時、4つのイメージ(b1bd49907d55、b157b77b1a65、511136ea3c5a、34e94e67e63a)がダウンロードされます。

# docker pull centos
Pulling repository centos
b1bd49907d55: Download complete 
b157b77b1a65: Download complete 
511136ea3c5a: Download complete 
34e94e67e63a: Download complete 

しかしながら、imagesで確認しても、2種類のイメージ(b1bd49907d55、b157b77b1a65)しか見えません。

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              centos6             b1bd49907d55        2 days ago          212.5 MB
centos              centos7             b157b77b1a65        2 days ago          243.7 MB
centos              latest              b157b77b1a65        2 days ago          243.7 MB

これは、上記のイメージを作成する時に、ベースイメージがあって、それに修正を加える形で作成したためです。-vオプションで、イメージの親子関係が分かります。

# docker images -v
Warning: '-v' is deprecated, it will be removed soon. See usage.
digraph docker {
 base -> "511136ea3c5a" [style=invis]
 "511136ea3c5a" -> "34e94e67e63a"
 "34e94e67e63a" -> "b1bd49907d55"
 "b1bd49907d55" [label="b1bd49907d55\ncentos:centos6",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
 "34e94e67e63a" -> "b157b77b1a65"
 "b157b77b1a65" [label="b157b77b1a65\ncentos:centos7\ncentos:latest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
 base [style=invisible]
}

ツリーにするとこんな感じ。

base
|
511136ea3c5a
|
34e94e67e63a
|
|-------------------------------------
|                                    |
b1bd49907d55                         b157b77b1a65
  ↑                     ↑
centos:centos6                       centos:centos7, centos:latest

これは、4種類のイメージを順番にダウンロードして、ローカルのdm-thinの論理デバイスに格納する順番に対応しています。

最初に「base」という空っぽのイメージからスナップショットを作って、その中に「511136ea3c5a」に対するtarを展開します。これが、「511136ea3c5a」の実体です。

次に、「511136ea3c5a」からスナップショットを作って、その中に「34e94e67e63a」に対応するtarを展開します。この時、「34e94e67e63a」に対応するtarの中身は、「34e94e67e63a」全体ではなくて、「34e94e67e63a」と「511136ea3c5a」の差分のみが入っています。

同様に、「34e94e67e63a」からスナップショットを作って、その中に「b1bd49907d55」に対応するtarを展開します。この時、「b1bd49907d55」に対応するtarの中身は、「b1bd49907d55」全体ではなくて、「b1bd49907d55」と「34e94e67e63a」の差分のみが入っています。

それぞれのイメージの「差分tar」の実体は、savaコマンドでイメージを書き出すと分かります。

# docker save centos:centos6 > centos6.tar
# tar -tvf centos6.tar 
drwx------ 0/0               0 2014-08-02 14:29 ./
drwxr-xr-x 0/0               0 2014-08-02 14:29 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a/
-rw-r--r-- 0/0               3 2014-08-02 14:29 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a/VERSION
-rw-r--r-- 0/0            1565 2014-08-02 14:29 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a/json
-rw-r--r-- 0/0            1024 2014-08-02 14:29 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a/layer.tar
drwxr-xr-x 0/0               0 2014-08-02 14:29 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/
-rw-r--r-- 0/0               3 2014-08-02 14:29 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/VERSION
-rw-r--r-- 0/0             585 2014-08-02 14:29 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/json
-rw-r--r-- 0/0            1536 2014-08-02 14:29 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar
drwxr-xr-x 0/0               0 2014-08-02 14:29 centos:centos6/
-rw-r--r-- 0/0               3 2014-08-02 14:29 centos:centos6/VERSION
-rw-r--r-- 0/0            1574 2014-08-02 14:29 centos:centos6/json
-rw-r--r-- 0/0       222638592 2014-08-02 14:29 centos:centos6/layer.tar

書きだされたtarファイルの中には、さらに3つのlayer.tarというtarファイルがあります。これらが、それぞれ、上位イメージとの差分を含むtarファイルになります。

ただし、各tarファイルのサイズから分かるように、この場合は、最後のlayer.tarにファイルの実体がほとんど詰まっているようです。おそらく、最初の2つは、Dockerfileでイメージを作った時に、MAINTAINERなどのメタデータだけを設定してcommitしたものだと思われます。

これではちょっと詰まらないので、centos:centos6をベースにさらに新しいイメージを作ってみます。

# docker run -it --name hoge centos:centos6 /bin/bash
bash-4.1# echo hoge > /root/hoge.txt
bash-4.1# exit
exit
# docker commit hoge hoge:latest
aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2
# docker rm hoge
hoge

できたイメージをsaveで取り出します。

# docker save hoge > hoge.tar
# tar -tvf hoge.tar 
# tar -tvf hoge.tar 
drwx------ 0/0               0 2014-08-02 14:49 ./
drwxr-xr-x 0/0               0 2014-08-02 14:49 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a/
-rw-r--r-- 0/0               3 2014-08-02 14:49 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a/VERSION
-rw-r--r-- 0/0            1565 2014-08-02 14:49 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a/json
-rw-r--r-- 0/0            1024 2014-08-02 14:49 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a/layer.tar
drwxr-xr-x 0/0               0 2014-08-02 14:49 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/
-rw-r--r-- 0/0               3 2014-08-02 14:49 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/VERSION
-rw-r--r-- 0/0             585 2014-08-02 14:49 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/json
-rw-r--r-- 0/0            1536 2014-08-02 14:49 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar
drwxr-xr-x 0/0               0 2014-08-02 14:49 aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/
-rw-r--r-- 0/0               3 2014-08-02 14:49 aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/VERSION
-rw-r--r-- 0/0            1285 2014-08-02 14:49 aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/json
-rw-r--r-- 0/0            3584 2014-08-02 14:49 aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/layer.tar
drwxr-xr-x 0/0               0 2014-08-02 14:49 b1bd49907d559b703c2b7c1b0d6f120b5182440f7ac5f08636625d328e96f1ef/
-rw-r--r-- 0/0               3 2014-08-02 14:49 b1bd49907d559b703c2b7c1b0d6f120b5182440f7ac5f08636625d328e96f1ef/VERSION
-rw-r--r-- 0/0            1574 2014-08-02 14:49 b1bd49907d559b703c2b7c1b0d6f120b5182440f7ac5f08636625d328e96f1ef/json
-rw-r--r-- 0/0       222638592 2014-08-02 14:49 b1bd49907d559b703c2b7c1b0d6f120b5182440f7ac5f08636625d328e96f1ef/layer.tar
-rw-r--r-- 0/0              86 2014-08-02 14:49 repositories

# tar -xvf hoge.tar aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/layer.tar
aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/layer.tar

# tar -tvf aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/layer.tar 
-rw------- 0/0              32 2014-08-02 14:34 .bash_history
dr-xr-x--- 0/0               0 2014-08-02 14:34 root/
-rw-r--r-- 0/0               5 2014-08-02 14:34 root/hoge.txt

この例から分かるように、最後のレイヤーの中身は、追加したファイル「/root/hoge.txt」のみが含まれています。

ここまでをまとめると、次のようになります。

・Dockerのイメージは、イメージを作った時の手順に従ったレイヤー構造になっている。
・DockerHubからイメージをpullすると、各レイヤーの差分のtarファイルが個別にダウンロードされた後、dm-thinによるスナップショットと連携しながら、各イメージの実体を復元していく。
・saveコマンドで、再度、各レイヤーの差分をtarファイルとして取り出すことができる。

なお、dm-thinの仕組み上は、一旦、下位のディスクイメージが復元できれば、上位のイメージは削除することも可能です。(スナップショットは論理的には独立したデバイスとして扱えるので。)しかしながら、上位のイメージを削除すると、saveコマンドで差分を再現することができなくなります。そのため、イメージを復元した後も依存関係のある上位イメージは、内部的には削除せずに残されます。(というか、もともとAUFSでやってた時は、AUFSの仕組み上、消せなかったという事情もあると思いますが。)

「-a」オプションをつけると、内部的に保存されているイメージも表示することができます。

# docker images -a
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
hoge                latest              aff9664bf7d8        21 minutes ago      212.5 MB
centos              centos6             b1bd49907d55        2 days ago          212.5 MB
centos              centos7             b157b77b1a65        2 days ago          243.7 MB
centos              latest              b157b77b1a65        2 days ago          243.7 MB
<none>              <none>              34e94e67e63a        8 weeks ago         0 B
<none>              <none>              511136ea3c5a        13 months ago       0 B

また、「docker history」でイメージの親をたどることもできます。下記の例では、Dockerfileでビルドした時の中間イメージが親のイメージになっていることが分かります。

# docker history centos:centos6
IMAGE               CREATED             CREATED BY                                      SIZE
b1bd49907d55        30 hours ago        /bin/sh -c #(nop) ADD file:5e220e99326e53ed32   212.5 MB
34e94e67e63a        8 weeks ago         /bin/sh -c #(nop) MAINTAINER The CentOS Proje   0 B
511136ea3c5a        13 months ago                                                       0 B

DockerHubの内部構造

それでは、これらのイメージを保存するDockerHubの内部は、どのような構造になっているのでしょうか? DockerHubの中身を見ることはできないので、ローカルにプライベートレジストリーを構築して確認してみます。ここでは、ローカルの「/var/lib/docker-registry」にイメージが保存されるようにしています。

# mkdir /var/lib/docker-registry
# docker run -itd -p 5000:5000 -v /var/lib/docker-registry:/tmp/registry --name myregistry registry

プライベートレジストリーにpushするときは、はじめに、「ホスト名:ポート番号/リポジトリー:タグ名」というちょっと特殊なリポジトリー名をつける必要があります。

# docker tag hoge:latest localhost:5000/hoge:latest
# docker images | grep hoge
hoge                  latest              aff9664bf7d8        28 minutes ago      212.5 MB
localhost:5000/hoge   latest              aff9664bf7d8        28 minutes ago      212.5 MB

イメージIDを見ると分かるように、これは、同じイメージに対して、2種類のリポジトリー名を付与しているだけです。実体は同じです。

それでは、ローカルのプライベートレジストリーにpushします。

# docker push localhost:5000/hoge:latest
The push refers to a repository [localhost:5000/hoge] (len: 1)
Sending image list
Pushing repository localhost:5000/hoge (1 tags)
511136ea3c5a: Image successfully pushed 
34e94e67e63a: Image successfully pushed 
b1bd49907d55: Image successfully pushed 
aff9664bf7d8: Image successfully pushed 
Pushing tag for rev [aff9664bf7d8] on {http://localhost:5000/v1/repositories/hoge/tags/latest}

依存関係のある親のイメージがまとめてpushされていることが分かります。先ほどのsaveと同じ方法で、各レイヤーの差分tarを作成して、差分tarをレジストリー内に保存しています。

ほんまかいな、と思う方のために、イメージを保存したディレクトリーの中を直接に覗いてみます。

# ls -lR /var/lib/docker-registry/images/
/var/lib/docker-registry/images/:
合計 0
drwxr-xr-x 2 root root 60  8月  2 15:02 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a
drwxr-xr-x 2 root root 60  8月  2 15:02 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
drwxr-xr-x 2 root root 60  8月  2 15:02 aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2
drwxr-xr-x 2 root root 60  8月  2 15:02 b1bd49907d559b703c2b7c1b0d6f120b5182440f7ac5f08636625d328e96f1ef

/var/lib/docker-registry/images/34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a:
合計 16
-rw-r--r-- 1 root root   75  8月  2 15:02 _checksum
-rw-r--r-- 1 root root  136  8月  2 15:02 ancestry
-rw-r--r-- 1 root root 1541  8月  2 15:02 json
-rw-r--r-- 1 root root   23  8月  2 15:02 layer

/var/lib/docker-registry/images/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158:
合計 16
-rw-r--r-- 1 root root  75  8月  2 15:02 _checksum
-rw-r--r-- 1 root root  68  8月  2 15:02 ancestry
-rw-r--r-- 1 root root 483  8月  2 15:02 json
-rw-r--r-- 1 root root  86  8月  2 15:02 layer

/var/lib/docker-registry/images/aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2:
合計 16
-rw-r--r-- 1 root root   75  8月  2 15:02 _checksum
-rw-r--r-- 1 root root  272  8月  2 15:02 ancestry
-rw-r--r-- 1 root root 1285  8月  2 15:02 json
-rw-r--r-- 1 root root  225  8月  2 15:02 layer

/var/lib/docker-registry/images/b1bd49907d559b703c2b7c1b0d6f120b5182440f7ac5f08636625d328e96f1ef:
合計 74280
-rw-r--r-- 1 root root       75  8月  2 15:02 _checksum
-rw-r--r-- 1 root root      204  8月  2 15:02 ancestry
-rw-r--r-- 1 root root     1550  8月  2 15:02 json
-rw-r--r-- 1 root root 76048722  8月  2 15:02 layer

# file /var/lib/docker-registry/images/aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/layer 
/var/lib/docker-registry/images/aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/layer: gzip compressed data

# tar -tvzf /var/lib/docker-registry/images/aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2/layer
-rw------- 0/0              32 2014-08-02 14:34 .bash_history
dr-xr-x--- 0/0               0 2014-08-02 14:34 root/
-rw-r--r-- 0/0               5 2014-08-02 14:34 root/hoge.txt

まさに、先ほどsaveコマンドで取り出したのと同じ構造がディレクトリー内にできていることが分かります。

この時、/imagesディレクトリーの下は、イメージIDのみで管理されており、それぞれのイメージがどのリポジトリーに属するかなどの情報は無いことに注意してください。リポジトリーというのは、イメージに対する単なるラベル(リンク)であって、イメージの実体には直接関係していません。リポジトリーの情報は、別途、下記のディレクトリーに保存されています。

# ls -lR /var/lib/docker-registry/repositories
/var/lib/docker-registry/repositories:
合計 0
drwxr-xr-x 3 root root 17  8月  2 15:02 library

/var/lib/docker-registry/repositories/library:
合計 0
drwxr-xr-x 2 root root 75  8月  2 15:02 hoge

/var/lib/docker-registry/repositories/library/hoge:
合計 16
-rw-r--r-- 1 root root 304  8月  2 15:02 _index_images
-rw-r--r-- 1 root root 160  8月  2 15:02 json
-rw-r--r-- 1 root root  64  8月  2 15:02 tag_latest
-rw-r--r-- 1 root root 160  8月  2 15:02 taglatest_json

それではここで、イメージ「hoge:latest」の親となるイメージ「centos:centos6」をレジストリーにpushするとどうなるか分かるでしょうか? 実は、これに対応するイメージ本体「b1bd49907d55」はすでにレジストリーにpushされているので、再度、イメージが転送されることはありません。

# docker tag centos:centos6 localhost:5000/centos:centos6
# docker push localhost:5000/centos:centos6
The push refers to a repository [localhost:5000/centos] (len: 1)
Sending image list
Pushing repository localhost:5000/centos (1 tags)
Image 511136ea3c5a already pushed, skipping
Image 34e94e67e63a already pushed, skipping
Image b1bd49907d55 already pushed, skipping
Pushing tag for rev [b1bd49907d55] on {http://localhost:5000/v1/repositories/centos/tags/centos6}

上記の出力から分かるように、保存済みのイメージに対して、リポジトリー(タグ)の情報だけを追加しています。

つまり、Dockerのレジストリー(DockerHub)というのは、下図のような、すべての差分tarの巨大な保管庫が1つある形になります。それとは別に、ユーザー個別のリポジトリーの管理情報があって、各イメージに対するリンク情報が保存されているというわけです。

これを見ると、仮に、centosというリポジトリーのオーナーと、hogeというリポジトリーのオーナーが違っていても、実際のイメージのデータ(差分tar)は共有する形で保存されていることが分かります。世界中の多数のユーザーが、それぞれ個別にイメージをアップロードすると、DockerHubのディスクなんてすぐに一杯になる気がしますが、このように、世界中のすべてのユーザーで差分tarを共有することにより、ディスクの使用量を抑えているというわけですね。

ちなみに、DockerHubでは、「プライベートリポジトリー」を作成することもできますが、これも、プライベートなのは、右側のリポジトリーのメタ情報だけであって、その実体の差分tarは、他のユーザーと共有する形で、同じ保管庫に入っているようです。

レイヤーの統合方法

さて、こうなると、レイヤーを統合して、1つのイメージにしてからレジストリーにpushしたいと考える人もいるかも知れません。1つの方法は、イメージのexport/importです。

起動中のコンテナーに対して、「docker export」を実行すると、ルートファイルシステム以下を単純にtarにまとめたファイルが生成されます。これを元に、「docker import」で、再度、イメージを作成すると、親のイメージを持たない、シングルレイヤーのイメージになります。ただし、各種メタデータはなくなるので、そのあたりは、再度、追加が必要です。

# docker run -itd --name hoge hoge:latest /bin/bash
faee1d99e81038333f142f2f8fef78c5ff286e96e093fcd6704a3004164959f3

# docker export hoge > hoge_single.tar
# docker stop hoge
# docker rm hoge

# tar -tvf hoge_single.tar | head
dr-xr-xr-x 0/0               0 2014-08-02 15:32 ./
-rw------- 0/0              32 2014-08-02 14:34 .bash_history
-rwxr-xr-x 0/0               0 2014-08-02 15:32 .dockerenv
-rwxr-xr-x 0/0               0 2014-08-02 15:32 .dockerinit
dr-xr-xr-x 0/0               0 2014-07-26 12:45 bin/
-rwxr-xr-x 0/0           24232 2014-06-25 17:55 bin/arch
lrwxrwxrwx 0/0               0 2014-07-26 12:44 bin/awk -> gawk
-rwxr-xr-x 0/0           23720 2014-06-25 17:55 bin/basename
-rwxr-xr-x 0/0          903336 2013-07-18 22:19 bin/bash
-rwxr-xr-x 0/0           45224 2014-06-25 17:55 bin/cat

# cat hoge_single.tar | docker import - hoge:single
1bfcd9eeb52cc7168df943c78a1297120eb99c72363a44b218b2aaaf4828eb7a

# docker images -v hoge:single
Warning: '-v' is deprecated, it will be removed soon. See usage.
digraph docker {
 base -> "1bfcd9eeb52c" [style=invis]
 "1bfcd9eeb52c" [label="1bfcd9eeb52c\nhoge:single",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
 base [style=invisible]
}

レジストリー保存イメージの削除

それでは、レジストリーに保存したリポジトリーを削除すると、レジストリー内部では何がおきるのでしょうか。REST APIを使って、リポジトリーの削除を行ってみます。

# curl -XDELETE http://localhost:5000/v1/repositories/hoge/
true

# curl -XDELETE http://localhost:5000/v1/repositories/centos/
true

# ls -lR /var/lib/docker-registry/images/
/var/lib/docker-registry/images/:
合計 0
drwxr-xr-x 2 root root 60  8月  2 15:02 34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a
drwxr-xr-x 2 root root 60  8月  2 15:02 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
drwxr-xr-x 2 root root 60  8月  2 15:02 aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2
drwxr-xr-x 2 root root 60  8月  2 15:02 b1bd49907d559b703c2b7c1b0d6f120b5182440f7ac5f08636625d328e96f1ef

/var/lib/docker-registry/images/34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a:
合計 16
-rw-r--r-- 1 root root   75  8月  2 15:02 _checksum
-rw-r--r-- 1 root root  136  8月  2 15:02 ancestry
-rw-r--r-- 1 root root 1541  8月  2 15:02 json
-rw-r--r-- 1 root root   23  8月  2 15:02 layer

/var/lib/docker-registry/images/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158:
合計 16
-rw-r--r-- 1 root root  75  8月  2 15:02 _checksum
-rw-r--r-- 1 root root  68  8月  2 15:02 ancestry
-rw-r--r-- 1 root root 483  8月  2 15:02 json
-rw-r--r-- 1 root root  86  8月  2 15:02 layer

/var/lib/docker-registry/images/aff9664bf7d8eed96731fffc05a2002306e09ca5e22290354eeca6fa577fe8b2:
合計 16
-rw-r--r-- 1 root root   75  8月  2 15:02 _checksum
-rw-r--r-- 1 root root  272  8月  2 15:02 ancestry
-rw-r--r-- 1 root root 1285  8月  2 15:02 json
-rw-r--r-- 1 root root  225  8月  2 15:02 layer

/var/lib/docker-registry/images/b1bd49907d559b703c2b7c1b0d6f120b5182440f7ac5f08636625d328e96f1ef:
合計 74280
-rw-r--r-- 1 root root       75  8月  2 15:02 _checksum
-rw-r--r-- 1 root root      204  8月  2 15:02 ancestry
-rw-r--r-- 1 root root     1550  8月  2 15:02 json
-rw-r--r-- 1 root root 76048722  8月  2 15:02 layer

はい。イメージの実体(差分tar)はしっかり残っています。リポジトリーからのリンク情報を削除しているだけのようです。

それぞれのイメージ(差分tar)には、UUID(世界で唯一の固有のID)としてのイメージIDが振られているので、再度、同じイメージをpushした際は、保存済みイメージが再利用されますので、「ゴミ」というわけではありません。一度、作成して、レジストリーにpushしたイメージは、世界に固有の存在として永遠に残るという考え方のようですね。。。。

なお、これはレジストリー内部の話であって、ローカルにダウンロードしたイメージの場合は、子供のイメージを削除して、依存関係の無くなった親イメージは自動的に削除されるようになっています。