registry存储目录
Registry 存储目录只有两种文件名的文件:
- data 文件:包括层文件(layer)、config 文件和 manifest 文件
- link 文件:存放在 repository 目录下,其内容是指向 data 文件的 digest 值
目录说明
_layers/sha256
repositories 目录下每个 repository 的 _layers/sha256 目录保存了此 repository 的所有层文件(layer)和 config 文件的 digest,该目录下 link 文件指向对应 blobs 目录下的 data 文件,当 pull 一个镜像的 layer 时,是通过 link 文件找到 layer 在 registry 种实际存储的存储位置
_manifests/revisions/sha256
repositories 目录下每个 repository 的 _manifests/revisions/sha256 目录保存了此 repository 的所有 manifest 文件的 link 文件
_manifests/tags/[tag]/current
repositories 目录下每个 repository 的 _manifests/tags/[tag]/current 保存了 tag 对应的 manifest 文件的 link 文件
_manifests/tags/[tag]/index/sha256
Registry GC 原理
删除镜像文件变化分析
推送两个镜像 library/nginx:latest、library/nginx:v1
# tree
.
└── docker
└── registry
└── v2
├── blobs
│ └── sha256
│ ├── 18
│ │ └── 186b1aaa4aa6c480e92fbd982ee7c08037ef85114fbed73dbb62503f24c1dd7d
│ │ └── data
│ ├── 58
│ │ └── 589b7251471a3d5fe4daccdddfefa02bdc32ffcba0a6d6a2768bf2c401faf115
│ │ └── data
│ ├── 5c
│ │ └── 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa
│ │ └── data
│ ├── 60
│ │ └── 605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85
│ │ └── data
│ ├── 62
│ │ └── 62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
│ │ └── data
│ ├── a0
│ │ └── a0bcbecc962ed2552e817f45127ffb3d14be31642ef3548997f58ae054deb5b2
│ │ └── data
│ ├── a2
│ │ └── a2abf6c4d29d43a4bf9fbb769f524d0fb36a2edab49819c1bf3e76f409f953ea
│ │ └── data
│ ├── a9
│ │ └── a9edb18cadd1336142d6567ebee31be2a03c0905eeefe26cb150de7b0fbc520b
│ │ └── data
│ ├── b4
│ │ └── b4df32aa5a72e2a4316aad3414508ccd907d87b4ad177abd7cbd62fa4dab2a2f
│ │ └── data
│ ├── be
│ │ └── beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a
│ │ └── data
│ └── ee
│ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ └── data
└── repositories
└── library
└── nginx
├── _layers
│ └── sha256
│ ├── 186b1aaa4aa6c480e92fbd982ee7c08037ef85114fbed73dbb62503f24c1dd7d
│ │ └── link
│ ├── 589b7251471a3d5fe4daccdddfefa02bdc32ffcba0a6d6a2768bf2c401faf115
│ │ └── link
│ ├── 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa
│ │ └── link
│ ├── 605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85
│ │ └── link
│ ├── a0bcbecc962ed2552e817f45127ffb3d14be31642ef3548997f58ae054deb5b2
│ │ └── link
│ ├── a2abf6c4d29d43a4bf9fbb769f524d0fb36a2edab49819c1bf3e76f409f953ea
│ │ └── link
│ ├── a9edb18cadd1336142d6567ebee31be2a03c0905eeefe26cb150de7b0fbc520b
│ │ └── link
│ ├── b4df32aa5a72e2a4316aad3414508ccd907d87b4ad177abd7cbd62fa4dab2a2f
│ │ └── link
│ └── beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a
│ └── link
├── _manifests
│ ├── revisions
│ │ └── sha256
│ │ ├── 62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
│ │ │ └── link
│ │ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ │ └── link
│ └── tags
│ ├── latest
│ │ ├── current
│ │ │ └── link
│ │ └── index
│ │ └── sha256
│ │ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ │ └── link
│ └── v1
│ ├── current
│ │ └── link
│ └── index
│ └── sha256
│ └── 62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
│ └── link
└── _uploads
删除 library/nginx:v1
curl -X "DELETE" -k 'http://localhost:5000/v2/library/nginx/manifests/sha256:62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee'
删除后,文件变化,从文件变化可以看出来,delete 一个镜像实质上是删除 repositories 元数据文件夹下的 tag 名文件夹和该 tag 的 revisions 下的 link 文件
# tree
.
└── docker
└── registry
└── v2
├── blobs
│ └── sha256
│ ├── 18
│ │ └── 186b1aaa4aa6c480e92fbd982ee7c08037ef85114fbed73dbb62503f24c1dd7d
│ │ └── data
│ ├── 58
│ │ └── 589b7251471a3d5fe4daccdddfefa02bdc32ffcba0a6d6a2768bf2c401faf115
│ │ └── data
│ ├── 5c
│ │ └── 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa
│ │ └── data
│ ├── 60
│ │ └── 605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85
│ │ └── data
│ ├── 62
│ │ └── 62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
│ │ └── data
│ ├── a0
│ │ └── a0bcbecc962ed2552e817f45127ffb3d14be31642ef3548997f58ae054deb5b2
│ │ └── data
│ ├── a2
│ │ └── a2abf6c4d29d43a4bf9fbb769f524d0fb36a2edab49819c1bf3e76f409f953ea
│ │ └── data
│ ├── a9
│ │ └── a9edb18cadd1336142d6567ebee31be2a03c0905eeefe26cb150de7b0fbc520b
│ │ └── data
│ ├── b4
│ │ └── b4df32aa5a72e2a4316aad3414508ccd907d87b4ad177abd7cbd62fa4dab2a2f
│ │ └── data
│ ├── be
│ │ └── beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a
│ │ └── data
│ └── ee
│ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ └── data
└── repositories
└── library
└── nginx
├── _layers
│ └── sha256
│ ├── 186b1aaa4aa6c480e92fbd982ee7c08037ef85114fbed73dbb62503f24c1dd7d
│ │ └── link
│ ├── 589b7251471a3d5fe4daccdddfefa02bdc32ffcba0a6d6a2768bf2c401faf115
│ │ └── link
│ ├── 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa
│ │ └── link
│ ├── 605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85
│ │ └── link
│ ├── a0bcbecc962ed2552e817f45127ffb3d14be31642ef3548997f58ae054deb5b2
│ │ └── link
│ ├── a2abf6c4d29d43a4bf9fbb769f524d0fb36a2edab49819c1bf3e76f409f953ea
│ │ └── link
│ ├── a9edb18cadd1336142d6567ebee31be2a03c0905eeefe26cb150de7b0fbc520b
│ │ └── link
│ ├── b4df32aa5a72e2a4316aad3414508ccd907d87b4ad177abd7cbd62fa4dab2a2f
│ │ └── link
│ └── beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a
│ └── link
├── _manifests
│ ├── revisions
│ │ └── sha256
│ │ ├── 62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
│ │ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ │ └── link
│ └── tags
│ └── latest
│ ├── current
│ │ └── link
│ └── index
│ └── sha256
│ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ └── link
└── _uploads
Registry GC 后文件变化
执行垃圾回收
# docker exec -it 7987b19396bb registry garbage-collect /etc/docker/registry/config.yml
library/nignx
library/nignx: marking manifest sha256:ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
library/nignx: marking blob sha256:605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85
library/nignx: marking blob sha256:a2abf6c4d29d43a4bf9fbb769f524d0fb36a2edab49819c1bf3e76f409f953ea
library/nignx: marking blob sha256:a9edb18cadd1336142d6567ebee31be2a03c0905eeefe26cb150de7b0fbc520b
library/nignx: marking blob sha256:589b7251471a3d5fe4daccdddfefa02bdc32ffcba0a6d6a2768bf2c401faf115
library/nignx: marking blob sha256:186b1aaa4aa6c480e92fbd982ee7c08037ef85114fbed73dbb62503f24c1dd7d
library/nignx: marking blob sha256:b4df32aa5a72e2a4316aad3414508ccd907d87b4ad177abd7cbd62fa4dab2a2f
library/nignx: marking blob sha256:a0bcbecc962ed2552e817f45127ffb3d14be31642ef3548997f58ae054deb5b2
8 blobs marked, 3 blobs and 0 manifests eligible for deletion
blob eligible for deletion: sha256:5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/5c/5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa go.version=go1.11.2 instance.id=54fcc304-f5ca-40b1-8be4-e43a6c1f3fa6 service=registry
blob eligible for deletion: sha256:62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/62/62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee go.version=go1.11.2 instance.id=54fcc304-f5ca-40b1-8be4-e43a6c1f3fa6 service=registry
blob eligible for deletion: sha256:beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/be/beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a go.version=go1.11.2 instance.id=54fcc304-f5ca-40b1-8be4-e43a6c1f3fa6 service=registry
垃圾回收后,文件变化
# tree
.
└── docker
└── registry
└── v2
├── blobs
│ └── sha256
│ ├── 18
│ │ └── 186b1aaa4aa6c480e92fbd982ee7c08037ef85114fbed73dbb62503f24c1dd7d
│ │ └── data
│ ├── 58
│ │ └── 589b7251471a3d5fe4daccdddfefa02bdc32ffcba0a6d6a2768bf2c401faf115
│ │ └── data
│ ├── 5c
│ ├── 60
│ │ └── 605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85
│ │ └── data
│ ├── 62
│ ├── a0
│ │ └── a0bcbecc962ed2552e817f45127ffb3d14be31642ef3548997f58ae054deb5b2
│ │ └── data
│ ├── a2
│ │ └── a2abf6c4d29d43a4bf9fbb769f524d0fb36a2edab49819c1bf3e76f409f953ea
│ │ └── data
│ ├── a9
│ │ └── a9edb18cadd1336142d6567ebee31be2a03c0905eeefe26cb150de7b0fbc520b
│ │ └── data
│ ├── b4
│ │ └── b4df32aa5a72e2a4316aad3414508ccd907d87b4ad177abd7cbd62fa4dab2a2f
│ │ └── data
│ ├── be
│ └── ee
│ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ └── data
└── repositories
└── library
└── nginx
├── _layers
│ └── sha256
│ ├── 186b1aaa4aa6c480e92fbd982ee7c08037ef85114fbed73dbb62503f24c1dd7d
│ │ └── link
│ ├── 589b7251471a3d5fe4daccdddfefa02bdc32ffcba0a6d6a2768bf2c401faf115
│ │ └── link
│ ├── 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa
│ │ └── link
│ ├── 605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85
│ │ └── link
│ ├── a0bcbecc962ed2552e817f45127ffb3d14be31642ef3548997f58ae054deb5b2
│ │ └── link
│ ├── a2abf6c4d29d43a4bf9fbb769f524d0fb36a2edab49819c1bf3e76f409f953ea
│ │ └── link
│ ├── a9edb18cadd1336142d6567ebee31be2a03c0905eeefe26cb150de7b0fbc520b
│ │ └── link
│ ├── b4df32aa5a72e2a4316aad3414508ccd907d87b4ad177abd7cbd62fa4dab2a2f
│ │ └── link
│ └── beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a
│ └── link
├── _manifests
│ ├── revisions
│ │ └── sha256
│ │ ├── 62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
│ │ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ │ └── link
│ └── tags
│ └── latest
│ ├── current
│ │ └── link
│ └── index
│ └── sha256
│ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ └── link
└── _uploads
GC 原理分析
GC 过程简析
假如有 manifest A 和manifest B,分别引用了layer a、b 和 a、c
A -----> a <----- B
\--> b |
c <--/
通过 registry API 删除 manifest B 后,layer c 并没有删除,只是删除了对它的引用
A -----> a B
\--> b
c
GC 后,没有任何引用 lay c 就会被删掉
A -----> a
\--> b
GC 阶段
GC 主要分两个阶段:mark 和 sweep
mark 阶段:扫描所有的 manifest 文件,标记扫描到的 manifest 文件所包含的 layer。按仓库进行,获取此仓库下所有 manifest 文件的索引(即 _manifests/revisions/sha256 文件夹下所有文件),根据索引获取所有的 manifest 文件并标记其包含 layer,如果添加了 -m,会进一步此 manifest 文件索引在这个仓库下有没有 tag 引用(不会扫描所有仓库),没有引用则会标记删除此 manifest文件及其所以引用。
sweep 阶段:将没有标记的 blob(layer 和 config 文件)就会被清除掉
总结
总结以上,用以下三张图片就能直观地理解这些过程
delete 镜像之前的 registry 存储目录结构
[图片上传失败...(image-8cb64b-1666173662460)]
delete 镜像之后的 registry 存储目录结构
[图片上传失败...(image-b8c961-1666173662460)]
GC 之后的 registry 存储目录结构
[图片上传失败...(image-adef3f-1666173662460)]
源码解析
# 入口
# GC command distribution/registry/root.go L43
初始化driver, registry, 参数等
# distribution/registry/storage/garbagecollect.go
func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, registry distribution.Namespace, opts GCOpts) error {
repositoryEnumerator, ok := registry.(distribution.RepositoryEnumerator)
if !ok {
return fmt.Errorf("unable to convert Namespace to RepositoryEnumerator")
}
// mark ——> 标记阶段
···
// sweep ——> 删除阶段
···
return err
}
mark 阶段
markSet := make(map[digest.Digest]struct{})
manifestArr := make([]ManifestDel, 0)
// 按仓库进行遍历
err := repositoryEnumerator.Enumerate(ctx, func(repoName string) error {
emit(repoName)
var err error
named, err := reference.WithName(repoName)
if err != nil {
return fmt.Errorf("failed to parse repo name %s: %v", repoName, err)
}
repository, err := registry.Repository(ctx, named)
if err != nil {
return fmt.Errorf("failed to construct repository: %v", err)
}
manifestService, err := repository.Manifests(ctx)
if err != nil {
return fmt.Errorf("failed to construct manifest service: %v", err)
}
manifestEnumerator, ok := manifestService.(distribution.ManifestEnumerator)
if !ok {
return fmt.Errorf("unable to convert ManifestService into ManifestEnumerator")
}
// 遍历每个 manifest 索引
err = manifestEnumerator.Enumerate(ctx, func(dgst digest.Digest) error {
// 如果开启 -m,即 delete manifests that are not currently referenced via tag
if opts.RemoveUntagged {
// 此 manifest 关联 tag 列表
tags, err := repository.Tags(ctx).Lookup(ctx, distribution.Descriptor{Digest: dgst})
if err != nil {
return fmt.Errorf("failed to retrieve tags for digest %v: %v", dgst, err)
}
// 如果关联的 tag 列表未空,则表示此 manifest 文件需要被删除
if len(tags) == 0 {
emit("manifest eligible for deletion: %s", dgst)
// fetch all tags from repository
// all of these tags could contain manifest in history
// which means that we need check (and delete) those references when deleting manifest
allTags, err := repository.Tags(ctx).All(ctx)
if err != nil {
return fmt.Errorf("failed to retrieve tags %v", err)
}
// 标记此 manifest 文件需要删除
manifestArr = append(manifestArr, ManifestDel{Name: repoName, Digest: dgst, Tags: allTags})
return nil
}
}
// 标记此 manifest 文件的所有层文件
emit("%s: marking manifest %s ", repoName, dgst)
markSet[dgst] = struct{}{}
// 获取 manifest 文件
manifest, err := manifestService.Get(ctx, dgst)
if err != nil {
return fmt.Errorf("failed to retrieve manifest for digest %v: %v", dgst, err)
}
// 获取所有 layer
descriptors := manifest.References()
for _, descriptor := range descriptors {
// 标记 layer
markSet[descriptor.Digest] = struct{}{}
emit("%s: marking blob %s", repoName, descriptor.Digest)
}
return nil
})
// In certain situations such as unfinished uploads, deleting all
// tags in S3 or removing the _manifests folder manually, this
// error may be of type PathNotFound.
//
// In these cases we can continue marking other manifests safely.
if _, ok := err.(driver.PathNotFoundError); ok {
return nil
}
return err
})
if err != nil {
return fmt.Errorf("failed to mark: %v", err)
}
删除阶段
vacuum := NewVacuum(ctx, storageDriver)
// 非 dryrun 模式
if !opts.DryRun {
// 删除无 tag 引用的 manifest 索引
for _, obj := range manifestArr {
err = vacuum.RemoveManifest(obj.Name, obj.Digest, obj.Tags)
if err != nil {
return fmt.Errorf("failed to delete manifest %s: %v", obj.Digest, err)
}
}
}
// 获取所有需要被删除的 blob 文件的 digest
blobService := registry.Blobs()
deleteSet := make(map[digest.Digest]struct{})
err = blobService.Enumerate(ctx, func(dgst digest.Digest) error {
// check if digest is in markSet. If not, delete it!
if _, ok := markSet[dgst]; !ok {
deleteSet[dgst] = struct{}{}
}
return nil
})
if err != nil {
return fmt.Errorf("error enumerating blobs: %v", err)
}
emit("\n%d blobs marked, %d blobs and %d manifests eligible for deletion", len(markSet), len(deleteSet), len(manifestArr))
// 删除层文件
for dgst := range deleteSet {
emit("blob eligible for deletion: %s", dgst)
if opts.DryRun {
continue
}
err = vacuum.RemoveBlob(string(dgst))
if err != nil {
return fmt.Errorf("failed to delete blob %s: %v", dgst, err)
}
}
return err
Registry GC 存在的问题
多架构镜像 GC 后无法拉取
将多个架构镜像存储在不同的 repository 时,存储目录如下,重点观察索引的组织方式,其他目录已省略
每个仓库下,tag 对应的 manifest 文件的索引在 revisions 目录均有对应
但在多架构仓库 nginx 中,revisions 目录下却多了 2 个索引,分别对应 amd64、arm 架构镜像
repositories
└── library
├── nginx-amd64
│ ├── _manifests
│ │ ├── revisions
│ │ │ └── sha256
│ │ │ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ │ │ └── link
│ │ └── tags
│ │ └── v1
│ │ ├── current
│ │ │ └── link
│ │ └── index
│ │ └── sha256
│ │ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ │ └── link
├── nginx-arm
│ ├── _manifests
│ │ ├── revisions
│ │ │ └── sha256
│ │ │ └── 6575132f2098216b9ac0140aaa4603ef55054891d7917200f4b827e8aa557ed3
│ │ │ └── link
│ │ └── tags
│ │ └── v1
│ │ ├── current
│ │ │ └── link
│ │ └── index
│ │ └── sha256
│ │ └── 6575132f2098216b9ac0140aaa4603ef55054891d7917200f4b827e8aa557ed3
│ │ └── link
└── nignx
├── _manifests
│ ├── revisions
│ │ └── sha256
│ │ ├── 62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
│ │ ├── 6575132f2098216b9ac0140aaa4603ef55054891d7917200f4b827e8aa557ed3
│ │ │ └── link
│ │ ├── 91b09d6ba61abfdb0da82f0d4cab86a3ebb1c60848c1735c419b652e45f0767e
│ │ │ └── link
│ │ └── ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
│ │ └── link
│ └── tags
│ └── v1
│ ├── current
│ │ └── link
│ └── index
│ └── sha256
│ └── 91b09d6ba61abfdb0da82f0d4cab86a3ebb1c60848c1735c419b652e45f0767e
│ └── link
└── _uploads
此时如果添加 -m 参数进行 GC
# registry garbage-collect -m /etc/docker/registry/config.yml
library/nginx-amd64
library/nginx-amd64: marking manifest sha256:ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
library/nginx-amd64: marking blob sha256:605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85
library/nginx-amd64: marking blob sha256:a2abf6c4d29d43a4bf9fbb769f524d0fb36a2edab49819c1bf3e76f409f953ea
library/nginx-amd64: marking blob sha256:a9edb18cadd1336142d6567ebee31be2a03c0905eeefe26cb150de7b0fbc520b
library/nginx-amd64: marking blob sha256:589b7251471a3d5fe4daccdddfefa02bdc32ffcba0a6d6a2768bf2c401faf115
library/nginx-amd64: marking blob sha256:186b1aaa4aa6c480e92fbd982ee7c08037ef85114fbed73dbb62503f24c1dd7d
library/nginx-amd64: marking blob sha256:b4df32aa5a72e2a4316aad3414508ccd907d87b4ad177abd7cbd62fa4dab2a2f
library/nginx-amd64: marking blob sha256:a0bcbecc962ed2552e817f45127ffb3d14be31642ef3548997f58ae054deb5b2
library/nginx-arm
library/nginx-arm: marking manifest sha256:6575132f2098216b9ac0140aaa4603ef55054891d7917200f4b827e8aa557ed3
library/nginx-arm: marking blob sha256:b7dd3d7d83385d0bad882b2a2e1298d2c2003dd58eeae7d959e183b8d8392b9b
library/nginx-arm: marking blob sha256:1dd75a3a9c893a7dc313f683dd62464b7eab6c6d522ee62c8a17022631830f32
library/nginx-arm: marking blob sha256:7db321c265d888c6653db5939cfefe58dcd57184beedea3d273c4e1b413087ee
library/nginx-arm: marking blob sha256:30e66ba016bdf4dae566ccaeae31a2b217de50fca6f913c746e8c818c556480f
library/nginx-arm: marking blob sha256:7365dfc955ef5860c3334f28683a9bb695f64e8a8da05a1f34419ff91ff207eb
library/nginx-arm: marking blob sha256:f3e27355fff573b08f5b87c2fcc4dfa1d32ae64c26cd1e19fbe48a29de009fd5
library/nginx-arm: marking blob sha256:de0fdbb1c0c24ca36b9d51aff08d75f9a14cb1af42155599b8d0cff7ec7b20ea
library/nignx
manifest eligible for deletion: sha256:6575132f2098216b9ac0140aaa4603ef55054891d7917200f4b827e8aa557ed3
library/nignx: marking manifest sha256:91b09d6ba61abfdb0da82f0d4cab86a3ebb1c60848c1735c419b652e45f0767e
library/nignx: marking blob sha256:ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
library/nignx: marking blob sha256:6575132f2098216b9ac0140aaa4603ef55054891d7917200f4b827e8aa557ed3
manifest eligible for deletion: sha256:ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
INFO[0000] deleting manifest: /docker/registry/v2/repositories/library/nignx/_manifests/revisions/sha256/6575132f2098216b9ac0140aaa4603ef55054891d7917200f4b827e8aa557ed3 go.version=go1.11.2 instance.id=cefd807b-a4e5-4c1d-b705-9b484da1acdc service=registry
INFO[0000] deleting manifest: /docker/registry/v2/repositories/library/nignx/_manifests/revisions/sha256/ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3 go.version=go1.11.2 instance.id=cefd807b-a4e5-4c1d-b705-9b484da1acdc service=registry
可以发现,在多架构仓库 nginx 中,revisions 目录下对应 amd64、arm 架构镜像的索引被删除了,此时 pull 镜像
# # docker pull localhost:5000/library/nginx:v1
v1: Pulling from library/nginx
manifest for localhost:5000/library/nginx:v1 not found: manifest unknown: manifest unknown
根因分析:
manifest 标记是按仓库进行的,由于在多架构仓库 nginx 中,amd64、arm 架构镜像的索引并没有 tag 对应,GC 时就会被删除,多架构镜像在拉取制定 platform 的镜像时,由于索引缺失,报了如上错误
GC 不彻底
_layers/sha256/digest/link删除不彻底
registry 无论是删除一个镜像还是进行 GC 操作,都不会删除 repositories 目录下的 _layers/sha256/digest/link
文件,在进行 GC 之后,一些镜像 layer 和 config 文件已经在 blobs 存储目录下删除了,但指向它的 layers/link 文件依旧保存在 repositories 目录下。GitHub 上有个 PR Remove the layer’s link by garbage-collect #2288 就是专门来清理这些无用的 layer link 文件的,最早的一个是三年前的,但是还没有合并
不使用 -m 参数时,blob 文件删除不彻底
在不使用 -m 参数时,没有 tag 引用的 manifest 文件所对应的 blob 文件也不会被删除,虽然添加 -m 参数可以解决此问题,但会导致多架构镜像 GC 后无法拉取
多架构镜像 pull 流程
待补充
参考资料