harbor-3.registry

171 阅读4分钟

registry介绍

docker镜像仓库 源码:github.com/docker/dist…

构建

make
./bin/registry --version

配置

命令: registry serve /etc/registry/config.yml 配置文件包含监听端口、auth 地址、存储驱动信息、回调通知等。 /etc/registry/config.yml

version: 0.1
log:
  level: info
  fields:
    service: registry
storage:
  cache:
    layerinfo: redis
  filesystem:
    rootdirectory: /storage
  maintenance:
    uploadpurging:
      enabled: false
  delete:
    enabled: true
redis:
  addr: redis:6379
  password: 
  db: 1
http:
  addr: :5000
  secret: placeholder
  debug:
    addr: localhost:5001
auth:
  token:
    issuer: harbor-token-issuer
    realm: http://test.docker.jd.com/service/token
    rootcertbundle: /etc/registry/root.crt
    service: harbor-registry
validation:
  disabled: true
notifications:
  endpoints:
  - name: harbor
    disabled: false
    url: http://core:8080/service/notifications
    timeout: 3000ms
    threshold: 5
    backoff: 1s
compatibility:
  schema1:
    enabled: true

存储

支持多种储存类型

inmemory/filesystem/aws/自实现

存储结构

同一镜像,在不同镜像仓库中,存储的方式和内容完全一样。文章中通过实验得出如下结论: 通过 Registry API 获得的两个镜像仓库中相同镜像的 manifest 信息完全相同。 两个镜像仓库中相同镜像的 manifest 信息的存储路径和内容完全相同。 两个镜像仓库中相同镜像的 blob 信息的存储路径和内容完全相同。

.
└── docker
    └── registry
        └── v2
            ├── blobs
            │   └── sha256
            │       ├── 07
            │       │   └── 0766572b4bacfaee9a8eb6bae79e6f6dbcdfac0805c7c6ec8b6c2c0ef097317a
            │       ├── 9a
            │       │   └── 9a597e826a59709a4af34279f496c323d496a79e4c998ee5249a738e391192bb
            │       └── 9c
            │           └── 9ca846b27f6e92f0739af5bba5509357b52be0ce0dd02d216f4dccdacd695a8a
            └── repositories
                ├── alpine
                │   ├── _layers
                │   │   └── sha256
                │   │       ├── 0766572b4bacfaee9a8eb6bae79e6f6dbcdfac0805c7c6ec8b6c2c0ef097317a
                │   │       └── 9ca846b27f6e92f0739af5bba5509357b52be0ce0dd02d216f4dccdacd695a8a
                │   ├── _manifests
                │   │   ├── revisions
                │   │   │   └── sha256
                │   │   │       └── 9a597e826a59709a4af34279f496c323d496a79e4c998ee5249a738e391192bb
                │   │   └── tags
                │   │       └── 3.4
                │   │           ├── current
                │   │           └── index
                │   │               └── sha256
                │   │                   └── 9a597e826a59709a4af34279f496c323d496a79e4c998ee5249a738e391192bb
                │   └── _uploads
                └── library
                    └── alpine
                        ├── _layers
                        │   └── sha256
                        │       ├── 0766572b4bacfaee9a8eb6bae79e6f6dbcdfac0805c7c6ec8b6c2c0ef097317a
                        │       └── 9ca846b27f6e92f0739af5bba5509357b52be0ce0dd02d216f4dccdacd695a8a
                        ├── _manifests
                        │   ├── revisions
                        │   │   └── sha256
                        │   │       └── 9a597e826a59709a4af34279f496c323d496a79e4c998ee5249a738e391192bb
                        │   └── tags
                        │       └── 3.4
                        │           ├── current
                        │           └── index
                        │               └── sha256
                        │                   └── 9a597e826a59709a4af34279f496c323d496a79e4c998ee5249a738e391192bb
                        └── _uploads

运行原理

启动

本质上 Registry 是个 HTTP 服务,启动后,监听在配置文件设定的某端口上。当 http 请求过来后,便会触发之前注册过的 handler。Handler 包含 manifest、tag、blob、blob-upload、blob-upload-chunk、catalog 等六类,具体请可参考 registry 源码:/registry/handlers/app.go。

    // Register the handler dispatchers.
    app.register(v2.RouteNameBase, func(ctx *Context, r *http.Request) http.Handler {
        return http.HandlerFunc(apiBase)
    })
    app.register(v2.RouteNameManifest, imageManifestDispatcher)
    app.register(v2.RouteNameCatalog, catalogDispatcher)
    app.register(v2.RouteNameTags, tagsDispatcher)
    app.register(v2.RouteNameBlob, blobDispatcher)
    app.register(v2.RouteNameBlobUpload, blobUploadDispatcher)
    app.register(v2.RouteNameBlobUploadChunk, blobUploadDispatcher)

代码流程:

/cmd/registry/main.go: registry.RootCmd.Execute()->

/registry/root.go: RootCmd.AddCommand(ServeCmd)注册命令->

/registry/registry.go: NewRegistry()->

/registry/registry.go: 加载配置文件(默认在/etc/docker/registry/config.yml)->

/registry/handler/app.go: NewApp()中注册handler->

/registry/registry.go: registry.ListenAndServe()监听

pull

当 pull 一个镜像时,先进行认证获取到 token 并授权通过,然后获取镜像的 manifest 文件,进行 signature 校验。校验完成后,依据 manifest 里的层信息并发拉取各层。 各层拉下来后,也会先在本地进行校验,校验算法采用 sha256。

监听docker请求,分配Handler从registry存储后端读取数据,读取时会根据不同的后端调用相应的storagedriver,读取数据之后将数据返回给docker端

registry/handlers/blob.go 具体提供服务的就是blobHandler.GetBlob函数

// GetBlob fetches the binary data from backend storage returns it in the response.
func (bh *blobHandler) GetBlob(w http.ResponseWriter, r *http.Request) {
    context.GetLogger(bh).Debug("GetBlob")
    blobs := bh.Repository.Blobs(bh)
    desc, err := blobs.Stat(bh, bh.Digest)
    if err != nil {
        if err == distribution.ErrBlobUnknown {
            bh.Errors = append(bh.Errors, v2.ErrorCodeBlobUnknown.WithDetail(bh.Digest))
        } else {
            bh.Errors = append(bh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
        }
        return
    }
    if err := blobs.ServeBlob(bh, w, r, desc.Digest); err != nil {
        context.GetLogger(bh).Debugf("unexpected error getting blob HTTP handler: %v", err)
        bh.Errors = append(bh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
        return
    }
}

desc, err := blobs.Stat(bh, bh.Digest)实现位于registry/storage/blobstore.go中,返回的是一个distribution.Descriptor,该结构包括MediaType、Size和Digest,可以用来fetch、store和target任何blob。最开始的代码

path, err := pathFor(blobDataPathSpec{digest: dgst,})根据digest返回blob的路径,该路径是从/docker/registry/v2开始的,不包括在yaml文件中配置的那部分前缀。这个函数并没有直接和磁盘交互,因为distribution将manifest、tag、blob等内容存入特定的目录,所以在这里只是根据用户要提取的内容组合出相应的目录返回。但是在pathFor之后的代码确实通过driver和磁盘交互了,返回了文件的大小以及建立时间等信息,针对于本地文件系统,函数的实现位于registry/storage/driver/filesystem/driver.go中,该函数组建了blob的全路径并通过读磁盘确立了路径的有效性

push

Push 过程则先将镜像各层并发推至 Registry,推送完成后,再将镜像的 manifest 推至 Registry。 同理执行blobUploadDispatcher