这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战
梳理完State - Definition - Edge,已经过去将近一个月了。 袁小白感觉实现纯手工打造,24K容器化运行生态,是越来越有信心了!
可话说回来。 我们看到哪儿了? 为什么在看这三个数据结构? 接下来要看啥? 袁小白突然感觉面临着人生三大终级问题。
好在按buildctl build全流程回顾后,发现,我们正在看Frontend,看到了dockerfile.Build,因为这个函数很长,长达近500行,发现关键地方出现了llb.State,为了搞清楚llb.State到底做了什么,所以转而去了解数据结构去了,现在好容易看完,再回过头来看看之前我们到达的地方:
func Build(ctx context.Context, c client.Client) (*client.Result, error) {
...
res := client.NewResult()
...
for i, tp := range targetPlatforms {
func(i int, tp *ocispecs.Platform) {
...
st, img, err := dockerfile2llb.Dockerfile2LLB(...)
...
}
...
def, err := st.Marshal(ctx)
...
r, err := c.Solve(ctx, client.SolveRequest{
Definition: def.ToPB(),
CacheImports: cacheImports,
})
...
ref, err := r.SingleRef()
...
res.SetRef(ref)
...
}
...
return res, nil
}
再回过头来看这一段代码,袁小白感觉一目了然,神清气爽,这感觉就像是被打通了任督二脉。
- 通过dockerfile2llb.Dockerfile2LLB方法,将dockerfile转换成State
- 然后将返回的State - st.Marshal转换成def - Definition
- 再重新解析一次c.Solve,这里的c - client也就是传入的llbBridge;并传入def.ToPB()
- 最后得到解析结果,并返回
数据结构是没太大的问题了,帮助我们理解起这小500行的代码,变得轻松了起来。 可这晨为什么要又调用一次llbBridge解析一遍呢? 在前面的理解是,这里传入的参数和第一次解析传入的参数不一样,所以结果不一样,再看看解析源码:
func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest, sid string) (res *frontend.Result, err error) {
if req.Definition != nil && req.Definition.Def != nil && req.Frontend != "" {
return nil, errors.New("cannot solve with both Definition and Frontend specified")
}
if req.Definition != nil && req.Definition.Def != nil {
res = &frontend.Result{Ref: newResultProxy(b, req)}
if req.Evaluate {
_, err := res.Ref.Result(ctx)
return res, err
}
} else if req.Frontend != "" {
f, ok := b.frontends[req.Frontend]
if !ok {
return nil, errors.Errorf("invalid frontend: %s", req.Frontend)
}
res, err = f.Solve(ctx, b, req.FrontendOpt, req.FrontendInputs, sid, b.sm)
if err != nil {
return nil, err
}
} else {
return &frontend.Result{}, nil
}
return
}
果然,同样的解析,这里有两个分支,一个是当req.Definitoin不为空时,一个是req.Frontend不为空时,前面一次是因为req.Frontend传入的是dockerfile.v0,是从buildctl build命令行参数里读取的。
而这一次是由Frontend dockerfile.Build方法,主动发起的调用,传入的req.Definition不为空。
那就对上了,那最终返回的结果res就是res = &frontend.Result{Ref: newResultProxy(b, req)}。
也就是说绕了一圈后,最终返回的结果是bridge.go里的Result。
真是把自己牛X坏了...
再看龙飞的时序图,真是不得不佩服,接下来就看真正的Build了。