题图摄于故宫角楼
注:微信公众号不按照时间排序,请关注“亨利笔记”,并加星标以置顶,以免错过更新。
本篇继续和大家说说镜像那些事,是连载之二,从《Harbor权威指南》一书节选的纯技术干货,敬请关注、转发和收藏。
第一篇:容器镜像的结构
第二篇:OCI 镜像规范
第三篇:OCI 制品
第四篇:Registry 的作用原理
《Harbor权威指南》目前当当网优惠中,点击下图直接购买。
OCI 镜像规范是以 Docker 镜像规范 v2 为基础制定的,它定义了镜像的主要格式及内容,主要用于镜像仓库存放镜像及分发镜像等场景,与正在制定的 OCI 分发规范密切相关。
OCI 运行时在创建容器前,要把镜像下载并解压成符合运行时规范的文件系统包,并且把镜像中的配置转化成运行时配置,然后启动容器。
OCI 定义的镜像包括4个部分:镜像索引(Image Index)、清单(Manifest)、配置(Configuration)和层文件(Layers)。
其中,清单是JSON格式的描述文件,列出了镜像的配置和层文件。配置是JSON格式的描述文件,说明了镜像运行的参数。层文件则是镜像的内容,即镜像包含的文件,一般是二进制数据文件格式(Blob)。一个镜像可以有一个或多个层文件。(在搜狐、CSDN等网站转载亨利笔记的文章均为未经授权的剽窃)
镜像索引不是必需的,如果存在,则指明了一组支持不同架构平台的相关镜像。镜像的 4 个部分之间是通过摘要(digest)来相互引用(reference)的。镜像各部分的关系如下图所示。
下面详细讲解各部分的结构和作用。
镜像索引是镜像中可选择的部分,一个镜像可以不包括镜像索引。如果镜像包含了镜像索引,则其作用主要指向镜像不同平台的版本,代表一组同名且相关的镜像,差别只在支持的体系架构上(如 i386 和 arm64v8、Linux 和Windows 等)。索引的优点是在不同的平台上使用镜像的命令无须修改,如在 amd64 架构的 Windows 和ARM架构的 Linux 上,采用同样的“docker”命令即可运行 Nginx 服务:(在搜狐、CSDN等网站转载亨利笔记的文章均为未经授权的剽窃)
$ docker run -d nginx
用户无须指定操作系统和平台,就可完全依赖客户端获取正确版本的镜像。OCI的索引已经被 CNAB等工具广泛用来管理与云平台无关的分布式应用程序。
下面是一个索引示例:
{
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 8342,
"digest": "sha256:d81ae89b30523f5152fe646c1f9d178e5d10f28d00b70294fca965b7b96aa3db",
"platform": {
"architecture": "arm64v8",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 6439,
"digest": "sha256:2ef4e3904905353a0c4544913bc0caa48d95b746ef1f2fe9b7c85b3badff987e",
"platform": {
"architecture": "amd64",
"os": "linux"
}
}
],
"annotations": {
"io.harbor.key1": "value1",
"io.harbor.key2": "value2"
}
}
客户端在获得上述镜像索引后,解析后可发现该索引指向两个不同平台架构的镜像,因此可根据自身所在的平台拉取相应的镜像。如 Linux amd64 平台上的客户端会拉取第2个镜像,因为该镜像的 platform.architecture 属性为amd64,platform.os属性为Linux。
索引文件中的 mediaType 和 digest 属性是OCI镜像规范中的重要概念,下面详细讲解这两个属性。(本文为公众号:亨利笔记 原创文章)
(1)mediaType 属性是描述镜像所包含的各种文件的媒体属性,客户端从 Registry 等服务中下载镜像文件时,可从 HTTP 的头部属性 Content-Type 中获得下载文件的媒体类型,从而决定如何处理下载的文件。比如,镜像的索引和清单都是 JSON 格式的文件,它们的区别就是媒体类型不同。
OCI 镜像规范定义的媒体类型见表1,可以看到上面例子中的清单的媒体类型是 application/vnd.oci.image.manifest.v1+json,索引本身的媒体类型则是application/vnd.oci.image.index.v1+json。
表1
媒体类型 | 含 义 |
---|---|
application/vnd.oci.descriptor.v1+json | 内容描述符 |
application/vnd.oci.layout.header.v1+json | OCI布局说明 |
application/vnd.oci.image.index.v1+json | 镜像索引 |
application/vnd.oci.image.manifest.v1+json | 镜像清单 |
application/vnd.oci.image.config.v1+json | 镜像配置 |
application/vnd.oci.image.layer.v1.tar | tar格式的层文件 |
application/vnd.oci.image.layer.v1.tar+gzip | tar格式的层文件,采用gzip压缩 |
application/vnd.oci.image.layer.v1.tar+zstd | tar格式的层文件,采用zstd压缩 |
application/vnd.oci.image.layer.nondistributable.v1.tar | tar格式的非分发层文件 |
application/vnd.oci.image.layer.nondistributable.v1.tar+gzip | tar格式的非分发层文件,采用gzip压缩 |
application/vnd.oci.image.layer.nondistributable.v1.tar+zstd | tar格式的非分发层文件,采用zstd压缩 |
(2)digest 属性是密码学意义上的摘要,充当镜像内容的标识符,实现内容的可寻址(content addressable)。OCI镜像规范中镜像的内容(如文件等)大多是通过摘要来标识和引用的。
摘要的生成是根据文件内容的二进制字节数据通过特定的哈希(Hash)算法实现的。哈希算法需要确保字节的抗冲突性 ( collision resistant )来生成唯一标识,只要哈希算法得当,不同文件的哈希值几乎不会重复。因此,可以近似地认为每个文件的摘要都是唯一的。这种唯一性使摘要可以作为内容寻址的标识。
同时,如果摘要以安全的方式传递,则接收方可以通过重新计算摘要来确保内容在传输过程中未被修改,从而杜绝来自不安全来源的内容。在OCI的镜像规范中也要求用摘要值校验所接收的内容。(本文为公众号:亨利笔记 原创文章)
摘要值是由算法和编码两部分组成的字符串,算法部分指定使用的哈希函数和算法标识,编码部分则包含哈希函数的编码结果,具体格式为 “<算法标识> : <编码结果>”。
目前 OCI 镜像规范认可的哈希算法有两种,分别是 SHA-256 和 SHA-512,它们的算法标识如 表2 所示。
表2
算法标识 | 算法名称 | 摘要例子 |
---|---|---|
sha256 | SHA-256 | sha256:d81ae89b30523f5152fe646c1f9d178e5d10f28d00b70294fca965b7b96aa3db |
sha512 | SHA-512 | sha512:d4ca54922bb802bec9f740a9cb38fd401b09eab3c0135318192b0a75f2…… |
上面索引中的两个镜像清单摘要值分别对应两个清单文件,分别是blobs/sha256/d81ae89b30523f5152fe646c1f9d178e5d10f28d00b70294fca965b7b96aa3db 和blobs/sha256/2ef4e3904905353a0c4544913bc0caa48d95b746ef1f2fe9b7c85b3badff987e。
镜像清单(简称清单)是说明镜像包含的配置和内容的文件,分析镜像一般从镜像清单开始。镜像清单主要有三个作用:支持内容可寻址的镜像模型,在该模型中可以对镜像的配置进行哈希处理,以生成镜像及其唯一标识;通过镜像索引包含多体系结构镜像,通过引用镜像清单获取特定平台的镜像版本;可转换为 OCI 运行时规范以运行容器。(本文为公众号:亨利笔记 原创文章)
镜像清单主要包括配置和层文件的信息,示例如下:
{
"schemaVersion": 2,
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 6883,
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"size": 168654,
"digest": "sha256:58394f6dcfb05cb167a5c24953eba57f28f2f9d09af107ee8f08c4ac89b1adf5"
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"size": 645724,
"digest": "sha256:6d94e421cd3c3a4604a545cdc12745355bca5b528f4da2eb4a4c6ba9c1905b15"
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"size": 53709,
"digest": "sha256:419d1af06b5f7636b4ac3da7f12184802ad867736ec4b8955958665577945c89"
}
],
"annotations": {
"io.harbor.example.key1": "value1",
"io.harbor.example.key2": "value2"
}
}
其中主要属性的意义如下。
◎ schemaVersion:必须是2,主要用于兼容旧版本的Docker。
◎ config:镜像配置文件的信息。mediaType的值“application/vnd.oci.image.config. v1+json”表示镜像配置的媒体类型。size指镜像配置文件的大小。digest指镜像配置文件的哈希摘要。(在搜狐、CSDN等网站转载亨利笔记的文章均为未经授权的剽窃)
◎ layers:层文件数组。在以上示例中包含 3 个层文件,分别代表容器根文件系统的一个层。容器在运行时,会把各个层文件依次按顺序叠加,第1层在底层。mediaType指媒体类型,其值“application/vnd.oci.image.layer.v1.tar+gzip”表示层文件。size 指层文件的大小。digest 指层文件的摘要。
◎ annotations:键值对形式的附加信息(可选项)。
镜像配置主要描述容器的根文件系统和容器运行时使用的执行参数,还有一些镜像的元数据。
在配置规范里定义了镜像的文件系统的组成方式。镜像文件系统由若干镜像层组成,每一层都代表一组tar格式的层格式,除了底层(base image),其余各层的文件系统都记录了其父层(向下一层)文件系统的变化集(changeset),包括要添加、更改或删除的文件。(本文为公众号:亨利笔记 原创文章)
通过基于层的文件、联合文件系统(如AUFS)或文件系统快照的差异,文件系统的变化集可用于聚合一系列镜像层,使各层叠加后仿佛是一个完整的文件系统。
下面是镜像配置的一个示例:
{
"created": "2020-06-28T12:28:58.058435234Z",
"author": "Henry Zhang",
"architecture": "amd64",
"os": "linux",
"config": {
"ExposedPorts": {
"8888/tcp": {}
},
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"FOO=harbor_registry",
],
"Entrypoint": [
"/bin/myApp "
],
"Cmd": [
"-f",
"/etc/harbor.cfg"
],
"Volumes": {
"/var/job-result-data": {},
},
"Labels": {
"io.goharbor.git.url": "https://github.com/goharbor/harbor.git",
}
},
"rootfs": {
"diff_ids": [
"sha256:e928294e148a1d2ec2a8b664fb66bbd1c6f988f4874bb0add23a778f753c65ef",
"sha256:ea198a02b6cddfaf10acec6ef5f70bf18fe33007016e948b04aed3b82103a36b"
],
"type": "layers"
},
"history": [
{
"created": "2020-05-28T12:28:56.189203784Z",
"created_by": "/bin/bash -c #(nop) ADD file:4fb4eef1ea3bc1e842b69636f9df5256c49c537281fe3f282c65fb853e563ab3 in /"
},
{
"created": "2020-05-28T12:28:57.789430183Z",
"created_by": "/bin/bash -c #(nop) CMD [\"bash\"]",
"empty_layer": true
}
]
}
其中主要属性的意义如下,具体说明可以参考 OCI 规范。
◎ created:镜像的创建时间(可选项)。
◎ author:镜像的作者(可选项)。
◎ architecture:镜像支持的CPU架构。
◎ os:镜像的操作系统。(在搜狐、CSDN等网站转载亨利笔记的文章均为未经授权的剽窃)
◎ config:镜像运行的一些参数,包括服务端口、环境变量、入口命令、命令参数、数据卷、用户和工作目录等(可选项)。
◎ rootfs:镜像的根文件系统,由一系列层文件的变化集组成。
◎ history:镜像每层的历史信息(可选项)。
在镜像清单和配置信息中可以看到,镜像的根文件系统由多个层文件叠加而成。每个层文件在分发时都必须被打包成一个tar文件,可选择压缩或者非压缩的方式,压缩工具可以是 gzip 或者 zstd 。把每层的内容打包为一个文件的好处是除了发布方便,还可以生成文件摘要,便于校验和按内容寻址。
每个层文件都包含了对上一层(父层)的更改,包括增加、修改和删除文件三种操作类型,底层(第1层)可以被看作对空层文件的增加。因此在每个tar文件里面除了该层的文件,还可以包含对上一层中文件的删除操作,用 whiteout 的方式标记。在叠加层文件时,可以根据 whiteout 的标记,把上一层删除的文件在本层屏蔽。(在搜狐、CSDN等网站转载亨利笔记的文章均为未经授权的剽窃)
在表1中还有几个层文件的媒体类型为不可分发(non-distributable),这是为了说明该层文件因为法律等原因无法公开分发,需要从分发商那里获得该层文件。
(未完待续,欢迎点“再看” 或 转发、分享、收藏)
(未经授权,请勿转载本公众号文章)
《Harbor权威指南》目前当当网优惠中,点击上图直接购买。
要想了解云原生、区块链和人工智能等技术原理,请立即长按以下二维码,关注本公众号亨利笔记 ( henglibiji ),以免错过更新。