docker 源码分析daemon 消息处理2
接上回,如果是V2版本的Puller,位于pull_v2.go文件
func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) { p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") ... if err = p.pullV2Repository(ctx, ref); err != nil { ... }
新建一个V2版本的仓库赋值给p.repo,调用pullV2Respository函数
func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) { var layersDownloaded bool if !reference.IsNameOnly(ref) { layersDownloaded, err = p.pullV2Tag(ctx, ref) ... } else { ... for _, tag := range tags { tagRef, err := reference.WithTag(ref, tag) .... pulledNew, err := p.pullV2Tag(ctx, tagRef) ... } } ... }
有tag的话,遍历tag,每个tag调用pullV2Tag,没有的话直接调用pullV2Tag
func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) { .... switch v := manifest.(type) { case *schema1.SignedManifest: if p.config.RequireSchema2 { return false, fmt.Errorf("invalid manifest: not schema2") } id, manifestDigest, err = p.pullSchema1(ctx, ref, v) if err != nil { return false, err } case *schema2.DeserializedManifest: id, manifestDigest, err = p.pullSchema2(ctx, ref, v) if err != nil { return false, err } case *manifestlist.DeserializedManifestList: id, manifestDigest, err = p.pullManifestList(ctx, ref, v) if err != nil { return false, err } default: return false, errors.New("unsupported manifest format") } .... }
先是获取 manifest 属性,根据属性的类型做不同的处理。pullManifestList 最终调用 pullSchema1或者pullSchema2 ,这2个函数只是对于参数的处理方式不同,最终下载镜像都是调用同样的处理函数
rootFS, release :=p.config.DownloadManager.Download(ctx, *rootFS, descriptors, p.config.ProgressOutput)
imageID := p.config.ImageStore.Put(configJSON) // ImageStore: distribution.NewImageConfigStoreFromStore(daemon.imageStore),
DownloadManager 根据config初始化函数就应该是daemon的DownloadManager,而daemon的DownloadManager是通过下面函数构建的d.downloadManager = xfer.NewLayerDownloadManager(d.layerStore, *config.MaxConcurrentDownloads),位于distribution/xfer/download.go 是一个分层下载的阻塞函数。
if topDownload != nil { xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload) defer topDownload.Transfer.Release(watcher) } else { xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil) } topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput)
download内调用makeDownloadFunc创建下载函数xferFunc,xferFunc函数内部初始化一个downloadTransfer 并返回。
xferFunc内部创建一个协程处理progressChan通道,调用descriptor.Download(..) , 这个是pull_v2.go内部初始化的v2LayerDescriptor,返回 io.ReadCloser,
再通过io.ReadCloser 创建 reader解压数据,利用downloadTransfer处理数据。
Transfer函数位于/distribution/xfer/transfer.go,调用xferFunc创建 downloadTransfer并把通道信息传给xferFunc创建的协程,返回Transfer, Watcher
Put是下面函数
func (s *imageConfigStore) Put(c []byte) (digest.Digest, error) { id, err := s.Store.Create(c) return digest.Digest(id), err }
调用 image/store.go 的Create函数把byte数据在本地创建image