首页 > 解决方案 > docker image layer ids是如何派生的

问题描述

试图了解 docker 镜像层 id 是如何得出的。

在基于 linux 的 VM 上,我拉取了一个 ubuntu 20.04 映像,如下所示。

码头工人拉ubuntu:20.04

然后我将其保存为 tar 文件,然后将其解压缩。

码头工人保存 ubuntu:20.04 > ubuntu2004.tar
tar -xvf ubuntu2004.tar

我已经将一个文件夹安装到我的虚拟机,所以现在我在我的 Windows 机器上看到了提取的 tar,如下所示。

Docker层结构

您可能知道 4 个文件夹包含图像的 4 层。看起来很长的文件夹名称是图层的 ID。在这些文件夹中,我们可以看到一个 json 文本文件,它有一个 json 对象。此 Json 对象也具有相同的图层 ID。所以 id 是 1c87ad44cc6b364480a5340ab1050b8dfb1691ed2abc85a1dbc3ee2fb5f2cf06

问题:这些 id 是如何获得的?

下面总结一下我在这方面所做的研究。

  1. 我读过的一篇文章说它们是随机生成的。

用于存储层内容的 diff 目录现在以随机生成的“缓存 ID”命名,Docker 引擎维护层与其缓存 ID 之间的链接,以便知道在磁盘上的何处定位层的内容。

我已经启动了多个虚拟机,提取了相同的 ubuntu:20.04 映像,然后提取到最终发现层 ID 完全相同。所以我得出结论,我的主机 VM 上的 docker 引擎一定不能随机生成这些 id。它必须要么使用一些逻辑来生成这些 ID。或者从中提取的存储库必须已经具有这些 ID。

Jessica G在这里深入研究了 docker 层并说了同样的话,即层 id 是随机生成的。

随着每一步,所创建的层都由其随机生成的 ID 列出。

  1. 在我遇到的这篇文章中,描述了chainid。首先,我能够按照那里的描述正确评估 imageId 和 diffids。现在为chainIds。对于底层,它表示链 id 与 diff id 相同。

对于底层:ChainID(layer0) = DiffID(layer0)

对于其他层:ChainID(layerN) = SHA256hex(ChainID(layerN-1) + " " + DiffID(layerN))

我观察到,对于任何层,id 都不同于 diff id。我是我在这里错过了一些东西。或者这篇文章可能已经过时了。

  1. Graham Jenson在这篇文章中总结道“文件名和文件夹结构无关紧要”。滚动到最后,你会看到。

所以到目前为止,我无法了解 docker 引擎是如何生成 id 的。还是推送的时候在repository生成,docker引擎拉取原样?我查看了此处描述的Moby 项目的shell 脚本。它将层的 id 生成为图像层 sha 的 sha256。首先获取layer.tar文件的sha256。我再次猜测 sha 是从那个 sha 中获得的,并用作图层的 id。但是这里的问题是这个层 id 与我提取后发现的不匹配。

任何指向正确方向的指针都将不胜感激。

标签: docker

解决方案


这有点解开,因为没有一个 id。首先,我建议查看OCI 规范,因为它介绍了图像清单是什么、配置和各个层。这就是您在注册表中看到的内容。以我拉下的 nginx 镜像为例,让我们来看看这些。

首先是 docker manifest 列表,或 OCI 索引:

$ regctl manifest get --list localhost:5000/library/nginx --format '{{jsonPretty .}}'
{
  "manifests": [
    {
      "digest": "sha256:7250923ba3543110040462388756ef099331822c6172a050b12c7a38361ea46f",
      "mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      },
      "size": 1570
    },
    {
      "digest": "sha256:bb1416167bc0274d8ad2eadaef292880f59a9fa67dd3dd2149a48f9ab6f3bb79",
      "mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "arm",
        "os": "linux",
        "variant": "v5"
      },
      "size": 1570
    },
    {
      "digest": "sha256:28511266c47574675169b06eddb33c759dd6b7964b87fc4b66460e24e20fdb92",
      "mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "arm",
        "os": "linux",
        "variant": "v7"
      },
      "size": 1570
    },
    {
      "digest": "sha256:bf6ac873f0bc7e0a3454ffea4ecf93145c712508a1ca4f125c82a004f5d798a5",
      "mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "arm64",
        "os": "linux",
        "variant": "v8"
      },
      "size": 1570
    },
    {
      "digest": "sha256:387da9849102b73c06e645a8f0f40c72fae755f20d33391596041a4dcf8284a9",
      "mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "386",
        "os": "linux"
      },
      "size": 1570
    },
    {
      "digest": "sha256:44b406885395b1ded51fd3e2715de09f581fdaa3b8c43837bdc4f179dd7e91e8",
      "mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "mips64le",
        "os": "linux"
      },
      "size": 1570
    },
    {
      "digest": "sha256:1312ed2db68926470e224d61d7a9a6b77b9dc8061c6b71f0a7f634e184d7aa2a",
      "mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      },
      "size": 1570
    },
    {
      "digest": "sha256:8ce8b5ec5634ff03911fb9d2548e9d112c79274de1a6d7192f2270b0d80e4d25",
      "mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "s390x",
        "os": "linux"
      },
      "size": 1570
    }
  ],
  "mediaType": "application\/vnd.docker.distribution.manifest.list.v2+json",
  "schemaVersion": 2
}

在那里,您可以看到指向此图像中包含的每个平台的每个清单的指针。此清单列表本身有一个摘要,因此如果您正在寻找图像摘要,请意识到可能有两个,一个用于索引,另一个用于您的平台特定图像:

$ docker image inspect localhost:5000/library/nginx
[                                                  
    {                                              
        "Id": "sha256:87a94228f133e2da99cb16d653cd1373c5b4e8689956386c1c12b60a20421a02",
        "RepoTags": [
            "nginx:latest",
            "localhost:5000/library/nginx:latest"
        ],                                      
        "RepoDigests": [         
            "nginx@sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36",
            "localhost:5000/library/nginx@sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36" 
        ],
...
$ regctl manifest get --list localhost:5000/library/nginx
Name:        localhost:5000/library/nginx
MediaType:   application/vnd.docker.distribution.manifest.list.v2+json
Digest:      sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36

Manifests:

  Name:      localhost:5000/library/nginx@sha256:7250923ba3543110040462388756ef099331822c6172a050b12c7a38361ea46f
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/amd64
...

$ regctl manifest get --list localhost:5000/library/nginx \
  --format raw-body | sha256sum
644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36  -

镜像本身是由一个配置和一个层数组组成的,它们在注册表中存储为 blob,这里的所有内容都是内容可寻址存储,您拉取的内容的 sha256sum 与您拉取的内容的名称相同.

$ regctl manifest get localhost:5000/library/nginx@sha256:7250923ba3543110040462388756ef099331822c6172a050b12c7a38361ea46f
{        
  "schemaVersion": 2,
  "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 
  "config": {        
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "size": 7731,                                       
    "digest": "sha256:87a94228f133e2da99cb16d653cd1373c5b4e8689956386c1c12b60a20421a02" 
  }, 
  "layers": [ 
    {         
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 27139510,                            
      "digest": "sha256:b380bbd43752f83945df8b5d1074fef8dd044820e7d3aef33b655a2483e030c7"
    },
    { 
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", 
      "size": 26638539,                              
      "digest": "sha256:fca7e12d1754baddbd07178dd1693c726e9d792c0c9659208e3f4b474dc41a7c"
    },
    { 
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 602,                                  
      "digest": "sha256:745ab57616cb3c803b3a00c3bd46fd0d94762bd5b9446eadc877cb7400fb6c11"
    },
    { 
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 894,
      "digest": "sha256:a4723e260b6fedec963910b3ae53e939fd58cbad8232258576ba26765cdcf522"
    }, 
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 666,                                  
      "digest": "sha256:1c84ebdff6819c01d0dc43a59ee391cc2cb14f7aba20cac0af1b04fb78652fc9"
    },
    { 
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", 
      "size": 1394,                                  
      "digest": "sha256:858292fd2e56e240ab472db6e9fabd5fd390486660978fcf8d65a06a04c00971"
    }
  ]
}

$ regctl manifest get \
  localhost:5000/library/nginx@sha256:7250923ba3543110040462388756ef099331822c6172a050b12c7a38361ea46f \
  --format raw-body | sha256sum
7250923ba3543110040462388756ef099331822c6172a050b12c7a38361ea46f  -

层本身是 tar+gz blob,您可以检查它们,正如您所期望的,它们是相同的内容可寻址存储:

$ regctl blob get \
  localhost:5000/library/nginx sha256:b380bbd43752f83945df8b5d1074fef8dd044820e7d3aef33b655a2483e030c7 \
  | tar -tvzf - | head
drwxr-xr-x 0/0               0 2021-10-10 20:00 bin/
-rwxr-xr-x 0/0         1168776 2019-04-18 00:12 bin/bash
-rwxr-xr-x 0/0           43744 2019-02-28 10:30 bin/cat
-rwxr-xr-x 0/0           64320 2019-02-28 10:30 bin/chgrp
-rwxr-xr-x 0/0           64288 2019-02-28 10:30 bin/chmod
-rwxr-xr-x 0/0           72512 2019-02-28 10:30 bin/chown
-rwxr-xr-x 0/0          146880 2019-02-28 10:30 bin/cp
-rwxr-xr-x 0/0          121464 2019-01-17 14:08 bin/dash
-rwxr-xr-x 0/0          109408 2019-02-28 10:30 bin/date
-rwxr-xr-x 0/0           76712 2019-02-28 10:30 bin/dd

$ regctl blob get \
  localhost:5000/library/nginx sha256:b380bbd43752f83945df8b5d1074fef8dd044820e7d3aef33b655a2483e030c7 \
  | sha256sum
b380bbd43752f83945df8b5d1074fef8dd044820e7d3aef33b655a2483e030c7  -

现在查看图像检查时,这些 id 将不匹配:

        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:e81bff2725dbc0bf2003db10272fef362e882eb96353055778a66cda430cf81b",
                "sha256:43f4e41372e42dd32309f6a7bdce03cf2d65b3ca34b1036be946d53c35b503ab",
                "sha256:788e89a4d186f3614bfa74254524bc2e2c6de103698aeb1cb044f8e8339a90bd",
                "sha256:f8e880dfc4ef19e78853c3f132166a4760a220c5ad15b9ee03b22da9c490ae3b",
                "sha256:f7e00b807643e512b85ef8c9f5244667c337c314fa29572206c1b0f3ae7bf122",
                "sha256:9959a332cf6e41253a9cd0c715fa74b01db1621b4d16f98f4155a2ed5365da4a"
            ]
        },

那是因为拉取镜像后主机上的内容可寻址存储是基于解压层的:

$ regctl blob get \
  localhost:5000/library/nginx sha256:b380bbd43752f83945df8b5d1074fef8dd044820e7d3aef33b655a2483e030c7 \
  | gunzip - | sha256sum
e81bff2725dbc0bf2003db10272fef362e882eb96353055778a66cda430cf81b  -

最后,还有两个更复杂的部分,docker 中的层目录名称和 save 中的文件名。查看代码,图层目录名称是随机生成的唯一 ID 。并且保存文件中的文件名基于一些v1 diff id,用于使用该点之前的层创建的图像配置以及在该配置上计算的摘要。这样做只是为了支持真正旧版本的 docker,并且您可以轻松创建具有不同文件名的保存文件,docker 只关心manifest.json. 甚至可以将OCI 布局定义与 docker save 格式混合并让 docker 加载图像:

$ regctl image export \
  localhost:5000/regclient/regctl:edge@sha256:76c041ed3bf9e4186327d2d63c9c597c648a7fbc07642d63f223c899e29f8d89 \
  >export.tar

$ tar -tvf export.tar
-rw-r--r-- 0/0              30 1969-12-31 19:00 oci-layout
-rw-r--r-- 0/0             389 1969-12-31 19:00 index.json
-rw-r--r-- 0/0            1435 1969-12-31 19:00 manifest.json
drwxr-xr-x 0/0               0 1969-12-31 19:00 blobs/sha256
-rw-r--r-- 0/0            1152 1969-12-31 19:00 blobs/sha256/76c041ed3bf9e4186327d2d63c9c597c648a7fbc07642d63f223c899e29f8d89
-rw-r--r-- 0/0            3021 1969-12-31 19:00 blobs/sha256/bd45abf90c52fb2c13499bbd7bb845c106e0d5b924e65dd26c8e8e2de25e54f6
-rw-r--r-- 0/0             941 1969-12-31 19:00 blobs/sha256/f6e2d7fa40092cf3d9817bf6ff54183d68d108a47fdf5a5e476c612626c80e14
-rw-r--r-- 0/0          122412 1969-12-31 19:00 blobs/sha256/92365f35877078c3e558e9a66ac083fe9a8d44bdb3150bdac058380054b05972
-rw-r--r-- 0/0             146 1969-12-31 19:00 blobs/sha256/fa98de7a23a1c3debba4398c982decfd8b31bcfad1ac6e5e7d800375cefbd42f
-rw-r--r-- 0/0         3536065 1969-12-31 19:00 blobs/sha256/24bfb25bb9426e2205338ab1480992e9a09bd6d2d9f248d3768f4feb12ad7d9e

$ tar -xvf export.tar manifest.json 
manifest.json

$ cat manifest.json | jq .
[
  {
    "Config": "blobs/sha256/bd45abf90c52fb2c13499bbd7bb845c106e0d5b924e65dd26c8e8e2de25e54f6",
    "RepoTags": [
      "localhost:5000/regclient/regctl:edge"
    ],
    "Layers": [
      "blobs/sha256/f6e2d7fa40092cf3d9817bf6ff54183d68d108a47fdf5a5e476c612626c80e14",
      "blobs/sha256/92365f35877078c3e558e9a66ac083fe9a8d44bdb3150bdac058380054b05972",
      "blobs/sha256/fa98de7a23a1c3debba4398c982decfd8b31bcfad1ac6e5e7d800375cefbd42f",
      "blobs/sha256/24bfb25bb9426e2205338ab1480992e9a09bd6d2d9f248d3768f4feb12ad7d9e"
    ],
    "LayerSources": {
      "sha256:24bfb25bb9426e2205338ab1480992e9a09bd6d2d9f248d3768f4feb12ad7d9e": {
        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
        "digest": "sha256:24bfb25bb9426e2205338ab1480992e9a09bd6d2d9f248d3768f4feb12ad7d9e",
        "size": 3536065
      },
      "sha256:92365f35877078c3e558e9a66ac083fe9a8d44bdb3150bdac058380054b05972": {
        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
        "digest": "sha256:92365f35877078c3e558e9a66ac083fe9a8d44bdb3150bdac058380054b05972",
        "size": 122412
      },
      "sha256:f6e2d7fa40092cf3d9817bf6ff54183d68d108a47fdf5a5e476c612626c80e14": {
        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
        "digest": "sha256:f6e2d7fa40092cf3d9817bf6ff54183d68d108a47fdf5a5e476c612626c80e14",
        "size": 941
      },
      "sha256:fa98de7a23a1c3debba4398c982decfd8b31bcfad1ac6e5e7d800375cefbd42f": {
        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
        "digest": "sha256:fa98de7a23a1c3debba4398c982decfd8b31bcfad1ac6e5e7d800375cefbd42f",
        "size": 146
      }
    }
  }
]

$ docker load <export.tar
132414a5f587: Loading layer [==================================================>]     941B/941B
482fa2862396: Loading layer [==================================================>]  122.4kB/122.4kB
8e47dcad786a: Loading layer [==================================================>]     146B/146B
65131f050950: Loading layer [==================================================>]  3.536MB/3.536MB
The image localhost:5000/regclient/regctl:edge already exists, renaming the old one with ID sha256:5ec718d68e782ea7df08e19af7b84de3c1d34b81fabe48c89a43c3439c9063dd to empty string 
Loaded image: localhost:5000/regclient/regctl:edge

(请注意,我在最后一个示例中切换到不同的图像,因为 docker 引擎已经具有 nginx 示例的层。)


推荐阅读