一次dockerfile的循环依赖错误实战记录
作者:phantom_111
1. 写在最前面
笔者在使用 dockerfile 多阶段构建的功能时,写出了一个「circular dependency detected on stage: xx」的错误。
解决方式:解耦互相依赖的构建阶段即可,构建 A <=> 构建 B 两个阶段是互相依赖的,改为构建 A => 构建 B
注:「多阶段构建」是 Docker 提供的一种功能,运行用户在一个 Dockerfile 中定义多个构建阶段,从而优化构镜像的大小和构建过程的效率。通过这种方式,开发者可以在不同的阶段使用不同的基础镜像和工具,最终只将所需要的文件和依赖项复制到最终的镜像中。
但是,作为一个有求知精神的软件开发工程师,笔者去翻看了一下源码的位置。(ps: 其实就是自己感兴趣 BuildKit 的源码想要学习一下,而带着问题学习的速度更快)
1.1 具体循环依赖的例子
FROM busybox AS stage0 COPY --from=stage0 f1 /sub/
FROM busybox AS stage0
: 这行代码定义了一个名为stage0
的构建阶段,并使用busybox
作为基础镜像。COPY --from=stage0 f1 /sub/
: 这行代码尝试从名为stage0
的构建阶段复制文件f1
到/sub/
目录。
在这个情况下,在同一个构建阶段中同时定义了一个新的阶段并尝试从该阶段复制文件。这会导致 Docker 无法解析这个依赖关系,因为 stage0
还没有完成构建就被引用了。
2. 报错的位置
源码仓库:GitHub - moby/buildkit: concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit
具体位置:buildkit/frontend/dockerfile/dockerfile2llb/convert.go at master · moby/buildkit · GitHub
2.1 代码快速分析
得益于 Github 支持了 Codespaces 让笔者可以无需代码下载到本地,可以直接基于 Codespaces 对 「convert_test.go」的具体 case 直接做在线 debug ,逐行分析。
注:GitHub Codespaces 是一个基于云的开发环境,允许开发者在浏览器中创建和使用完整的开发环境。它旨在简化开发流程,特别是对于团队协作和快速启动项目。以下是 GitHub Codespaces 的一些关键特性和功能:
代码 debug 效果:
注:感慨一下 Codespaces 真的香!
2.2 代码总结
核心的循环依赖检测逻辑代码如下:
func validateCircularDependency(states []*dispatchState) error { var visit func(*dispatchState, []instructions.Command) []instructions.Command if states == nil { return nil } visited := make(map[*dispatchState]struct{}) path := make(map[*dispatchState]struct{}) visit = func(state *dispatchState, current []instructions.Command) []instructions.Command { _, ok := visited[state] if ok { return nil } visited[state] = struct{}{} path[state] = struct{}{} for dep, c := range state.deps { next := append(current, c) if _, ok := path[dep]; ok { return next } if c := visit(dep, next); c != nil { return c } } delete(path, state) return nil } for _, state := range states { if cmds := visit(state, nil); cmds != nil { err := errors.Errorf("circular dependency detected on stage: %s", state.stageName) for _, c := range cmds { err = parser.WithLocation(err, c.Location()) } return err } } return nil }
核心分析:它使用深度优先搜索(DFS)的方式来检测循环依赖,并在发现循环时返回一个错误。
注:看来不是算法没有用,是业务逻辑的代码中使用 DFS 这种算法的场景比较少,还是得多看源码
2.3 关于 parser 的记录
对于 dockerfile 的 parser 也有点兴趣,后面要继续抽个时间深入分析一下。笔者当前负责的模块重构成一个通用的 parser 的话,代码的复用率会更高一点。希望后面有时间可以优化改进一波
总结
到此这篇关于一次dockerfile的循环依赖错误的文章就介绍到这了,更多相关dockerfile循环依赖错误内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!