本文分析 containerd pull 镜像的分析过程,包括 ctr image 命令行以及 containerd daemon 执行 过程,也包含镜像 metadata,content 等内容。
1. 执行如下命令 ctr image pull
首先分析 ctr 命令,实现在 cmd/ctr 的子命令 listCommand 实现,利用 GRPC 链接到 remote cotainerd 端请求,核心函数 img, err := c.fetch(ctx, fetchCtx, ref, 0)
1.1 fetch 函数
Resolver.Resolve 向 docker registry 请求 mainifest 文件清单, 本文示例镜像 docker.io/library/nginx:latest, 具体实现就不详细讲解, remotes/docker/resolver.go 中 dockerResolver 结构体实现了 Resolver 接口。
func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, limit int) (images.Image, error) {
store := c.ContentStore()
name, desc, err := rCtx.Resolver.Resolve(ctx, ref)
name: 为 docker.io/library/nginx:latest;
desc 为如下内容, Digest 在 response 的 header 里返回的;
1.2 dockerResolver 实现 Fetcher 方法
将镜像进行分析,包括远端 registry 名称, 仓库名称, tag等, 发请求就知道 host 地址
func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
base, err := r.resolveDockerBase(ref)
if err != nil {
return nil, err
}
return dockerFetcher{
dockerBase: base,
}, nil
}
1.3 Dispatch 核心函数
参数 handler 切片包含多个 handler 函数, descs 镜像的文件清单, 递归把子文件清单也处理了。 接着看几个关键的 handler 函数。
func Dispatch(ctx context.Context, handler Handler, limiter *semaphore.Weighted, descs ...ocispec.Descriptor) error {
eg, ctx2 := errgroup.WithContext(ctx)
for _, desc := range descs {
desc := desc
eg.Go(func() error {
desc := desc
children, err := handler.Handle(ctx2, desc)
if len(children) > 0 {
return Dispatch(ctx2, handler, limiter, children...)
}
return nil
})
}
return eg.Wait()
}
handlers := append(rCtx.BaseHandlers,
remotes.FetchHandler(store, fetcher),
convertibleHandler,
childrenHandler,
appendDistSrcLabelHandler,
)
2. remotes FetchHandler
这里 ingester 是 store 接口, 实现的是 proxyContentStore 结构体, 路径为 content/proxy/content_store.go 文件, 其向 containerd 守护进程发起 GRPC 请求。 接着看 fetch 函数实现。
// FetchHandler returns a handler that will fetch all content into the ingester
// discovered in a call to Dispatch. Use with ChildrenHandler to do a full
// recursive fetch.
func FetchHandler(ingester content.Ingester, fetcher Fetcher) images.HandlerFunc {
return func(ctx context.Context, desc ocispec.Descriptor) (subdescs []ocispec.Descriptor, err error) {
switch desc.MediaType {
default:
err := fetch(ctx, ingester, fetcher, desc)
return nil, err
}
}
}
2.1 fetch 函数
content.OpenWriter 向 containerd 发送 GRPC WriteContentRequest 请求, 包括 ref, total, digest。在看 containerd 处理 WriteContentRequest 请求。 containerd 先写入到 io.containerd.content.v1.content/ingest 下, 最后 commit GRPC WriteContentRequest 请求成功时在移入到 io.containerd.content.v1.content/blobs/sha256 目录下。
fetcher.Fetch 实现在 remotes/docker/fetcher.go
func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc ocispec.Descriptor) error {
cw, err := content.OpenWriter(ctx, ingester, content.WithRef(MakeRefKey(ctx, desc)), content.WithDescriptor(desc))
ws, err := cw.Status()
rc, err := fetcher.Fetch(ctx, desc)
return content.Copy(ctx, cw, rc, desc.Size, desc.Digest)
}
总结:
拉取镜像, 首先获取到 mainifest 文件清单, 在递归将子层下载到 content中, 最后将每一层解压到 snapshotter 下。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)