菜单

文件树系统

相关源文件

文件系统树是 Dive 的核心组件,它在容器镜像的每个层级中提供文件系统的内存表示。该系统使 Dive 能够高效地分析层级变更、计算存储效率指标,并向用户呈现可导航的文件层级结构。本文档描述了构成文件系统树的数据结构、算法和操作。

有关此系统如何集成到更广泛的镜像分析过程的信息,请参阅 镜像分析过程

1. 核心数据结构

文件系统树围绕三个主要数据结构构建,它们协同工作以表示镜像层的文件系统层级结构。

来源: dive/filetree/file_tree.go24-43 dive/filetree/file_node.go26-34 dive/filetree/file_info.go12-23

1.1 FileTree

The FileTree 结构表示容器镜像层的完整文件系统层级结构。它维护着根节点的引用,跟踪总树大小(节点数量),并提供用于树操作和遍历的方法。

关键属性

  • Root: 指向树根节点的引用
  • Size: 树中的节点数量(不包括根节点)
  • FileSize: 树中所有文件的大小总和
  • Name: 树的标识符(通常是层 ID)
  • Id: 树实例的 UUID
  • SortOrder: 定义树遍历的排序策略

来源: dive/filetree/file_tree.go24-32

1.2 FileNode

The FileNode 结构表示树中的单个文件或目录。每个节点可以有多个子节点(用于目录),并包含它所表示文件的元数据。

关键属性

  • Tree: 指向包含树的引用
  • Parent: 指向父节点的引用
  • Size: 此节点及其所有子节点的大小总和(懒加载)
  • Name: 文件或目录的名称
  • Data: 包含文件元数据和视图信息
  • Children: 按名称索引的子节点映射
  • path: 到此节点的缓存绝对路径(按需计算)

来源: dive/filetree/file_node.go26-34

1.3 FileInfo

The FileInfo 结构包含特定文件或目录的元数据,类似于从文件系统或 tar 存档中获取的信息。

关键属性

  • Path: 文件路径
  • TypeFlag: 文件类型(类似于 tar 头类型)
  • Linkname: 符号链接的目标
  • hash: 内容哈希(用于高效文件比较)
  • Size: 文件大小(字节)
  • Mode: 文件权限
  • UidGid: 所有者和组 ID
  • IsDir: 节点是否为目录

来源: dive/filetree/file_info.go12-23

2. 树操作

文件系统树支持多项关键操作,这些操作使得分析 Docker 镜像层成为可能。

2.1 树的构建

文件系统树通过添加路径来逐步构建。系统在添加路径时根据需要创建中间节点。

来源: dive/filetree/file_tree.go244-282

The AddPath 方法为路径中的每个段创建一个节点,并将提供的 FileInfo 附加到最终节点。该方法返回创建的节点和所有新创建的节点列表。

2.2 树遍历

文件系统树支持两种遍历方法:

  1. 深度优先,子节点优先(后序遍历):先访问最深的节点,然后向上工作。
  2. 深度优先,父节点优先(前序遍历):先访问最浅的节点,然后向下工作。

来源: dive/filetree/file_tree.go195-205 dive/filetree/file_node.go209-262

遍历可以通过以下方式进行定制:

  • Visitor: 为每个访问的节点调用的函数
  • VisitEvaluator: 决定节点是否应被访问的函数
  • OrderStrategy: 遍历期间子节点排序的策略(按名称、按大小等)

2.3 树的比较

文件系统树可以比较两棵树,以识别已添加、已修改和已删除的文件。这就是 Dive 显示层级之间变更的方式。

来源: dive/filetree/file_tree.go300-365 dive/filetree/file_node.go333-354

比较过程识别出:

  • 已添加文件:上层树中不存在于下层树的文件
  • 已移除文件:在上层树中带有 whiteout 前缀的文件
  • 已修改文件:存在于两棵树中但内容或元数据不同的文件

2.4 树的堆叠

文件系统树可以“堆叠”起来,以表示层层叠加的构建过程。这是分析由多个层组成的 Docker 镜像的核心功能。

来源: dive/filetree/file_tree.go208-225 dive/filetree/file_tree.go376-390

堆叠过程:

  1. 遍历上层树中的每个节点
  2. 对于 whiteout 文件,从下层树中删除相应路径
  3. 对于非 whiteout 文件,在下层树中添加或更新路径
  4. 返回一个修改后的下层树,表示组合后的文件系统

3. 层分析

文件系统树用于分析 Docker 镜像层的效率和浪费的空间。

3.1 Diff 类型

该系统使用四个 DiffType 值来跟踪层级之间的变更。

  • Added: 当前层中添加的文件
  • Removed: 当前层中移除的文件
  • Modified: 当前层中已更改的文件
  • Unmodified: 与上一层相比未更改的文件

这些 diff 类型对于向用户显示文件系统如何在层级之间发生变化并识别效率低下至关重要。

来源: dive/filetree/file_node.go18-23 dive/filetree/file_node.go298-354

3.2 效率计算

文件系统树通过识别跨层重复或在后续层中移除的文件来计算效率。

来源: dive/filetree/efficiency.go35-131

效率计算:

  1. 跨所有层跟踪每个文件
  2. 识别出现在多个层中的文件(重复文件)
  3. 识别在后续层中移除的文件(浪费)
  4. 计算效率得分,作为最少所需空间与实际使用空间的比例
  5. 返回得分和低效文件模式列表(重复或浪费的文件)

4. 与镜像分析的集成

文件系统树通过 Layer 结构与镜像分析过程集成。

来源: dive/image/layer.go16-24 dive/image/image.go7-11

镜像中的每个 Layer 都包含一个 FileTree 的引用,该 FileTree 表示该层的文件系统。在分析过程中:

  1. Image Analyzer 提取层数据并创建 FileTree 实例
  2. 每个 FileTree 都被单独分析,并与其他层进行关联分析
  3. UI 使用这些 FileTree 实例来显示层内容和变更
  4. 效率计算跨所有 FileTree 实例执行

Layer 结构充当了从 Docker/Podman/Archive 的原始层数据到文件系统树系统的桥梁,使得 Dive 能够提供对镜像结构的详细分析。

5. 使用示例

为了说明文件系统树在实践中如何使用,以下是一个 Dive 分析镜像层的示例:

  1. 从容器镜像(tarball)中提取层数据
  2. 通过添加每个文件路径及其元数据来构建 FileTree
  3. 对于每个附加的层:
    • 为该层构建 FileTree
    • 将新的 FileTree 堆叠到上一层的 FileTree 之上
    • 使用适当的 DiffType 值(Added, Removed, Modified)标记节点
  4. 跨所有层计算效率指标
  5. 在 UI 中呈现结果树以供探索

此过程使 Dive 能够向用户展示每个层如何贡献到最终镜像,识别效率低下之处,并提供容器文件系统结构的详细可见性。