K8S 1.20 弃用 Docker 评估之 Docker 和 OCI 镜像格式的差别
本文最后更新于:2024年7月24日 晚上
背景
2020 年 12 月初,Kubernetes 在其最新的 Changelog 中宣布,自 Kubernetes 1.20 之后将弃用 Docker 作为容器运行时。
弃用 Docker 带来的,可能是一系列的改变,包括不限于:
- 容器镜像构建工具
- 容器 CLI
- 容器镜像仓库
- 容器运行时
专题文章《K8S 1.20 弃用 Docker 评估》会从多方面分析由此带来的变动和影响,今天先介绍镜像格式的改变。
Docker 镜像仍然可以使用吗?
是的,可以使用。Docker 较新版本生成的镜像实际上并不是特定于 Docker 的镜像,而是 OCI(Open Container Initiative)镜像。无论你使用什么工具构建镜像,任何符合 OCI 标准的镜像在 Kubernetes 看来都是一样的。containerd 和 CRI-O 都能够提取这些镜像并运行它们。所以您可以仍然使用 Docker 来构建容器镜像,并且可以继续在 containerd 和 CRI-O 上使用。
那为什么发现 Docker 镜像和 Containerd 镜像存在不兼容情况?
具体如下:在 K8S > 1.20 版本中,发现 containerd ctr
上传到镜像仓库的镜像与同版本的 docker 镜像间存在以下问题
- 不能被 docker 使用
- docker push 不能覆盖
根本原因还是在于镜像格式的差别。下面做详细解释。
Docker 和 OCI 镜像格式的差别?
目前有以下几种容器镜像格式:
- ❌已弃用:Docker Image V1
- ⚠ 已弃用:Docker Image Manifest V2 Schema 1
- Docker Image Manifest V2 Schema 2
- Open Container Initiative (OCI) 规范
Docker V1 镜像
❌严重警告:
Docker V1 格式早已弃用,请不要再使用!!!
📖 引用 :
- 版本 1.8.3 增加了一个标志 (
--disable-legacy-registry=false
),用于阻止 docker 守护进程对 v1 镜像注册表进行拉、推和登录操作。尽管默认情况下是启用的,但这表示不赞成 v1 协议。 - 自 2017 年 2 月 28 日起,随着 Docker v1.13 的发布,Docker Engine 不再支持 v1 协议 。
- 从 Docker 17.12 开始,对 V1 镜像注册表的支持已经被删除 ,并且
--disable-legacy-registry
标志不再使用,当设置该标志时dockerd
时将无法启动。
Docker V2 镜像简介
Docker V2 镜像清单(Image Manifest)是 Docker 的 V2 版本容器映像规范,它允许多架构镜像并支持内容可寻址映像。
从 2017 年 2 月 28 日开始,Docker V2 镜像注册表规范 取代了 Docker V1 规范。Docker V1 规范已被弃用,并且 Docker V1 映像不能再用于 Container Registry。
为支持内容可寻址镜像并简化镜像图层的跟踪,Docker V2 对 Docker 镜像格式进行了一系列更改。 Docker V2 镜像清单包含镜像图层的所有内容地址(“摘要”),而 Docker V1 映像则不包含这些信息。
Docker Image Manifest V2 Schema 1
下面简单介绍下 V2 Schema 1 镜像清单的格式。V2 Schema 1 它是一个临时清单,提供与 V1 Image 格式的兼容性,V2 Schema 2 才是当前 Docker 镜像格式的最终格式。
📓 备注:
V2 Schema 1 由于需要与 V1 的向后兼容性原因,它比 V2.2(即 Docker Image Manifest V2 Schema 2) 更复杂。
镜像清单描述了一个 Docker 镜像的各种组成部分。镜像清单可以序列化为 JSON 格式与以下媒体类型:
清单类型(Manifest Type) | 媒体类型(Media Type) |
---|---|
manifest | “application/vnd.docker.distribution.manifest.v1+json” |
signed manifest | “application/vnd.docker.distribution.manifest.v1+prettyjws” |
清单格式不详细介绍,下面通过示例说明:
示例清单格式
1 |
|
⚠ 注意 :
随着 Docker Image Manifest V2 Schema 2 的发布,Docker Image Manifest V2 Schema 1 已被弃用。这可能导致尚未更新到 Docker Image Manifest V2 Schema 2 的镜像存在兼容性和漏洞问题。
Docker Image Manifest V2 Schema 2
然后介绍 V2 Schema 2 的格式。
V2 Schema 2 版本有两个主要目标。第一种是允许多架构镜像,通过“胖清单”引用特定于平台版本的镜像的镜像清单。第二种方法是将 Docker 引擎转向可内容寻址的图像,方法是支持一个镜像模型,在该模型中,可以对镜像的配置进行哈希,以生成镜像的 ID。
清单列表(Manifest List)
清单列表 是 Docker V2 Schema 2 和 OCI 镜像的一部分。
利用清单列表,您可以使用单个摘要或标记来表示映像的多种形式。
示例清单列表(Manifest List):
1 |
|
镜像清单
示例镜像清单:
1 |
|
如何将 Docker V1 或 Manifest V2 Schema 1 升级到:Docker Image Manifest V2 Schema 2 ?
- 将老的 Docker V1 或 Manifest V2 Schema 1 使用
docker pull
下来; - 然后用新版本的 Docker
docker push
到镜像仓库即可
这样做将自动将镜像转换为使用最新的镜像清单规范。
或者也可以这样升级:
- 可以通过更新 Dockerfile 中的
FROM
语句来重新 build 镜像。如果您的镜像清单过期了,那么从 Dockerfile 中的from
语句中提取的镜像也有可能过期。
OCI 格式
OCI 格式是基于 Docker Image Manifest Version 2 Schema 2 格式的容器镜像规范。该规范定义了如何创建 OCI Image(通常由构建系统完成),并输出 镜像清单 、 文件系统 (镜像层) 序列化 和镜像配置。下面简要拿镜像清单做一个和 Docker 的对比说明,更多就不详细展开了。
镜像索引(Image Index)
镜像索引(Image Index)相当于 OCI 映像中的清单列表(Manifest List)。
与清单列表一样,镜像索引清单指的是多个镜像清单。镜像索引对多平台镜像很有用。
示例镜像索引:
1 |
|
镜像清单
示例镜像清单:
1 |
|
镜像格式差别总结
首先是 Docker V1 镜像,这是非常老旧且已经弃用的镜像格式,是 Docker 刚出来的时候,没有考虑多架构多平台(如:x86 和 arm 镜像),所以通常 Docker V1 镜像只有 x86 平台的镜像。⚠ 注意:请不要再使用,如果使用,请尽快升级到 Docker Image Manifest V2 Schema 2 或 OCI 格式
然后是 Docker Image Manifest V2 Schema 1,这个是过过渡形态的格式,兼容 Docker V1 和 Docker Image Manifest V2 使得它更为复杂,它的作用也仅仅是为了过渡而非长期使用。⚠ 注意:请不要再使用,如果使用,请尽快升级到 Docker Image Manifest V2 Schema 2 或 OCI 格式
最后是 Docker Image Manifest V2 Schema 2 和 OCI 格式,OCI 主要参考的就是 Docker Image Manifest V2 Schema 2 格式,二者是兼容的,这也就回答了上文所说的:「Docker 镜像仍然可以使用吗?-- 是的,可以使用。」
但是二者在媒体类型(Media Type)、压缩方式等细节上存在不同,部分举例如:
- 清单(manifest)上:
- Docker Image Manifest V2 Schema 2 的 Media Type 是:
"application/vnd.docker.distribution.manifest.list.v2+json"
, - 而 OCI 是:
"application/vnd.oci.image.manifest.v1+json"
- Docker Image Manifest V2 Schema 2 的 Media Type 是:
- 每一层镜像层上:
- Docker Image Manifest V2 Schema 2 的 Media Type 是:
"application/vnd.docker.image.rootfs.diff.tar.gzip"
- OCI 的 Media Type 是:
application/vnd.oci.image.layer.v1.tar+gzip
或application/vnd.oci.image.layer.v1.tar
等类型。
- Docker Image Manifest V2 Schema 2 的 Media Type 是:
也正是因为这些差异,最终导致即使是完全相同的镜像,二者的 digest、镜像大小不尽相同。
所以这也正好解释了「那为什么发现 Docker 镜像和 Containerd 镜像存在不兼容情况?」
- 「不能被 docker 使用?」 - 可能是 docker 版本过低导致的;
- 「docker push 不能覆盖?」- 因为即使是完全相同的镜像,二者的 digest、镜像大小不尽相同。所以无法覆盖。
我的建议
针对镜像,因为 OCI 主要参考了 Docker Image Manifest V2 Schema 2,而 Docker Image Manifest V2 Schema 2 是 OCI 的一种实现。所以 Docker 较新版本生成的镜像,containerd 和 CRI-O 都能够提取这些镜像并运行它们。
两种办法都可以:
- 镜像构建方式保持不变,仍然是采用 Docker 构建镜像(注意 Docker 版本要较新,确保构建的镜像是:Docker Image Manifest V2 Schema 2 格式)
- 变更镜像构建工具,不再使用 Docker,而是使用可以构建 OCI 格式的镜像构建工具。
具体您可以视实际情况进行选择。