预备动作

ctr 下载镜像,导出为压缩包。并解压到文件夹。

镜像文件层级

image-spec/image-layout.md at main · opencontainers/image-spec · GitHub

镜像布局的结构如下:

$ tar xvf wg.tar -C wg-image
blobs/sha256/0303a59d29548085675c8dc77b594cfdaed591c2d3dc2eedb7f86423d68d4217
blobs/sha256/0350af939280a3c1cd93680faa5890636e2b2e1853c59bfa543fbf1dd3022123
blobs/sha256/0c885228653809eeb06ad27eca297d2dcc22e7e46056f420a83ead31d674eff7
blobs/sha256/0ff5eb3a6b4475e3dbd12f3e2683ea873ed76e3ad34a7b81a31ea77d98034d4b
blobs/sha256/2d2e0fc760791a12a90fa8101ab88f8e8dfc178539d0b3ead91f0394b124da81
blobs/sha256/38ae9c4bfd7c2b34961b19ff96a4e8930eb545b81a08a42e680beca6327eea43
blobs/sha256/5b7522ab11647cb4fc176d2817b3425b6a03bec4cced283746f0bf9b1767d1d0
blobs/sha256/96d9ee2f8e97cc43d61fe2c01555543a411ee2662f4ffb514b9580200c2692d2
blobs/sha256/9a36f2af670028da7e5201a9ad40616f66a2e07af074439103f3b0ead5d61a26
blobs/sha256/bed0e8f20ee92bd6b5867a0f0bd8f6cb3490b103539b1607f1621e3493ccd19f
blobs/sha256/e0fb2429c802bef7d81813dfd5a6a4e3f9b06b86b103a87c02cfbecc5cc9150e
blobs/sha256/fc515c5e05210d9f5f813831bc05ec38810e226c4715582e8d36ee42ceccec1c
index.json
manifest.json
oci-layout
  • blobs 目录
    • blobs 子目录中的对象名称由每种哈希算法的目录组成,其子目录将包含实际内容。
    • blobs/<alg>/<encoded> 中的内容必须与摘要 <alg>:<encoded> 匹配。
    • 目录可能包含未被任何引用所引用的 Blob。
    • 如果缺少引用的 Blob,则应通过外部 Blob 存储来补充。
  • oci-layout 文件
    • 该 JSON 对象作为开放容器镜像布局的标记,并提供正在使用的镜像布局版本。imageLayoutVersion 值将与 OCI 镜像规范版本对齐,并在需要更改镜像布局时进行固定。
  • index.json 文件
    • 这是一个必需的文件,作为镜像布局的引用和描述符的入口点。该索引提供了一个已建立的路径(/index.json),以便在镜像布局中发现辅助描述符(auxiliary descriptors)。
    • 每个描述符对象的 mediaType 字段将是 index 或 manifest
  • manifest.json 文件
    • Docker 格式镜像使用的 manifest.json,ctr image export 时会默认生成,可以增加 --skip-manifest-json 参数不生成此文件。
    • 在containerd InportIndex函数导入index时,若存在 oci-layout 则不会读取本文件。否则认为是 Docker v1.1 或 v1.2 的镜像格式,会读取本文件,并转换为OCI格式处理。

OCI镜像各个类型的组织形式:

image.png

index

image index是一个高层级的manifest,用于指向特定的 image manifest,以支持一个或多个平台。

mediaType为 application/vnd.oci.image.index.v1+json

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}
  • schemaVersion int 这是一个必需属性,指定图像清单的模式版本。对于本规范的版本,这必须是 2,以确保与旧版本 Docker 的向后兼容性。此字段的值将不会改变,未来的规范版本中可能会移除此字段。

  • mediaType string 此属性应被使用,并与本规范的早期版本及其他类似外部格式保持兼容。当使用时,此字段必须包含媒体类型 application/vnd.oci.image.index.v1+json。该字段的用法与描述符的 mediaType 使用不同。

  • manifests array of objects 此必需属性包含特定平台的图像清单列表。虽然该属性必须存在,但数组的大小可以为零。 manifests 中的每个对象包括一组描述符属性,具有以下附加属性和限制:

    • mediaType string 此描述符属性有额外的限制。实现必须至少支持以下媒体类型:

      • application/vnd.oci.image.manifest.v1+json 实现应支持以下媒体类型:
      • application/vnd.oci.image.index.v1+json(嵌套索引)

      关注可移植性的图像索引应使用上述媒体类型之一。未来版本的规范可能会使用不同的媒体类型(即新的版本格式)。遇到未知的 mediaType 不应导致实现错误。

    • platform 对象 此可选属性描述图像的最低运行时要求。如果目标是特定平台,则此属性应存在。

      • architecture string 此必需属性指定 CPU 架构。图像索引应使用,并且实现应理解,在 Go 语言文档中列出的值。
      • os string 此必需属性指定操作系统。图像索引应使用,并且实现应理解,在 Go 语言文档中列出的值。
      • os.version string 此可选属性指定目标操作系统的版本。实现可能拒绝使用 os.version 不知道与主机 OS 版本兼容的清单。有效值由实现定义。例如,在 Windows 上为 10.0.14393.1066

    如果多个清单符合客户端或运行时的要求,则应使用第一个匹配条目。

  • annotations 字符串-字符串映射 此可选属性包含图像索引的任意元数据。此可选属性必须使用注释规则。

另外像以下 wireguard 在index中使用了 vnd.docker.distribution.manifest.list.v2+json 这一类型的数据,也被称为fat manifest

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
      "digest": "sha256:0c885228653809eeb06ad27eca297d2dcc22e7e46056f420a83ead31d674eff7",
      "size": 685,
      "annotations": {
        "containerd.io/distribution.source.lscr.io": "linuxserver/wireguard",
        "io.containerd.image.name": "lscr.io/linuxserver/wireguard:legacy",
        "org.opencontainers.image.ref.name": "legacy"
      }
    }
  ]
}

manifest

manifest有三个用处。 第一个用处是内容可寻址的image; 第二个用处是支持不同的platform; 第三个用处是可转换为 OCI 运行时规范。

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:517b897a6a8312ce202a85c8a517d820b0fc5b6f5d14ec2a3267906f75680403",
    "size": 372
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:430378704d12f9a980f41ae1a29c587974e1f0234d5dab0765fa95a4d764622e",
      "size": 2155944
    }
  ],
  "annotations": {
    "org.opencontainers.image.url": "https://github.com/docker-library/busybox",
    "org.opencontainers.image.version": "1.37.0-glibc"
  }
}

config

config 用于指定镜像配置,application/vnd.oci.image.config.v1+json

例如上文的sha256:517b897a6a8312ce202a85c8a517d820b0fc5b6f5d14ec2a3267906f75680403 记录如下:

{
  "config": {
    "Cmd": [
      "sh"
    ]
  },
  "created": "2024-09-26T21:31:42Z",
  "history": [
    {
      "created": "2024-09-26T21:31:42Z",
      "created_by": "BusyBox 1.37.0 (glibc), Debian 12"
    }
  ],
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:dab88e68d7096681d624de8bd542df9d91bae06c739a73483b6fb0e6869421ea"
    ]
  },
  "architecture": "amd64",
  "os": "linux"
}

其中内容也是可以明白的。其中rootfs 中的layers 又是一堆分平台的manifests,同样会回到本章描述的 manifest

layers

layers 指定了本镜像的各个层级。其类型为

  • application/vnd.oci.image.layer.v1.tar
  • application/vnd.oci.image.layer.v1.tar+gzip
  • application/vnd.oci.image.layer.v1.tar+zstd
  • application/vnd.oci.image.layer.nondistributable.v1.tar
  • application/vnd.oci.image.layer.nondistributable.v1.tar+gzip
  • application/vnd.oci.image.layer.nondistributable.v1.tar+zstd

nondistributable 表示不可分发,不会上传到网络中。 gzip / zstd 为压缩格式。

使用 tar 即可看到该层文件,使用正则过滤一层目录:

$ tar -tf busybox-image/blobs/sha256/430378704d12f9a980f41ae1a29c587974e1f0234d5dab0765fa95a4d764622e | grep -P "^[a-z]+/$"
bin/
dev/
etc/
home/
lib/
root/
tmp/
usr/
var/

后续

结合busybox和wireguard镜像分析 OCI 镜像规范后,实际会存在很多疑问。例如

  • manifest.json 是什么,怎么处理的。(上文大致有写)
  • vnd.docker.distribution.manifest.list.v2+json 这类媒体类型应该怎么处理。
  • OCI镜像 / Docker镜像在 containerd 中怎么做到兼容的,又怎么下发为 OCI运行时标准给 runc的。
  • .config.StopSignal and .config.Labels 这种被称为 意外写入 Docker v1.2镜像标准 的配置,在未来会怎么去处理。

这些疑问都需要进现在首选的 high-level 运行时 containerd 代码里去看。

参考资料