尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Velero 对象图谱清单(Object Graph Manifest)设计解析:为 dry-run、并发备份恢复与图依赖恢复奠定基础

Velero 对象图谱清单(Object Graph Manifest)设计解析:为 dry-run、并发备份恢复与图依赖恢复奠定基础 Velero 对象图谱清单Object Graph Manifest设计解析为 dry-run、并发备份恢复与图依赖恢复奠定基础【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero导读本文以 design/graph-manifest.md 设计提案为骨架深入解析 Velero 提出的Object Graph Manifest对象图谱清单数据结构一个随备份一起存放于对象存储、以最小字段描述备份内容全貌的manifest.json。读完本文你将理解 Velero 现有备份/恢复在内容感知上的局限掌握清单数据结构的 Go 类型设计、序列化与兼容性策略并看到它如何为 dry-run、无重叠并行操作以及基于依赖图的复杂应用恢复铺平道路。背景为什么 Velero 需要一份备份内容清单Velero 当前的工作方式是备份时逐个处理对象按 API Group 与命名空间排序恢复时同样逐个处理对象借助restoreResourcePriorities标志指定各 API Group 的对象恢复先后顺序。这种线性处理模式对大多数应用足够但对依赖关系呈图状而非严格线性的复杂应用存在明显挑战。文档以 Cluster APICAPI为例CAPI 集群是一组复杂的 Kubernetes 对象要求根对象先于叶子对象恢复——如果ClusterResourceSetBinding所引用的Cluster尚不存在整个 CAPI 集群的恢复就会失败。此外Velero 缺少一种**在不真正执行操作的前提下可靠告知用户哪些对象会受影响**的机制。这会带来两个连锁问题dry-run 困难用户必须真实执行备份/恢复动作才能知道会触碰哪些对象并发受限当前无法判断某个 Kubernetes 对象是否同时被多个备份或恢复覆盖。一旦 Velero 走向更高并发就可能导致不可靠、死锁乃至竞态条件。目标与非目标目标Goals引入一个定义备份内容的数据结构Manifest将 Manifest 数据与既有备份数据一并存入对象存储。非目标Non Goals本提案明确说明以下能力是被开启而非被定义的不会与数据结构的引入同期实现在 Velero 现有并发之外实现更高并发实现 dry-run 特性实现新的恢复排序流程。数据结构设计必须为上述场景留出空间但它们不属于本提案的交付范围。高层设计用最小字段唯一标识一个 Kubernetes 对象要在一个集群或备份内唯一标识 Kubernetes 对象以下字段已经足够见 design/graph-manifest.mdAPI Group 与 Version例如backup.velero.io/v1NamespaceNameLabels这组标准覆盖了 Velero 绝大部分的包含/排除inclusion/exclusion逻辑。此外以下附加字段可进一步解锁更多用例Owners与该对象存在某种关系的其他 Kubernetes 对象可能是严格依赖也可能是软依赖Annotations对象额外的元数据方便其他程序消费UUIDKubernetes 生成的唯一标识用于定义 Owner 关系为对象提供单一且不可变的主键。注意UUID在恢复时不会被考虑仅内部用于定义对象间链接。为什么不直接解析备份 tarball上述信息其实都已存在于 Velero 备份 tarball 中但提取成本高昂必须完整下载并解压整个 tarball再解析其中的 JSON 才能读到 labels、owners、annotations 和 UUID其余信息则编码在 tarball 的目录结构里。可行但重量级耗时且可能消耗大量内存。从仓库源码可以印证这种结构的实际形态。备份 tarball 内的对象文件路径由 pkg/archive/filesystem.go 的GetVersionedItemFilePath生成path : filepath.Join(rootDir, velerov1api.ResourcesDir, groupResource, versionPath, GetScopeDir(namespace), namespace, name.json)即形如root/resources/groupResource/version/cluster 或 namespaces/namespace/name.json命名空间级对象与root/resources/groupResource/version/cluster/name.json集群级对象。对应测试见 pkg/archive/filesystem_test.go。因此提案主张新增一种与备份 tarball 并排存放的 Manifest 结构它只包含上述字段可用来对备份执行包含/排除逻辑从备份中选择特定资源对备份或恢复内容做集合运算识别重叠资源。清单将解锁的三类场景备份/恢复的 dry-run先构建并保存一份 Manifest让用户看到如果执行会选中什么确认后再真正执行备份恢复操作同理。无重叠的高效并行在执行备份/恢复前先构建或读取 Manifest判断资源是否存在重叠——无重叠则并行执行有重叠则串行执行。面向非线性依赖的图式恢复Kubernetes 集群中并非所有资源都能用严格线性方式定义它们可能有多个 Owner。与其要求插件作者在BackupItemAction/RestoreItemAction里手工返回一长串 Owner 链不如由 Velero 基于包含足够信息的 Manifest自动构建依赖先于被依赖者恢复的离散列表从而大幅降低插件作者的负担。详细设计Go 数据结构与接口核心类型定义Manifest 数据结构的 Go 类型设计如下原文完整摘录自 design/graph-manifest.md// NamespacedItems maps a given namespace to all of its contained items. type NamespacedItems map[string]*Item // APIGroupNamespaces maps an API group/version to a map of namespaces and their items. type KindNamespaces map[string]NamespacedItems type Manifest struct { // Kinds holds the top level map of all resources in a manifest. Kinds KindNamespaces // Index is used to look up an individual item quickly based on UUID. // This enables fetching owners out of the maps more efficiently at the cost of memory space. Index map[string]*Item } // Item represents a Kubernetes resource within a backup based on its selectable criteria. // It is not the whole Kubernetes resource as retrieved from the API server, but rather a collection of important fields needed for filtering. type Item struct { // Kubernetes API group which this Item belongs to. // Could be a core resource, or a CustomResourceDefinition. APIGroup string // Version of the APIGroup that the Item belongs to. APIVersion string // Kubernetes namespace which contains this item. // Empty string for cluster-level resource. Namespace string // Items given name. Name string // Map of labels that the Item had at backup time. Labels map[string]string // Map of annotations that the Item had at Backup time. // Useful for plugins that may decide to process only Items with specific annotations. Annotations map[string]string // Owners is a list of UUIDs to other items that own or refer to this item. Owners []string // Manifest is a pointer to the Manifest in which this object is contained. // Useful for getting access to things like the Manifest.Index map. Manifest *Manifest }结构要点Manifest.Kinds采用三级映射Kind → Namespace → Items天然对齐 Velero 按 API Group 与命名空间组织的处理模型Manifest.Index是以 UUID 为键的索引表用于快速定位某个 Item从而高效解析 Owner 关系——以内存换取查询效率Item刻意不保存完整的 Kubernetes 资源对象只保存过滤所需的关键字段这正是内存开销可控的根本原因Item.Owners存的是其他 Item 的 UUID 列表配合Index即可在清单内完成图遍历。便捷接口与哨兵错误除数据类型外提案还提供以下 Go 接口type Itermer interface { // Returns the Item as a string, following the current Velero backup version 1.1.0 tarball structure format. // APIGroup/Namespace/APIVersion/name.json String() string // Owners returns a slice of realized Items that own or refer to the current Item. // Useful for building out a full graph of Items to restore. // Will use the UUIDs in Item.Owners to look up the owner Items in the Manifest. Owners() []*Item // Kind returns the Kind of an object, which is a combination of the APIGroup and APIVersion. // Useful for verifying the needed CustomResourceDefinition exists before actually restoring this Item. Kind() *Item // Children returns a slice of all Items that refer to this item as an Owner. Children() []*Items } // This error type is being created in order to make reliable sentinel errors. // See https://dave.cheney.net/2019/06/10/constant-time for more details. type ManifestError string func (e ManifestError) Error() string { return string(e) } const ItemAlreadyExists ManifestError(item already exists in manifest) type Manifester interface { // Set returns the entire list of resources as a set of strings (using Itemer.String). // This is useful for comparing two manifests and determining if they have any overlapping resources. // In the future, when implementing concurrent operations, this can be used as a sanity check to ensure resources arent being backed up or restored by two operations at once. Set() sets.String // Adds an item to the appropriate APIGroup and Namespace within a Manifest // Returns (true, nil) if the Item is successfully added to the Manifest, // Returns (false, ItemAlreadyExists) if the Item is already in the Manifest. Add(*Item) (bool, error) }接口设计的意图值得展开String()将 Item 序列化为符合备份 tarball 目录约定的字符串APIGroup/Namespace/APIVersion/name.json。这与上述 pkg/archive/filesystem.go 中的 tarball 路径规则相呼应只是 Manifest 用更轻量的方式表达了同一份位置信息Owners()/Children()借助Item.OwnersUUID 列表与Manifest.Index在清单内正向/反向遍历依赖图是构建依赖优先恢复顺序的直接入口Kind()返回 APIGroup APIVersion 的组合可用于在真正恢复某个 Item 前校验所需 CRD 是否存在Manifester.Set()把整份清单变成字符串集合用于比较两份 Manifest 的重叠情况——这正是未来并发操作前的健全性检查sanity check原语Add()幂等地向 Manifest 写入 Item重复写入时返回哨兵错误ItemAlreadyExists。文档特意将错误定义为ManifestError字符串类型借鉴 Go 社区常量时间错误constant-time sentinel error实践使调用方可以用errors.Is/做可靠判断。序列化manifest.json 的存储形态整个Manifest会被序列化到单个备份对应的manifest.json文件中存放于对象存储内文档同时指出该文件可考虑压缩以节省空间。从当前仓库的持久化层可以直观看到这种备份目录下并列多个附属文件的既有模式。pkg/persistence/object_store_layout.go 定义了备份目录下的各类产物 key例如velero-backup.json备份元数据backup.tar.gz备份内容 tarballbackup-logs.gz日志backup-resource-list.json.gz资源清单backup-podvolumebackups.json.gz、backup-volumesnapshots.json.gz、backup-itemoperations.json.gz等而 pkg/persistence/object_store.go 中的BackupInfo与PutBackup方法见 object_store.go则把这些io.Reader逐个上传到对象存储。manifest.json正是要作为这一类新增附属文件加入上传流程即文档Implementation一节所述在persistence包中确保新文件可上传且被允许uploadable and allowed。内存考量由于Manifest只保存最小字段集合而非完整对象对绝大多数集群而言内存不应成为顾虑。文档在此留有一个 TODO记录 API Group 名称、资源名称与 Kind 名称的已知字符长度限制作为后续明确的上限依据。安全与兼容性安全Security Considerations引入 Manifest不会扩大 Velero 的攻击面清单中的字段本就存在于既有备份数据中将manifest.json放在既有备份数据旁也不会改变访问模式仍受同样的对象存储凭证与 BSL 配置约束。兼容性Compatibilitymanifest.json的引入对应Velero 备份版本 1.2.0即备份格式版本随之提升该文件是**纯增量additive**的不会干扰不支持Manifest的旧版 Velero 读取备份长期看manifest.json将取代backup-resource-list.json.gz文件但为兼容起见两者会并存一段时期正如 object_store_layout.go 所展示的resource-listkey 仍然存在首次落地时Velero 只需在备份 Item 的过程中顺带构建 Manifest并在备份结束时序列化保存。任何依赖Manifest的逻辑变更都必须以独立的设计文档另行提出并自带各自的兼容性考量。实现路径从类型到上传的落地点文档明确了实现方向design/graph-manifest.mdManifest不会实现为 Kubernetes CustomResourceDefinition而是 Velero 的内部构造internal construct数据结构的实现量应尽量小在独立的manifest包中定义上述类型备份流程创建Manifest并将其传递给backup包中的各个*Backupper由这些方法逐个插入Item在persistence包中新增逻辑确保新的manifest.json可被上传且被允许。从源码结构看pkg/backup包中的 item_backupper.go、backed_up_items_map.go、backup.go 正是文档所描述的各种 Backupper所在的实现位置它们逐项采集对象并维护去重映射天然适合在采集过程中同步向 Manifest 插入 Item。持久化侧则以 pkg/persistence/object_store.go 的PutBackup为参照为manifest.json增加对应的 key 生成与上传逻辑。开放问题与后续展望文档以三个开放问题收尾为社区后续讨论留白何时正式移除对backup-resource-list.json.gz的兼容除 Cluster API 外还有哪些适合验证该特性的测试用 Kubernetes 资源/控制器Cluster API 是显而易见的选择但显然不止于此既然 Manifest 不是 CRD如何保留一份 Manifest 以便用户先执行 dry-run 再执行真实操作一个思路是存放在 Velero 的临时目录中——但需要指出这会让 Velero 自身变得更有状态stateful。总结Object Graph Manifest 是 Velero 在备份内容可感知性上的关键设计增量它以一份极轻量的manifest.json随备份存入对象存储用最小的字段集合完整描述备份内容与对象间依赖关系。它本身不直接交付 dry-run、并发或新恢复排序但通过Index、Owners、Set()等精心设计的原语为这些更高阶的能力提供了坚实的结构性基础——这也是本文将其称为 Velero 后续架构演进地基的原因。对这一提案感兴趣的读者可继续阅读原始设计文档 design/graph-manifest.md并结合 pkg/persistence/object_store_layout.go 与 pkg/archive/filesystem.go 理解其落地的存储与归档上下文。【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表