深入理解Moby Buildkit系列 #28 - SourceOp CacheMap

497 阅读3分钟

这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战

Edges inputs - edge pipes.jpg 要想理解递归,除了抽象出同样的行为外,再就是找到循环的出口。 在上一次梳理的结果中可以看到,Edge1是递归的最后一个edge,在edge中,由于SourceOp的inputs为空,所以在edge中的出口是pipe3,也就是op.CacheMap:

f.NewFuncRequest(func(ctx context.Context) (interface{}, error) {
   cm, err := e.op.CacheMap(ctx, index)
   return cm, errors.Wrap(err, "failed to load cache key")
})

那现在是时候看一看这个自定义函数做了哪些处理,这样就是能调用pipe.OnSendCompletion来完成并退出这个操作,也就是递归结束开始的信号。

e.op.CacheMap究竟做了什么呢? 经过梳理,袁小白终于找到了CacheMap真正干活的那个了: Source of Truth - CacheMap.jpg

  1. 出发点-edge unpark,在这里将op.CacheMap封装成自定义函数,用于新建pipe
  2. op来自于jobs里用newSharedOp创建出来的sharedOp,这样可以将vertex封闭成在上一阶段处理所需要的状态和信息
  3. 在sharedOp的CacheMap里,是通过s.getOp,也就是sharedOp.getOp方法来获取op的
  4. 在上一步中,又是用的sharedOp的resolver来解析的,而这个resolver的类型是state.opts.ResolveOpFunc,它来自于jl.opts
  5. jl指的还是jobs,是在NewSolver创建方法被调用的时候,传入的SolverOpt来帮助说明的
  6. NewSolver是在solver的New方法里被调用的
  7. 实际的解析是发生在worker里的ResolveOp方法,在咱们的例子中,因为最后一个op是source op,对应在dockerfile里的指令就是FROM FOO
  8. 最终,是由我们ops.NewSourceOp方法创建出来的SourceOp实例,调用自己的CacheMap来真正处理这一请求的

梳理完后,袁小白不禁松了口气。 这个传递链也太长了,好多前面命名是用这个,后面是那个;要么就是从前到后用的都一个,一不留神就断掉了,难怪源码看起来这么费劲费时间。

好在有龙飞的全流程图,来对比着检验一下: Buildctl build.jpg 关键的点都有,还有一些细节看来可以补充下。

那再看看好容易找到的最终处理代码:

func (s *sourceOp) CacheMap(ctx context.Context, g session.Group, index int) (*solver.CacheMap, bool, error) {
   src, err := s.instance(ctx)
   if err != nil {
      return nil, false, err
   }

   k, pin, cacheOpts, done, err := src.CacheKey(ctx, g, index)
   if err != nil {
      return nil, false, err
   }

   dgst := digest.FromBytes([]byte(sourceCacheType + ":" + k))
   if strings.HasPrefix(k, "session:") {
      dgst = digest.Digest("random:" + strings.TrimPrefix(dgst.String(), dgst.Algorithm().String()+":"))
   }

   var buildInfo map[string]string
   if !strings.HasPrefix(s.op.Source.GetIdentifier(), "local://") {
      buildInfo = map[string]string{s.op.Source.GetIdentifier(): pin}
   }

   return &solver.CacheMap{
      // TODO: add os/arch
      Digest:    dgst,
      Opts:      cacheOpts,
      BuildInfo: buildInfo,
   }, done, nil
}

看来先是初始化一个sourceOp的实例 - s.instance(ctx),再根据缓存健从缓存中取对应的信息 - src.CacheKey(ctx, g, index)。 没忍住点进instance方法看了一下,发现这个实例,指的是实现了source.SourceInstance接口的对象,由s.sm.Resolve方法进行的解析,sm指的是source.Manager,Resolve方法也就是从自己的sources源中取出实例,可另一个问题又冒了出来 - 这些sources又是在哪里被装载的呢?

下一篇:深入理解Moby Buildkit系列 #29 - SourceOp instance CacheKey