深入理解Moby Buildkit系列 #15 - 神奇的llb.State

582 阅读3分钟

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

回想起因为dockerfile.Build里如何使用llb的场景: Screen Shot 2021-11-16 at 7.49.36 PM.png 袁小白想着,对llb有了大概的理解后,如何找到一个难度适宜的例子,对llb.State进行学习呢。

经过一番查找,真是黄天不负有心人,还真就找到了: Screen Shot 2021-11-16 at 7.52.58 PM.png 从名字上来看,这个测试用例测试的场景是默认平台。 通过s := llb.Image("foo").Run(llb.Shlex("bar"))组装好构建语言,基于image - "foo",运行脚本"bar",然后对状态进行整治s.Marshal,还记得这里是可以传入默认平台的,目前是缺省,那就是用的默认平台;接着用llbsolver.Load来加载,这个好像是第一次出现,并且返回的类型是solver.Edge,这个类型也是第一次出现,那这个类型和之前的Definition, State又有什么关系呢?最后对比了一下默认平台和拼装的image id。

为了弄清楚在整个过程中,llb.State到底起到了什么样的作用,是如何将这些状态都关联起来的。 袁小白准备从最关键的一句开始:

s := llb.Image("foo").Run(llb.Shlex("bar"))

经过一番挣扎,耗时好几个晚上的时间,终于把关系给梳理了出来: State concept.jpg 从图中也可以看出,实例间的关联结构确实有点绕。

  • 前半句llb.Image("foo"),创建了一个SourceOp,也就是源操作
  • 后半句.Run(llb.shlex("bar")),创建了一个ExecOp,执行操作
  • 每一个Op的结构里都需要实现Output()接口,也可以理解为每一个操作都需要一个执行结果,这里的SourceOp的output对象里包含了一个Vertex,并指向SourceOp自己,这里就不好理解,这个SoruceOp和output到底是个什么关系,分别有什么侧重。同样在ExecOp里,也实现了Output接口,但这里对应的属性是root,袁步白猜想这是因为这个执行操作需要依赖根文件系统的缘故,后面可以进一步验证,和SourceOp一样,也有一个output,并指向自己的Vertex。这个频繁出现的Vertex又是什么?
  • 最绕的还属于这个State结构体
    • 前半句创建S1(State)的过程如图所示,指名了output
    • 并在S1的基础上分别执行了Dir("/")操作,但这里并没有生成新的State,而是withValuer操作生成了S2,S2的prev属性链接了S1
    • 而后半句,并没有直接生成S3,而是先生成es1(ExecState),原来对应不同的Op还有不同的State,但为了和之前的State关联上,实际上es1有对应的S3,这里的操作和S2一样,有指向自己的State,和前一个结点S2

这样梳理下来,确实清晰了不少,但整体的感觉还是很模糊。 为了再找到一些线索,袁小白决定从源码的角度再梳理总结一遍,看能不能得到一些启发。

下一篇:深入理解Moby Buildkit系列 #16 - 神奇llb.State的代码实现