记一次 dockerfile 的循环依赖错误

文章目录

    • [1. 写在最前面](#1. 写在最前面)
      • [1.1 具体循环依赖的例子](#1.1 具体循环依赖的例子)
    • [2. 报错的位置](#2. 报错的位置)
      • [2.1 代码快速分析](#2.1 代码快速分析)
      • [2.2 代码总结](#2.2 代码总结)
      • [2.3 关于 parser 的记录](#2.3 关于 parser 的记录)
    • [3. 碎碎念](#3. 碎碎念)

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 代码总结

核心的循环依赖检测逻辑代码如下:

go 复制代码
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 的话,代码的复用率会更高一点。希望后面有时间可以优化改进一波

3. 碎碎念

抓住 2024 的尾巴,努力学习感兴趣的知识。希望 2025 平安喜乐,万事胜意!

  • 最好的选择是,做自己的太阳

  • 幸福的秘诀是,拥有苹果时,只在意苹果,不去管橘子,更不要想橙子的事情。

  • 终于明白朝花夕拾什么意思了,你一生追求的东西其实一开始就在,只是你后知后觉而已,人无法同时拥有青春和对于青春的感受,有些东西要靠消失才能证明它的珍贵。

相关推荐
山林竹笋1 小时前
Docker入门常用命令总结
docker·云原生
学Linux的语莫5 小时前
docker基本概念,docker镜像管理,docker命令
docker
迷茫运维路6 小时前
graylog+sidecar通过docker-compose部署并采集SSH登录日志
运维·docker·ssh·graylog·pipelines
程序猿零零漆6 小时前
SpringCloud系列教程:微服务的未来(六)docker教程快速入门、常用命令
spring cloud·docker·微服务
cv小白菜6 小时前
容器化部署算法服务技术文档
python·ubuntu·docker
花姐夫Jun8 小时前
基于CentOS的Docker + Nginx + Gitee + Jenkins部署总结
nginx·docker·centos·jenkins
shelby_loo8 小时前
在Ubuntu下通过Docker部署MySQL服务器
服务器·ubuntu·docker
檀越剑指大厂9 小时前
【fnOS使用Docker快速搭建WordPress站点并免费配置公网地址】
运维·docker·容器
u01005596010 小时前
sudo mkdir -p /etc/docker其中的 -p 什么意思?
运维·docker·容器
嗑瓜子儿溜茶水儿16 小时前
docker 部署 NginX
nginx·docker·容器