Harbor查询镜像接口

背景

当Harbor中的某个镜像下的标签数目过多时,调用Harbor查询tag接口有时候会变得很慢,所以就想知道为什么Harbor官方不通过分页的形式来获取tag而且要一次性全部查询出来

接口调用逻辑

当Harbor页面去查询一个镜像的所有版本详细信息时,会调用一个/api/repositories/{project}/{image}/tags接口,通过查询源码我发现其内部调用逻辑如下

  • 发送/api/repositories/{project}/{image}/tags会被core/router.go接收并处理
1
beego.Router("/api/repositories/*/tags", &api.RepositoryAPI{}, "get:GetTags;post:Retag")
  • 调用core/api/repository.go的GetTag方法
1
2
3
4
5
6
7
8
9
10
11
func (ra *RepositoryAPI) GetTags() {
...
client, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repoName)
tags, err := client.ListTag()
...

# 通过client获取镜像下所有的tags,然后根据tags查询Harbor自身的数据库填充每个版本下镜像的标签、创建时间、大小等信息返回(assembleTagsInParallel方法)
ra.Data["json"] = assembleTagsInParallel(client, repoName, tags,
ra.SecurityCtx.GetUsername())
ra.ServeJSON()
}
  • 在GetTag内部通过client的ListTag方法获取tags
1
2
3
4
5
6
7
8
9
10
# common/utils/registry/repository.go
func (r *Repository) ListTag() ([]string, error) {
...
req, err := http.NewRequest("GET", buildTagListURL(r.Endpoint.String(), r.Name), nil)
...
}

func buildTagListURL(endpoint, repoName string) string {
return fmt.Sprintf("%s/v2/%s/tags/list", endpoint, repoName)
}
  • client发起第二次Restful接口调用
1
2
# core/router.go
beego.Router("/v2/*", &controllers.RegistryProxy{}, "*:Handle")

看这里可以知道Harbor查询镜像版本详细信息的接口实际上就是docker registry的V2/tags/list接口

harbor的架构

image-20180910192701065

Proxy:底层实际上就是跑了一个nginx的容器,向docker client及浏览器暴露端口,将这些客户端发来的请求反向代理到后端Core Service、Registry。

Registry:这个其实是就是官方的docker registry,其配置了webhook到Core Services,这样当镜像的状态发生变化时,可通知到Core Services。

Core Services:这个里面内容就比较多了,主要由多个http服务组成,完成的功能主要有以下几点:

  • 监听Registry上镜像的变化,做相应处理,比如记录日志、发起复制等
  • 充当Docker Authorization Service的角色,对镜像资源进行基于角色的鉴权
  • 连接Database,提供存取projects、users、roles、replication policies和images元数据的API接口
  • 提供UI界面:从目前的代码来看主要有这4个部分ui(这个感觉改名为controller好一点)、adminserver、registryctl、portal。

Job Service:定时执行一些任务,提供API供外部提交任务及查询执行结果。

Log collector:说白了就是一个rsyslog日志服务,其它组件可以将日志发送到这里,它负责集中存储。

Database:就是mysql数据库服务,用于存储projects、users、roles、replication policies和images的元数据。

docker registry

查询Harbor makefile的配置文件可以看到

1
2
#versions
REGISTRYVERSION=v2.6.2

查询对应docker registry2.6版本的源码https://github.com/docker/distribution/blob/c192a281f8ac6f2a351fe729c8a56108f8edb377/registry/handlers/tags.go#L35

GetTags方法逻辑如下,内部调用了tagService.All方法来获取全部tag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()

tagService := th.Repository.Tags(th)
tags, err := tagService.All(th)
if err != nil {
switch err := err.(type) {
case distribution.ErrRepositoryUnknown:
th.Errors = append(th.Errors, v2.ErrorCodeNameUnknown.WithDetail(map[string]string{"name": th.Repository.Named().Name()}))
case errcode.Error:
th.Errors = append(th.Errors, err)
default:
th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
}
return
}

w.Header().Set("Content-Type", "application/json")

enc := json.NewEncoder(w)
if err := enc.Encode(tagsAPIResponse{
Name: th.Repository.Named().Name(),
Tags: tags,
}); err != nil {
th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
return
}
}

从blobStore数据库获取镜像的所有tag

https://github.com/docker/distribution/blob/c192a281f8ac6f2a351fe729c8a56108f8edb377/registry/storage/tagstore.go#L25

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// All returns all tags
func (ts *tagStore) All(ctx context.Context) ([]string, error) {
var tags []string

pathSpec, err := pathFor(manifestTagPathSpec{
name: ts.repository.Named().Name(),
})
if err != nil {
return tags, err
}

entries, err := ts.blobStore.driver.List(ctx, pathSpec)
if err != nil {
switch err := err.(type) {
case storagedriver.PathNotFoundError:
return tags, distribution.ErrRepositoryUnknown{Name: ts.repository.Named().Name()}
default:
return tags, err
}
}

for _, entry := range entries {
_, filename := path.Split(entry)
tags = append(tags, filename)
}

return tags, nil
}

参考

http://www.dockerinfo.net/3597.html

https://jeremy-xu.oschina.io/2018/09/harbor%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB/

https://github.com/docker/distribution/tree/release/2.6