ca3358(containerd采坑记containerd 不支持老版本镜像下载)

我们的生产环境很早就从Docker切换到containerd了,目前containerd的版本是1.4.3,最近发生了一个故障,无法下载redis镜像:

# crictl pull docker.io/library/redis:2.8.23FATA[0021] pulling image: rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/redis:2.8.23":failed to unpack image on snapshotter overlayfs: failed to extract layer sha256:4dcab49015d47e8f300ec33400a02cebc7b54cadd09c37e49eccbc655279da90: failed to get reader from content store: content digest sha256:51f5c6a04d83efd2d45c5fd59537218924bc46705e3de6ffc8bc07b51481610b: not found

提示的错误是content的分层没有下载

查看containerd具体报错

level=warning msg="reference for unknown type: application/octet-stream" digest="sha256:481995377a044d40ca3358e4203fe95eca1d58b98a1d4c2d9cec51c0c4569613" mediatype=application/octet-stream size=5946

很明显是layer的类型 application/octet-stream 不是标准的镜像格式,翻看OCI规范可以发现,config的格式标准格式是“application/vnd.oci.image.config.v1+json”。

查看containerd源码支持的config类型

func IsKnownConfig(mt string) bool { switch mt { case MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig, MediaTypeContainerd1Checkpoint, MediaTypeContainerd1CheckpointConfig: return true } return false}

除了支持标准OCI的config还可以支持“application/vnd.docker.container.image.v1+json”。但老的镜像config的类型是 application/octet-stream 是不支持的,所以会提升上面的错误。

解决这个问题有两种方式,一种方式就是重新制作镜像,使用新版docker构建就没有问题了。另外一种方式就是修改containerd源代码,我们尝试通过源码的方式fix这个问题。

我们把这个类型加到上面的方法 IsKnownConfig 里面就行了,这并非掩耳盗铃,因为在1.4.3的代码中发现了,containerd 会去执行convert操作,将老的config的类型修改成标准支持的镜像格式:application/vnd.docker.container.image.v1+json。具体代码参见remotes/docker/converter.go。

但事情没有这么简单,发现镜像还是没有下载成功。因为在unpack的地方也校验了这个参数 代码在unpacker.go里面。

case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig

只有类型符合这两种格式的config才能执行unpack。下载layer并执行apply操作。还需要在这里加上 application/octet-stream 才行。重新编译后,老的镜像redis 2.8.23已经可以成功下载了。

打卡下班!