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