Skip to content

Sealer Image Cache

Eric Bao edited this page Aug 6, 2021 · 1 revision

本文将描述sealer镜像的cache机制,即sealer build时会复用的cache,而非容器镜像的cache。

名次解释

ChainID:ChainID的类型就是一个digest:type ChainID digest.Digest,不过其含义是表示一组文件系统的digest值,所以这个值的计算是一个或多个layer组合计算来的。ChainID相同,则表示有着相同的文件系统,那么说明layer可复用,所以可以依靠ChainID进行cache命中。

cache实现

sealer在启动时,会读取/var/lib/sealer文件系统,将所有layer进行读取。

func (cs *chainStore) newCacheLayer(layer *v1.Layer) (*Layer, error) {
	var cacheLayer = Layer{Type: layer.Type, Value: layer.Value}
	// only copy layer needs the cache id.
	if layer.Type != common.COPYCOMMAND {
		return &cacheLayer, nil
	}

	cacheIDBytes, err := cs.fs.GetMetadata(layer.ID, common.CacheID)
	if err != nil {
		return nil, err
	}
	// TODO maybe we should validate the cacheid over digest
	cacheLayer.CacheID = string(cacheIDBytes)
	return &cacheLayer, nil
}
func (cs *chainStore) restore() error {
	cs.Lock()
	defer cs.Unlock()

	//read all image layers
	images := cs.Images()
	for _, image := range images {
		layers := image.Spec.Layers
		var lastChainItem *chainItem = &chainItem{}
		for _, layer := range layers {
			var (
				chainID ChainID
				err     error
			)
                        // 传入v1Layer, 获取cacheLayer。该函数特别的是,对于COPY类型的layer,我们会获取其CacheID,设置到cacheLayer对象上,其比其他类型layer就多了这个字段。
			cacheLayer, err := cs.newCacheLayer(&layer)
			if err != nil {
                                // 这里也许改成Debug会更合适,因为有些layer build失败,可能会没有设置cache id成功。再读的时候就会报错。
                                // 但是不会影响build,只是不会hit cache。
				logger.Warn("failed to new a cache layer for %v, err: %s", layer, err)
				continue
			}

			// first chainItem's parent chainID is empty
                        // 计算chainID的方法是传入parent ChainID。
                        // 具体算法是,digest (parent chainID : layer.CacheID : layer.Type : layer.Value)
			chainID, err = cacheLayer.ChainID(lastChainItem.chainID)
			if err != nil {
				logger.Error(err)
				break
			}
			logger.Debug("current layer %+v, restore chain id: %s", cacheLayer, chainID)

                       // 最后存到map中。
			_, ok := cs.chains[chainID]
			if !ok {
				cItem := &chainItem{
					layer:   layer,
					chainID: chainID,
				}
				cs.chains[chainID] = cItem
			}
			lastChainItem = &chainItem{
				layer:   layer,
				chainID: chainID,
			}
		}
	}

	return nil
}

下面介绍下cache的命中。

//后面可能改成tryCache
func (l *LocalBuilder) goCache(parentID cache.ChainID, layer *v1.Layer, cacheService cache.Service) (continueCache bool, chainID cache.ChainID) {
	var (
		srcDigest = digest.Digest("")
		err       error
	)

	// specially for copy command, we would generate digest of src file as srcDigest.
	// and use srcDigest as cacheID to generate a cacheLayer, eventually use the cacheLayer
	// to hit the cache layer
        // 计算copy中source文件的digest
	if layer.Type == common.COPYCOMMAND {
		srcDigest, err = l.generateSourceFilesDigest(strings.Fields(layer.Value)[0])
		if err != nil {
			logger.Warn("failed to generate src digest, discard cache, err: %s", err)
		}
	}
        // 获取layer对应的cacheLayer,规则如上面提到的,copy layer会接受上面的digest作为CacheID。
	cacheLayer := cacheService.NewCacheLayer(*layer, srcDigest)
        // 试探能够撞到cache,最终就是到上面restore获得的map中查看。
	cacheLayerID, err := l.Prober.Probe(parentID.String(), &cacheLayer)
	if err != nil {
		logger.Debug("failed to probe cache for %+v, err: %s", layer, err)
		return false, ""
	}
	// cache hit
	logger.Info("---> Using cache %v", cacheLayerID)
        // 命中cache,该layer复用之前build成功的layer,这里的cacheLayerID不是cacheID,而是v1Layer中的ID。
	layer.ID = cacheLayerID
	cID, err := cacheLayer.ChainID(parentID)
	if err != nil {
		return false, ""
	}
	return true, cID
}
Clone this wiki locally