背景
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 镜像
{% note danger %}
❌重大正告:
Docker V1 格局早已弃用,请不要再应用!!!
{% endnote %}
{% note info %}
援用:
- 版本 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
时将无奈启动。
{% endnote %}
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 镜像格局的最终格局。
{% note default %}
备注:
V2 Schema 1 因为须要与 V1 的向后兼容性起因,它比 V2.2(即 Docker Image Manifest V2 Schema 2)更简单。
{% endnote %}
镜像清单形容了一个 Docker 镜像的各种组成部分。镜像清单能够序列化为 JSON 格局与以下媒体类型:
清单类型(Manifest Type) | 媒体类型(Media Type) |
---|---|
manifest | “application/vnd.docker.distribution.manifest.v1+json” |
signed manifest | “application/vnd.docker.distribution.manifest.v1+prettyjws” |
清单格局不具体介绍,上面通过示例阐明:
示例清单格局
{
"name": "hello-world",
"tag": "latest",
"architecture": "amd64",
"fsLayers": [
{"blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"},
{"blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"},
{"blobSum": "sha256:cc8567d70002e957612902a8e985ea129d831ebe04057d88fb644857caa45d11"},
{"blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"}
],
"history": [
{"v1Compatibility": "{\"id\":\"e45a5af57b00862e5ef5782a9925979a02ba2b12dff832fd0991335f4a11e5c5\",\"parent\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"created\":\"2014-12-31T22:57:59.178729048Z\",\"container\":\"27b45f8fb11795b52e9605b686159729b0d9ca92f76d40fb4f05a62e19c46b4f\",\"container_config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) CMD [/hello]\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"docker_version\":\"1.4.1\",\"config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/hello\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":0}\n"
},
{"v1Compatibility": "{\"id\":\"e45a5af57b00862e5ef5782a9925979a02ba2b12dff832fd0991335f4a11e5c5\",\"parent\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"created\":\"2014-12-31T22:57:59.178729048Z\",\"container\":\"27b45f8fb11795b52e9605b686159729b0d9ca92f76d40fb4f05a62e19c46b4f\",\"container_config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) CMD [/hello]\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"docker_version\":\"1.4.1\",\"config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/hello\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":0}\n"
},
],
"schemaVersion": 1,
"signatures": [
{
"header": {
"jwk": {
"crv": "P-256",
"kid": "OD6I:6DRK:JXEJ:KBM4:255X:NSAA:MUSF:E4VM:ZI6W:CUN2:L4Z6:LSF4",
"kty": "EC",
"x": "3gAwX48IQ5oaYQAYSxor6rYYc_6yjuLCjtQ9LUakg4A",
"y": "t72ge6kIA1XOjqjVoEOiPPAURltJFBMGDSQvEGVB010"
},
"alg": "ES256"
},
"signature": "XREm0L8WNn27Ga_iE_vRnTxVMhhYY0Zst_FfkKopg6gWSoTOZTuW4rK0fg_IqnKkEKlbD83tD46LKEGi5aIVFg",
"protected": "eyJmb3JtYXRMZW5ndGgiOjY2MjgsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxNS0wNC0wOFQxODo1Mjo1OVoifQ"
}
]
}
{% note warning %}
留神:
随着 Docker Image Manifest V2 Schema 2 的公布,Docker Image Manifest V2 Schema 1 已被弃用。这可能导致尚未更新到 Docker Image Manifest V2 Schema 2 的镜像存在兼容性和破绽问题。
{% endnote %}
Docker Image Manifest V2 Schema 2
而后介绍 V2 Schema 2 的格局。
V2 Schema 2 版本有两个次要指标。第一种是容许多架构镜像,通过“胖清单”援用特定于平台版本的镜像的镜像清单。第二种办法是将 Docker 引擎转向可内容寻址的图像,办法是反对一个镜像模型,在该模型中,能够对镜像的配置进行哈希,以生成镜像的 ID。
清单列表(Manifest List)
清单列表是 Docker V2 Schema 2 和 OCI 镜像的一部分。
利用清单列表,您能够应用单个摘要或标记来示意映像的多种形式。
示例清单列表(Manifest List):
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 7143,
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
"platform": {
"architecture": "ppc64le",
"os": "linux",
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 7682,
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"platform": {
"architecture": "amd64",
"os": "linux",
"features": ["sse4"]
}
}
]
}
镜像清单
示例镜像清单:
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 7023,
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 32654,
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 16724,
"digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 73109,
"digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
}
]
}
如何将 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)。
与清单列表一样,镜像索引清单指的是多个镜像清单。镜像索引对多平台镜像很有用。
示例镜像索引:
{
"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"
}
}
],
"annotations": {
"com.example.key1": "value1",
"com.example.key2": "value2"
}
}
镜像清单
示例镜像清单:
{
"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:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0"
},
{
"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"
}
}
镜像格局差异总结
首先是 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 格局的镜像构建工具。
具体您能够视理论状况进行抉择。
三人行, 必有我师; 常识共享, 天下为公. 本文由东风微鸣技术博客 EWhisper.cn 编写.