1 概述

1.1 环境

版本信息如下:
a、操作系统:centos 7.6
b、registry版本:v2.7.1



1.2 现象

registry的blob目录下的文件被GC之后,再重新docker push旧镜像提示Layer already exists,即从服务端响应的数据来看推送镜像是成功的,但在blob目录中写入相应的文件,即实际上推送镜像是失败的。执行docker pull请求,registry告知manifest unknown,即拉取镜像是失败的。



1.3 原因

1)docker push请求发送给registry,registry从缓存中查询到该blob数据是存在的,不会去文件系统中查看文件是否真的存在,因此registry响应客户端说Layer already exists。而实际上在文件系统层面,blob数据已被GC行为删除了。

2)docker pull请求到达registry时,registry会在文件系统层面查询manifest文件在blob目录下是否真的存在,因为确实找不到manifest文件(因为registry的GC行为会去blob目录下将该镜像的manifest文件、config文件、layer文件都删除了),所以响应manifest unknown,即拉取镜像是失败的。

1.4 解决方案

重启registry即可,缓存会被清空,registry则会从文件系统层面查询数据文件是否存在,不存在则提示客户端进行上传blob文件。

2 复现

2.1 步骤

1)推送镜像
2)使用rest delete api删除镜像的manifest文件
3)registry执行gc
4)重新推送旧镜像
5)拉取旧镜像

2.2 过程数据

2.2.1 推送镜像

docker push 192.168.0.70:5000/liujun/busybox:1.27

docker pull 文件目录 docker pull unknown blob_docker

2.2.2 使用rest delete api删除镜像

sha256=$( curl -H "Accept:application/vnd.docker.distribution.manifest.v2+json" http://192.168.0.70:5000/v2/liujun/busybox/manifests/1.27 2> /dev/null | sha256sum | awk '{print $1}' )

echo $sha256

curl -v -XDELETE http://192.168.0.70:5000/v2/liujun/busybox/manifests/sha256:$sha256

docker pull 文件目录 docker pull unknown blob_数据_02

2.2.3 执行gc、重新推送旧镜像、拉取旧镜像

export GO111MODULE=off

go build -o /tmp/registry ./cmd/registry

/tmp/registry garbage-collect -m --delete-untagged=true ./cmd/registry/config-example.yml

find /var/lib/registry/ -type f

docker push 192.168.0.70:5000/liujun/busybox:1.27

docker pull 192.168.0.70:5000/liujun/busybox:1.27

docker pull 文件目录 docker pull unknown blob_容器_03


3 小结

镜像推送,registry会去缓存查询数据,由于GC行为(单独的cmd命令)只是在文件系统层面删除了数据,没有修改缓存中的数据(数据是守护进程中的数据),会导致缓存和文件系统在数据上的不一致,并且registry会告知客户端Layer already exists,从而导致GC行为发生之后的推送旧镜像的操作实际上是失败的。
镜像拉取,registry会在文件系统层面查看数据(manifest文件、config文件、layer文件)是否真的存在。