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