容器OCI规范 镜像规范

容器OCI规范 镜像规范

整篇文章还没有润色,条理性还不够强,Image Layerd的规范还没来得及写。还只是半成品,先放出来给有兴趣的同学看一下。

前言

OCI 镜像规范初步

从一个比较高层次的角度来看这个规范,主要有以下内容:

  • 一个镜像由四部分组成:Manifest、Image Index (可选)、Layers、Configuration。
  • 根据存储内容的密码学哈希值找到镜像存储的位置,根据内容寻址的描述格式。(Content Descriptors)
  • 一种格式存储CAS斑点和引用它们(可选OCI层)
  • 签名是基于签名的图像内容的地址(可选OCI层)
  • 命名是联合基于DNS,可以授权(可选OCI层)

Manifest 包括镜像内容的元信息和镜像层的摘要信息,这些镜像层可以解包部署成最后的运行环境。
Configuration 则包含了应用的参数环境。
Index 则从更高的角度描述了Manifest,主要应用于镜像跨平台。

OCI 文件类型

Media Type

  • application/vnd.oci.descriptor.v1+json: Content Descriptor(内容描述文件)
  • application/vnd.oci.layout.header.v1+json: OCI Layout(布局描述文件)
  • application/vnd.oci.image.index.v1+json: Image Index(高层次的镜像元信息文件)
  • application/vnd.oci.image.manifest.v1+json: Image Manifest(镜像元信息文件)
  • application/vnd.oci.image.config.v1+json: Image Config(镜像配置文件)
  • application/vnd.oci.image.layer.v1.tar: Image Layer(镜像层文件)
  • application/vnd.oci.image.layer.v1.tar+gzip: Image Layer(镜像层文件), gzip压缩
  • application/vnd.oci.image.layer.nondistributable.v1.tar: Image Layer, 非内容寻址管理
  • application/vnd.oci.image.layer.nondistributable.v1.tar+gzip: Image Layer, gzip压缩,非内容寻址管理

Media Type 的不一致

当获取块文件时,可以返回文件的Media Type,比如Http请求的返回头中设置Content-Type字段。镜像规范的实现也可能会有预期的Media Type。

  • 当你没有预期的Media Type时,信任块文件返回的Media type类型。
  • 当你预期的Media Type和返回一致时,是正常情况。
  • 当你预期的Media Type和返回不一致时,你必须:
    • 如果你的预期的Digest和返回块的Digest一致时,报一个警告。
    • 如果你的预期的Digest和返回块的Digest不一致时,返回错误。
    • 如果你没有预期的Digest,返回错误。

OCI Content Descriptor

  • OCI 镜像由几部分组成,每个组件都是存储在一个目录结构中。
  • 每个组件之间都是通过内容寻址的方式互相引用。
  • Content Descriptor 描述了一个目标内容的位置。
  • Content Descriptor 包含:内容的类型、内容的标识符、内容的大小。
  • Content Descriptor 必须通过嵌入到其他格式中使用。

Content Descriptor 属性

  • mediaType string
  • digest string
  • size int64
  • urls array of strings
  • annotations string-string map
  • data string 保留字段

摘要和校验

digest属性是Descriptor的核心,扮演了内容的标识符的角色。digest使用了防碰撞的哈希算法唯一的标识了内容。如果标识符能够以安全的获取。那么就算内容通过了不安全的来源获取,也能独立计算出标识符对内容的正确性进行确认。

digest的格式要求与示例:(sha256已经是一个广泛使用的Hash算法)

digest      := algorithm ":" hex
algorithm   := /[a-z0-9_+.-]+/
hex         := /[a-f0-9]+/

sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b
  • 1
  • 2
  • 3
  • 4
  • 5

Content Descriptor 示例

这个 Content Descriptor 描述了一个Manifest

{
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "size": 7682,
  "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
}
  • 1
  • 2
  • 3
  • 4
  • 5

OCI 镜像布局规范

  • OCI的镜像布局将内容寻址和位置寻址做了很明显的分割。
  • 这个布局可以使用多种方式传输:归档文件、共享文件系统、网络传输等

详细内容

镜像布局有以下部分组成:

  • blobs directory
    • 内容寻址的块文件
    • A blob has no schema and SHOULD be considered opaque ?
    • 目录必须存在,但是可以为空
  • oci-layout file
    • 文件必须存在、必须是JSON格式
    • 文件中必须包含一个imageLayoutVersion字段
    • 文件中可以有其他字段作为扩展使用
  • index.json file
    • 文件必须存在、必须是JSON格式
    • 文件中必须包含镜像Index的基本属性
示例
$ find . -type f
./index.json
./oci-layout
./blobs/sha256/3588d02542238316759cbf24502f4344ffcc8a60c803870022f335d1390c13b4
./blobs/sha256/4b0bc1c4050b03c95ef2a8e36e25feac42fd31283e8c30b3ee5df6b043155d3c
./blobs/sha256/7968321274dc6b6171697c33df7815310468e694ac5be0ec03ff053bb135e768
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Blobs 文件夹

  • blobs文件夹下的子文件夹是以Hash算法的名称来命名的,这些子文件夹下包含了真正的实体文件。
  • 一个块被digest引用时(:,descriptor),这个块必须存放在blobs/<alg>/<hex>目录。
  • blobs文件夹下面可能会存放很多已经没有任何引用的块文件。
  • blobs文件夹下可以缺失一些被引用的块文件,只要被其他额外的块填满就可以了。
Blobs 示例

Image Index

$ cat ./blobs/sha256/9b97579de92b1c195b85bb42a11011378ee549b02d7fe9c17bf2a6b35d5cb079 | jq
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    },
    ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Image Manifest

$ cat ./blobs/sha256/afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51 | jq
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 7023,
    "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 32654,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
    },
    ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Image Config

$ cat ./blobs/sha256/5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270 | jq
{
  "architecture": "amd64",
  "author": "Alyssa P. Hacker <alyspdev@example.com>",
  "config": {
    "Hostname": "8dfe43d80430",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Cmd": null,
    "Image": "sha256:6986ae504bbf843512d680cc959484452034965db15f75ee8bdd1b107f61500b",
    ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Image Layer Blob

$ cat ./blobs/sha256/e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f
[tar stream]
  • 1
  • 2

oci-layout 文件

在镜像规范中,这个文件特别简单,只有一个布局版本的字段。

oci-layout 示例
{
    "imageLayoutVersion": "1.0.0"
}
  • 1
  • 2
  • 3

index.json 文件

index.json文件相当于整个镜像的入口。从这个文件可以获取整个镜像依赖到的所有文件的信息。
每一个在manifests字段中的descriptor都指向一个 application/vnd.oci.image.index.v1+json 或 application/vnd.oci.image.manifest.v1+json 类型的文件。

一个通用的做法,org.opencontainers.image.ref.name注解被认为是镜像Tag的含义。表示镜像的不同版本。

index.json 示例
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.index.v1+json",
      "size": 7143,
      "digest": "sha256:0228f90e926ba6b96e4f39cf294b2586d38fbb5a1e385c05cd1ee40ea54fe7fd",
      "annotations": {
        "org.opencontainers.image.ref.name": "stable-release"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      },
      "annotations": {
        "org.opencontainers.image.ref.name": "v1.0"
      }
    },
    {
      "mediaType": "application/xml",
      "size": 7143,
      "digest": "sha256:b3d63d132d21c3ff4c35a061adf23cf43da8ae054247e32faa95494d904a007e",
      "annotations": {
        "org.freedesktop.specifications.metainfo.version": "1.0",
        "org.freedesktop.specifications.metainfo.type": "AppStream"
      }
    }
  ],
  "annotations": {
    "com.example.index.revision": "r124356"
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

OCI Image Manifest

官网中列举了Manifest的三大主要目标,但我觉得Manifest的主要任务就只有一个。
通过内容描述的方式,记录一个镜像的元信息,包括Image Config和Image Layers。

详细内容

  • schemaVersion int
    这个字段是必须的,以当前版本的规范你必须填2(为了兼容老版本的Docker)。
  • mediaType string
    保留字段,用于标识当前文件是什么类型的。
  • config descriptor
    这个字段是必须的,用于标识镜像的配置文件的位置。
    • mediaType string
      配置文件的mediaType,必须使用这个application/vnd.oci.image.config.v1+json
  • layers array of descriptor
    列表中的每一个成员都是一个镜像层,最底层的镜像在列表的第一项,其他各层按照堆叠顺序依次排列在后面。最终得到的文件系统应该和在一个空文件夹上堆叠各层得到的结果一样。用于堆叠的空文件夹的各种权限是不确定的。
    • mediaType string
      镜像层的mediaType,有比较多的选择:
      • application/vnd.oci.image.layer.v1.tar
      • application/vnd.oci.image.layer.v1.tar+gzip
      • application/vnd.oci.image.layer.nondistributable.v1.tar
      • application/vnd.oci.image.layer.nondistributable.v1.tar+gzip
  • annotations string-string map
    注释,和其他定义一样。

示例

{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 7023,
    "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 32654,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 16724,
      "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 73109,
      "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

OCI Image Index

Image Index是一个更高层次的Manifest,一般在一个镜像需要提供多个平台支持时使用。
MediaType:application/vnd.oci.image.index.v1+json

详细内容

  • schemaVersion int
    这个字段是必须的,以当前版本的规范你必须填2(为了兼容老版本的Docker)。
  • mediaType string
    保留字段,用于标识当前文件是什么类型的。
  • manifests array of objects
    这个字段是必须的,但是可能为空。他给出了对于特定平台的Manifests列表
    • mediaType string
      Manifests的mediaType,必须使用这个application/vnd.oci.image.manifest.v1+json
    • platform object
      这个字段是可选的,描述了Manifest中描述的镜像是运行在哪一个指定的平台。
      • architecture string
        这个字段是必须的,指定了CPU的架构类型。在运行时规范中会给出平台的相关帮助。
      • os string
        这个字段是必须的,指定了操作系统的类型。在运行时规范中会给出操作系统的相关帮助。
      • os.version string
        这个字段是可选的,指定操作系统的版本要求。
      • os.features array of strings
        这个字段是可选的,指定了一些对于系统的特殊要求。
      • variant string
        这个字段是可选的,指定了CPU的版本要求。
      • features array of strings
        这个字段是可选的,指定了一些对于CPU指令集的特殊要求。(比如sse4、aes)
      • annotations string-string map
        注解

示例

{
  "schemaVersion": 2,
  "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",
        "os.features": [
          "sse4"
        ]
      }
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

Image Layer

Annotations

对于大部分前文中描述的结构,基本都可以添加Annotations。对于Annotations的使用主要有以下一些内容。

使用规则

  • Annotations必须是键值对,其中键必须是string类型的。
  • 键必须唯一,最佳实践是使用命名空间,将其做区分。
  • 值必须存在,但是可以是空字符串。
  • org.opencontainers前缀的键是OCI规范的,不要随便用。
  • org.opencontainers.image前缀的键是OCI镜像规范的,不要随便用。
  • 使用镜像时,不要因为遇到了未知的注解,而直接抛出错误。

预定义的一些注解

  • org.opencontainers.image.created 镜像构建的日期 (string, RFC 3339).
  • org.opencontainers.image.authors 镜像的负责人或组织 (string)
  • org.opencontainers.image.homepage 镜像相关信息地址 (string, URL)
  • org.opencontainers.image.documentation 镜像帮助文档地址 (string, URL)
  • org.opencontainers.image.source 镜像源代码地址 (string, URL)
  • org.opencontainers.image.ref.name 镜像名称(Tag) (string)

其他需要考虑的问题

可扩展性

为了保证可扩展性。使用该规范的实现,不能因为获取到了一些规范之外的属性,而产生错误或者是异常。

规范化

  • OCI 镜像是基于内容寻址的。
  • 内容寻址的一大好处就是可以共享重复的数据。
  • 多个镜像依赖同一个层时,这个层只会存储一份。
  • 使用不同的序列化算法时,语义上一样的层往往会得到不用的Hash值,这样的话这样语义上一样的层就会被存储两份。这两份是一样的。
  • 为了保证高效存储,我们必须使用权威的序列化方式。
  • 这样的话多个不同的该规范实现在推送时表现出来的行为将会是一致的。
  • 许多组件都是JSON格式的,这里也应该使用权威的序列化方式
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐