我们的生产环境很早就从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已经可以成功下载了。
打卡下班!