十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Velero DeleteItemAction 插件:为备份删除流程扩展清理能力的设计与实现

Velero DeleteItemAction 插件:为备份删除流程扩展清理能力的设计与实现 Velero DeleteItemAction 插件为备份删除流程扩展清理能力的设计与实现【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero导读在 Kubernetes 备份恢复领域Velero 的备份/恢复插件体系BackupItemAction、RestoreItemAction早已为备份与恢复过程提供了自定义扩展点但备份删除这一生命周期环节长期缺乏对应的插件机制当用户删除一个备份时那些由自定义插件创建在 Kubernetes 集群之外如云厂商对象存储、外部服务的资源将无人清理。本篇技术指南以 delete-item-action.md 设计文档为核心结合当前仓库的实际源码实现完整讲解 DeleteItemAction 插件 API 的定义、备份删除流程的改造方案、插件编写与注册方式以及 Velero 内置的 VolumeSnapshotContent 删除插件如何运用该机制帮助读者掌握通过 DeleteItemAction 在备份删除时安全、有序地清理外部资源的完整方案。DeleteItemAction 的设计动机与背景从 CSI 快照支持说起Velero 在引入 Container Storage InterfaceCSI快照支持时新增了通过 BackupItemAction 与 RestoreItemAction 插件来备份和恢复快照的模式。但社区开发者很快发现这套模式存在一个明显的缺口Velero 没有在备份删除阶段暴露任何扩展点导致这些 ItemAction 插件在备份/恢复期间创建的资源尤其是存在于 Kubernetes 集群之外的外部资源在备份被删除时无从清理。设计文档 delete-item-action.md 明确指出了这一痛点这些插件的核心诉求就是删除存在于 Kubernetes 之外的外部资源而当前设计正是要补齐备份删除时无扩展点的缺失环节。设计目标与非目标设计文档给出了清晰的范围界定Goals目标提供可供插件实现的 DeleteItemAction API修改 Velero 备份删除逻辑使其能够调用已注册的 DeleteItemAction 插件。Non Goals非目标不提供 DeleteItemAction API 的具体业务实现测试用例除外不提供 DeleteItemAction 执行的回滚机制。最后一点尤为关键由于备份删除是不可逆流程一旦备份中的卷快照、restic/Kopia 存储库快照或其他 DeleteItemAction 管理的资源已被删除没有任何途径可以恢复它们因此 DeleteItemAction 插件无法回滚其操作。整体架构备份删除流程如何与插件协同高层设计DeleteItemAction 插件 API 的设计与 RestoreItemAction 高度相似插件会接收到正在被删除的 VeleroBackupGo 结构体以及从备份 tarball 中解压出的、与之匹配的 Kubernetes 资源。备份删除流程的改造要点如下若存在任何已注册的 DeleteItemAction 插件则先从备份存储BackupStorageLocation下载备份 tarball 并解压这与现有恢复restore逻辑的处理方式一致遍历备份 tarball 中的每一个条目检查是否有 DeleteItemAction 插件与其匹配若匹配则将Backup对象与该条目一同传给 DeleteItemAction 插件执行。执行顺序与语义保证优先执行DeleteItemAction 插件在备份删除流程中最先运行先于从存储中删除快照、以及从 Kubernetes API Server 删除Restore对象等操作确定顺序多个 DeleteItemAction 插件按照其注册名称的字母数字顺序alphanumeric order执行错误容忍从源码看单个插件的 Execute 失败并不会中断整个删除流程错误会被收集聚合后返回使删除流程失败可重试从而避免插件管理的私有制品如数据移动仓库快照被永久孤立的风险见 delete_item_action_handler.go 中的deleteErrs收集逻辑。与备份删除控制器Deletion Controller的衔接实际实现位于 backup_deletion_controller.go。在processRequest中控制器会先通过插件管理器获取全部 DeleteItemAction 插件actions, err : pluginManager.GetDeleteItemActions()若没有插件len(actions) 0则按原有逻辑继续删除流程若存在一个或多个插件则先通过downloadToTempFile下载备份 tarball下载失败时若属于 tarball 不存在的永久性错误HTTP 404/not found则跳过插件直接继续删除若是瞬时错误限流、鉴权失败、网络问题则记录错误使删除失败以便重试下载成功后构造delete.Context包含 Backup、BackupReader、Actions、DiscoveryHelper 等调用delete.InvokeDeleteActions(deleteCtx)完成插件调用。DeleteItemAction API 详解Go 接口定义设计文档给出了接口原型最终落地的实现位于 pkg/plugin/velero/delete_item_action.go与设计基本一致仅输入结构体名称由设计稿的DeleteItemActionInput演进为DeleteItemActionExecuteInput// DeleteItemAction is an actor that performs an operation on an individual item being restored. type DeleteItemAction interface { // AppliesTo returns information about which resources this action should be invoked for. // A DeleteItemActions Execute function will only be invoked on items that match the returned // selector. A zero-valued ResourceSelector matches all resources. AppliesTo() (ResourceSelector, error) // Execute allows the ItemAction to perform arbitrary logic with the item being deleted. // An error should be returned if there were problems with the deletion process, but the // overall deletion process cannot be stopped. // Returned errors are logged. Execute(input *DeleteItemActionExecuteInput) error }两个方法的职责非常明确AppliesTo()声明该插件适用于哪些资源。Execute只会被那些与返回的选择器匹配的条目触发返回零值ResourceSelector表示匹配所有资源Execute(input)针对正在被删除的条目执行任意清理逻辑。输入结构体定义如下// DeleteItemActionExecuteInput contains the input parameters for the ItemActions Execute function. type DeleteItemActionExecuteInput struct { // Item is the item taken from the pristine backed up version of resource. Item runtime.Unstructured // Backup is the representation of the restore resource processed by Velero. Backup *velerov1api.Backup }其中Item取自备份 tarball 中原始备份版本pristine backed up version的runtime.Unstructured资源即不含任何恢复时修改的原始形态BackupVelero 正在处理并删除的*velerov1api.Backup结构体指针。ResourceSelector 字段AppliesTo()返回的ResourceSelector与 BackupItemAction / RestoreItemAction 共用同一类型支持以下过滤维度字段作用IncludedNamespaces仅匹配列出的命名空间ExcludedNamespaces排除列出的命名空间IncludedResources仅匹配列出的资源如volumesnapshotcontents.snapshot.storage.k8s.ioExcludedResources排除列出的资源LabelSelector通过标签选择器进一步过滤条目gRPC 通信层Protobuf 定义与客户端/服务端DeleteItemAction 插件采用与 BackupItemAction / RestoreItemAction 完全相同的 HashiCorp go-plugin gRPC 通信模型。Protobuf 定义设计文档规划的pkg/plugin/proto/DeleteItemAction.proto已落地见 DeleteItemAction.protomessage DeleteItemActionExecuteRequest { string plugin 1; bytes item 2; bytes backup 3; } message DeleteItemActionAppliesToRequest { string plugin 1; } message DeleteItemActionAppliesToResponse { ResourceSelector ResourceSelector 1; } service DeleteItemAction { rpc AppliesTo(DeleteItemActionAppliesToRequest) returns (DeleteItemActionAppliesToResponse); rpc Execute(DeleteItemActionExecuteRequest) returns (Empty); }注意ExecuteRPC 的返回类型是Empty与设计文档的DeleteItemActionExecuteResponse相比落地实现做了简化——因为Execute没有返回值需要传递错误直接通过 gRPC error 通道返回。服务端实现服务端位于 delete_item_action_server.go。其核心逻辑是通过getImpl(req.Plugin)从ServerMux中按插件名解析出实际的velero.DeleteItemAction实现AppliesTo调用将插件返回的ResourceSelector各字段映射为 proto 消息中的ResourceSelectorExecute调用将请求中的item与backupJSON 字节流分别反序列化为unstructured.Unstructured与api.Backup构造velero.DeleteItemActionExecuteInput后转发给插件实现两个方法都通过common.HandlePanic捕获插件 panic 并转换为 gRPC 错误。客户端实现客户端位于 delete_item_action_client.go实现了velero.DeleteItemAction接口AppliesTo()向 gRPC server 发起调用并还原ResourceSelectorExecute(input)将input.Item.UnstructuredContent()与input.Backup分别 JSON 序列化为字节流构造DeleteItemActionExecuteRequest发送返回的响应是空结构体实际错误通过 gRPC error 传递。插件框架的接线位于 server.go// RegisterDeleteItemAction registers a delete item action. Accepted format RegisterDeleteItemAction(pluginName string, initializer common.HandlerInitializer) Server RegisterDeleteItemActions(map[string]common.HandlerInitializer) Server可重启插件进程restartableDeleteItemAction与 RestoreItemAction / BackupItemAction 一样DeleteItemAction 也实现了可重启restartable的进程包装代码位于 restartable_delete_item_action.go// restartableDeleteItemAction is a delete item action for a given implementation (such as pod). It is associated with // a restartableProcess, which may be shared and used to run multiple plugins. At the beginning of each method // call, the restartableDeleteItemAction asks its restartableProcess to restart itself if needed (e.g. if the // process terminated for any reason), then it proceeds with the actual call. type restartableDeleteItemAction struct { key process.KindAndName sharedPluginProcess process.RestartableProcess } func NewRestartableDeleteItemAction(name string, sharedPluginProcess process.RestartableProcess) *restartableDeleteItemAction其方法语义如下方法行为getDeleteItemAction()返回当前插件进程中的 DeleteItemAction 实现不触发进程重启getDelegate()先通过ResetIfNeeded()在必要时重启插件进程再返回插件实现AppliesTo()/Execute()均先经getDelegate()确保进程健康再委托给实际实现这种设计保证了当插件进程因任何原因崩溃后后续调用能够自动拉起新进程继续工作。插件的插件种类标识PluginKindDeleteItemAction字符串值为DeleteItemAction定义于 plugin_kinds.go。插件管理器扩展设计文档规划的Manager接口扩展已落地于 manager.go// GetDeleteItemActions returns all delete item action plugins. GetDeleteItemActions() ([]velero.DeleteItemAction, error) // GetDeleteItemAction returns the delete item action plugin for name. GetDeleteItemAction(name string) (velero.DeleteItemAction, error)其实现要点包括GetDeleteItemActions遍历注册表中所有PluginKindDeleteItemAction种类的插件逐个通过GetDeleteItemAction(id.Name)获取并聚合GetDeleteItemAction按名称获取restartableProcess并包装为restartableDeleteItemAction与其它插件类型一致二者对velero.io/前缀的命名空间插件有相同的例外处理逻辑源码注释提及DeleteItemActions 引入时插件体系已经是命名空间化的因此旧的非命名空间插件中不包含 DeleteItemAction。备份 tarball 解析与插件分发InvokeDeleteActions设计文档描述的核心流程——下载 tarball、解压、逐条匹配并调用插件——在 delete_item_action_handler.go 中完整实现func InvokeDeleteActions(ctx *Context) error其中Context提供了插件执行所需的全部环境type Context struct { Backup *velerov1api.Backup BackupReader io.Reader Actions []velero.DeleteItemAction Filesystem filesystem.Interface Log logrus.FieldLogger DiscoveryHelper discovery.Helper resolvedActions []framework.DeleteItemResolvedAction }整体执行流程分五个阶段解析动作通过framework.NewDeleteItemActionResolver(ctx.Actions)构造解析器调用ResolveActions(helper, log)借助 discovery helper 将每个插件的AppliesTo()声明解析为DeleteItemResolvedAction内部组合了resolvedAction包含资源 Include/Exclude、命名空间 Include/Exclude 与标签选择器若没有任何已解析动作且无错误直接返回 nil按原流程继续删除解压 tarball通过archive.NewExtractor(...).UnzipAndExtractBackup(ctx.BackupReader)将备份解压到临时目录defer中清理临时目录解析备份内容通过archive.NewParser(...).Parse(dir)解析出backupResources按 group/resource → namespace → items 组织若备份中无资源目录archive.ErrNotExist则忽略插件直接返回逐资源分发外层遍历每个 groupResource已处理过的跳过内层按命名空间分组先通过ctx.getApplicableActions(groupResource, namespace)一次性过滤出适用于该组/资源与命名空间的动作列表再遍历每个条目通过archive.GetItemFilePath与archive.Unmarshal从 tarball 恢复出unstructured.Unstructured对象对每个动作先检查action.Selector.Matches(labels.Set(obj.GetLabels()))标签选择器匹配才触发匹配后调用action.DeleteItemAction.Execute(...)聚合错误单个插件执行失败不会终止循环保证剩余条目的清理不被阻塞但错误会被收集进deleteErrs最终通过kubeerrs.NewAggregate(deleteErrs)返回——若返回非 nil 错误控制器会让删除失败以便后续重试避免插件管理的私有制品被永久孤立。编写并注册一个 DeleteItemAction 插件注册 API插件进程内通过 framework server 注册见 server.gos.RegisterDeleteItemAction(example.io/delete-item-action, newDeleteItemAction)或批量注册s.RegisterDeleteItemActions(map[string]common.HandlerInitializer{ example.io/foo: fooInitializer, example.io/bar: barInitializer, })仓库中的示例examples_test.go展示了完整的实现骨架func newDeleteItemAction(logger logrus.FieldLogger) (any, error) { return DeleteItemAction{FieldLogger: logger}, nil } type DeleteItemAction struct { FieldLogger logrus.FieldLogger } func (d *DeleteItemAction) AppliesTo() (velero.ResourceSelector, error) { d.FieldLogger.Infof(AppliesTo called) // ... return velero.ResourceSelector{}, nil } func (d *DeleteItemAction) Execute(input *velero.DeleteItemActionExecuteInput) error { d.FieldLogger.Infof(Execute called) // ... return nil }实战编写要点基于源码语义编写插件时应遵循以下要点AppliesTo尽量收窄只声明本插件负责的资源类型与命名空间范围避免零值选择器匹配全部带来的不必要开销与误清理风险在Execute中做幂等与防御性判断由于删除流程可能因瞬时错误重试Execute应具备幂等性。可参考内置插件通过input.Backup.Name与资源标签判断该资源是否由本次备份创建的方式见下文内置插件善用input.Item与input.Backupinput.Item是备份时的原始资源形态input.Backup提供备份的名称、命名空间、标签等信息可用于关联判断错误处理Execute返回错误会导致整个删除流程失败可重试因此要区分永久性错误返回错误与无需处理的正常跳过返回 nil。内置参考实现VolumeSnapshotContent 删除插件设计文档明确指出Specific implementations of the DeleteItemAction API beyond test cases不在目标范围内但当前仓库的 CSI 删除动作已内置了一个真实的生产级实现——volumesnapshotcontent_action.go 中的volumeSnapshotContentDeleteItemAction。它是理解 DeleteItemAction 最佳实践的绝佳范本。AppliesTo 声明func (p *volumeSnapshotContentDeleteItemAction) AppliesTo() (velero.ResourceSelector, error) { return velero.ResourceSelector{ IncludedResources: []string{volumesnapshotcontents.snapshot.storage.k8s.io}, }, nil }该插件只处理volumesnapshotcontents.snapshot.storage.k8s.io资源。Execute 的防御性逻辑Execute内部体现了前面提到的关键实践将input.Item.UnstructuredContent()通过runtime.DefaultUnstructuredConverter.FromUnstructured转换为强类型的snapshotv1api.VolumeSnapshotContent关键防御通过kubeutil.HasBackupLabel(snapCont.ObjectMeta, input.Backup.Name)检查 VolumeSnapshotContent 是否带有本次备份名称的标签——若没有说明该快照并非由 Velero 创建直接跳过删除避免误删用户手工创建的快照if !kubeutil.HasBackupLabel(snapCont.ObjectMeta, input.Backup.Name) { p.log.Infof( VolumeSnapshotContent %s was not taken by backup %s, skipping deletion, snapCont.Name, input.Backup.Name, ) return nil }兼容性处理优先尝试从集群删除原始 VSC兼容 1.15 之前DeletionPolicyRetain的旧备份遗留场景若原始 VSC 已不在集群中则创建一个临时 VSCvsc-uuid并将DeletionPolicy置为VolumeSnapshotContentDelete以触发云侧底层快照的删除删除后再清理临时 VSC。其单元测试 volumesnapshotcontent_action_test.go 覆盖了AppliesTo、Execute的各类分支以及创建-睡眠-删除的时序场景可作为编写与验证自定义 DeleteItemAction 插件的测试范式参考。备选方案回顾为什么不是别的方式设计文档对曾考虑过的备选方案做了记录更高级别的 DeleteItemActions 提案最初曾考虑过更高层的 DeleteItemAction需要实现者自行下载备份 tarball。虽然长期看可能有用但每个插件都要重复实现大量样板代码与当前目标不匹配该备选方案详见 deletion-plugins.mdVolumeSnapshotter 接口该接口专门用于对块设备拍摄快照通用性不足以满足本需求。安全性考量DeleteItemAction插件的本质是删除数据这通常属于安全敏感操作。设计文档给出的安全论证是这类插件只会在两种场景下被触发——一是用户通过veleroCLI 或其他管理系统发起BackupDeleteRequest二是 VeleroBackup因超过 TTL 而过期被自动清理。由于触发路径都是受控的管理操作数据删除风险是可控的。兼容性向后兼容性方面该设计对大多数升级中的 Velero 安装是兼容的若未安装任何 DeleteItemAction 插件备份删除流程与引入插件之前完全一致。这一保证在实现层面得到双重印证——控制器中len(actions) 0时直接走原流程InvokeDeleteActions中无已解析动作且无错误时直接返回 nil。实现顺序与扩展建议设计文档建议的实现依赖顺序与 Detailed Design 章节的叙述顺序一致落地实现也遵循了这条路径API 类型pkg/plugin/velero→ Protobuf 与 gRPC 客户端/服务端pkg/plugin/proto、pkg/plugin/framework→ 可重启包装pkg/plugin/clientmgmt→ 插件管理器manager.go→ 删除控制器改造pkg/controller→ tarball 解析与分发internal/delete。对于希望为 Velero 补充外部资源清理能力的开发者推荐按以下路径深入当前仓库阅读 delete-item-action.md 设计文档掌握全貌阅读 delete_item_action.go 与 DeleteItemAction.proto 理解 API 契约对照 delete_item_action_handler.go 理解分发语义参照 volumesnapshotcontent_action.go 编写首个插件实现与测试。【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表