资讯动态

Velero 删除插件(Deletion Plugins / DeleteItemAction)设计详解与源码实现

发布时间:2026/9/15 22:05:06 来源:尧图企业网站定制
Velero 删除插件Deletion Plugins / DeleteItemAction设计详解与源码实现【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero导读本篇文章围绕 Velero 的删除插件机制展开核心解决一个现实痛点当备份Backup被删除时由备份插件在备份/恢复过程中创建的外部关联资源如云厂商快照、自定义资源等可能被遗留为孤儿资源。文章先完整还原官方设计文档design/Implemented/deletion-plugins.md的提案思路再结合当前仓库中已经落地的DeleteItemAction实现从插件接口、gRPC 框架、备份删除控制器调用链到两个内置实现DataUpload 删除、CSI VolumeSnapshotContent 删除进行源码级剖析。读完本文你将掌握 Velero 删除插件的设计动机、接口契约、注册方式与执行流程并具备自行编写第三方删除插件的基础能力。背景为什么需要一种删除插件Velero 的插件体系此前已经拥有两类核心插件BackupItemActionBIA在备份单个资源时执行可创建额外的资源来完成备份RestoreItemActionRIA在恢复单个资源时执行负责还原备份项。以 CSI 插件为例Velero 开发者正是借助 BIA/RIA 插件与 PersistentVolumeClaim 绑定来创建 VolumeSnapshot、VolumeSnapshotContent 等辅助资源从而完成卷备份。对于这些辅助资源当前是在 Velero 核心服务器内部直接做清理即删除这些 Kubernetes 自定义资源。但这种核心内清理模式对外部插件并不实用原因有二见 design/Implemented/deletion-plugins.mdVelero 核心无法为所有可能出现的自定义资源都编写清理逻辑插件创建的外部资源并不一定都是 Kubernetes 自定义资源它们可能存在于任意外部系统中如对象存储中的快照、第三方服务的资源实例。因此设计文档得出结论Velero 需要一种机制让在 BIA/RIA 插件中创建过资源的插件作者能够保证这些资源在备份被删除时得到清理——无论这些资源位于哪个系统。设计目标与非目标Goals目标在 Velero 中提供一种新的插件类型当备份被删除时被调用。Non Goals非目标不实现具体的删除插件即核心仓库不负责为每种外部资源编写清理逻辑不支持删除插件执行的回滚rollback。这两个非目标非常关键它界定了 Velero 核心只负责提供机制mechanism而把具体资源的清理逻辑交给插件作者同时明确删除操作是不可撤销的这与后面插件不能阻止删除的设计约束一脉相承。高层设计DeleteAction 插件的核心约定设计文档为这类新插件命名为DeleteAction并给出如下高层约定调用时机当备份被删除时执行输入插件接收正在被删除的Backup自定义资源不能阻止删除由于多个DeleteAction插件可以同时注册且方案不包含回滚/撤销如果某些插件已经执行、而另一个插件要求停止删除那么保留下来的备份会处于不一致状态。因此插件只负责清理无权中止删除流程按标签匹配AppliesToDeleteAction基于Backup自身的标签决定是否生效。插件可以注册一个AppliesTo函数它定义了一个作用于 Velero 备份的标签选择器从而避免执行与自身无关的删除插件执行顺序DeleteAction按插件名称的字母顺序执行。该顺序虽然有些任意但能为插件作者和用户提供相对可预测的事件顺序。详细设计Go 接口提案设计文档给出了DeleteAction插件的 Go 接口草案定义于pkg/plugin/velero/deletion_action.gotype DeleteAction struct { // AppliesTo will match the DeleteAction plugin against Velero Backups that it should operate against. AppliesTo() // Execute runs the custom plugin logic and may connect to external services. Execute(backup *api.backup) error }同时文档提议在clientmgmt.Manager接口即 pkg/plugin/clientmgmt/manager.go中新增方法type Manager interface { ... // GetDeleteActions returns the registered DeleteActions. //TODO: do we need to get these by name, or can we get them all? GetDeleteActions([]velero.DeleteAction, error) ...需要指出的是设计文档此处标注为Status: Alternative Proposal备选提案接口为草案形态。在仓库中落地时接口演化为下文介绍的DeleteItemAction。从提案到落地当前仓库中的 DeleteItemAction 接口设计文档的DeleteAction最终在仓库中实现为DeleteItemAction定义在 pkg/plugin/velero/delete_item_action.go// 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 }与提案相比落地版本有以下关键演变名称DeleteAction→DeleteItemAction语义上强调针对备份中的每个资源项item执行AppliesTo 返回值从无返回值变为(ResourceSelector, error)。ResourceSelector定义在 pkg/plugin/velero/shared.go包含IncludedNamespaces、ExcludedNamespaces、IncludedResources、ExcludedResources与LabelSelector等字段用于精细控制插件作用于哪些资源零值空选择器表示匹配所有资源Execute 入参从只接收Backup变为接收结构体DeleteItemActionExecuteInput见 pkg/plugin/velero/delete_item_action.gotype 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 }也就是说实际执行时插件不仅能拿到正在被删除的Backup还能拿到备份归档中的每一个资源对象Unstructured这正是设计文档中确保这些资源被删除诉求的具体落点插件从备份包中逐个取出由自己创建的资源执行外部清理。Manager 接口中的落地实现设计文档提议的GetDeleteActions在 pkg/plugin/clientmgmt/manager.go 中以GetDeleteItemActions实现// GetDeleteItemActions returns all delete item actions as restartableDeleteItemActions. func (m *manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { list : m.registry.List(common.PluginKindDeleteItemAction) actions : make([]velero.DeleteItemAction, 0, len(list)) for i : range list { id : list[i] r, err : m.GetDeleteItemAction(id.Name) if err ! nil { return nil, err } actions append(actions, r) } return actions, nil }该方法从插件注册表registry中按PluginKindDeleteItemAction类型列出全部已注册插件插件类型常量定义在 pkg/plugin/framework/common/plugin_kinds.go并为每个插件构造可重启restartable的客户端包装后批量返回——这也回答了设计文档中//TODO: do we need to get these by name, or can we get them all?的疑问当前实现一次性返回全部已注册的删除插件。执行链路备份删除控制器如何调用删除插件DeleteItemAction的真正触发点位于备份删除控制器 pkg/controller/backup_deletion_controller.go。当用户删除一个 Backup 时实际通过DeleteBackupRequest驱动控制器的处理流程大致如下前置校验检查备份存储位置BackupStorageLocation是否处于只读模式或不可用状态见 pkg/controller/backup_deletion_controller.go状态流转将DeleteBackupRequest置为InProgress并给请求打上velero.io/backup-name、velero.io/backup-uid标签同时把Backup的阶段置为Deleting见 pkg/controller/backup_deletion_controller.go获取插件通过pluginManager.GetDeleteItemActions()获取全部删除插件见 pkg/controller/backup_deletion_controller.go下载备份包如果存在已注册的删除插件则从对象存储下载备份 tarballdownloadToTempFile。若下载失败如 tarball 不存在则跳过删除插件仅做 CSI VolumeSnapshot 的离线清理见 pkg/controller/backup_deletion_controller.go构造上下文并调用创建delete.Context包含 Backup、BackupReader、Actions、DiscoveryHelper、Filesystem 等然后调用delete.InvokeDeleteActions(deleteCtx)真正执行插件见 pkg/controller/backup_deletion_controller.go后续清理插件执行完成后控制器继续清理 PV 快照、Pod 卷快照、DataMover 移动的数据等见 pkg/controller/backup_deletion_controller.go。InvokeDeleteActions 的内部实现InvokeDeleteActions定义在 internal/delete/delete_item_action_handler.go其工作流程是理解删除插件执行语义的核心解析动作通过framework.NewDeleteItemActionResolver(ctx.Actions).ResolveActions(...)将插件与其AppliesTo选择器解析为DeleteItemResolvedAction。若没有任何插件且无错误直接返回删除流程照旧——这就是兼容性的来源解包备份将备份 tarball 解压到临时目录archive.NewExtractor(...).UnzipAndExtractBackup再通过archive.NewParser(...).Parse解析出备份中的资源清单按资源遍历对每个 group/resource、每个命名空间、每个具体 item先通过getApplicableActions按 groupResource 与 namespace 过滤出候选插件再对每个插件校验action.Selector.Matches(labels.Set(obj.GetLabels()))——即用资源对象自身的标签去匹配插件的 LabelSelector见 internal/delete/delete_item_action_handler.go执行与容错逐个调用action.DeleteItemAction.Execute(velero.DeleteItemActionExecuteInput{Item: obj, Backup: ctx.Backup})。即使某个插件执行出错循环也会继续保证单个失败插件不会阻断其余资源的清理但错误会被聚合收集kubeerrs.NewAggregate(deleteErrs)并返回给控制器导致本次删除失败以便后续重试——因为插件失败往往意味着其管理的外部产物如 DataMover 仓库快照可能未被删除若此时直接删除备份元数据会造成永久孤儿资源相关注释见 internal/delete/delete_item_action_handler.go。这里值得对照设计文档的一个细节DeleteActions 将按插件名称的字母顺序执行。在实现中插件的遍历顺序取决于GetDeleteItemActions从注册表取回的列表顺序而在单个资源 item 上多个插件的执行顺序由 resolvedActions 的顺序决定另一方面控制器里对所有资源项是无序遍历的。因此可预测的顺序更多体现在同一资源上多个插件的执行次序这一层面。gRPC 插件框架删除插件的进程外通信与 BIA/RIA 插件一致DeleteItemAction也走 Velero 的 gRPC 插件框架基于 hashicorp/go-plugin插件类型封装DeleteItemActionPlugin实现了 go-plugin 的Plugin接口GRPCServer注册DeleteItemActionGRPCServer见 pkg/plugin/framework/delete_item_action.gogRPC 服务端DeleteItemActionGRPCServer.AppliesTo调用实现者的AppliesTo()并把ResourceSelector序列化为 proto 响应Execute则把请求中的ItemUnstructured JSON与Backup反序列化后封装为DeleteItemActionExecuteInput交给实现者见 pkg/plugin/framework/delete_item_action_server.goproto 契约定义在 pkg/plugin/proto/DeleteItemAction.proto生成代码位于 pkg/plugin/generated/DeleteItemAction.pb.go客户端侧还有restartable_delete_item_action.go负责插件进程崩溃后的自动重启逻辑。这意味着第三方插件作者可以完全独立于 Velero 核心进程开发删除插件通过标准插件协议注册即可——这正是设计文档中核心无法为所有自定义资源扩展问题的最终解法。内置实现仓库中的两个 DeleteItemAction 实例当前仓库在 pkg/cmd/server/plugin/plugin.go 内置注册了两个删除插件可作为插件作者的参考范例RegisterDeleteItemAction( velero.io/dataupload-delete, newDateUploadDeleteItemAction(f), ). RegisterDeleteItemAction( velero.io/csi-volumesnapshotcontent-delete, newVolumeSnapshotContentDeleteItemAction(f), )示例一DataUploadDeleteAction实现位于 pkg/datamover/dataupload_delete_action.go职责是在 DataMover 数据移动场景下为被删除备份对应的 DataUpload 生成快照信息 ConfigMap供备份删除控制器后续定位并清理 Kopia 快照。其AppliesTo声明只作用于datauploads.velero.io资源func (d *DataUploadDeleteAction) AppliesTo() (velero.ResourceSelector, error) { return velero.ResourceSelector{ IncludedResources: []string{datauploads.velero.io}, }, nil }其Execute中有一个非常值得学习的防御性归属校验它检查 DataUpload 上的velero.io/backup-name标签是否与正在删除的 Backup 一致只有当归属匹配时才创建 ConfigMap。原因见 pkg/datamover/dataupload_delete_action.go在于若标签缺失或指向其他备份例如用户把 velero 命名空间也纳入了备份导致 DataUpload CR 被卷入他人备份包凭空创建带错误标签的 ConfigMap 会让真正的属主备份删除时查不到快照信息从而在对象存储中泄漏 Kopia 快照。此外该插件的单元测试位于 pkg/datamover/dataupload_delete_action_test.go。示例二VolumeSnapshotContentDeleteItemAction实现位于 internal/delete/actions/csi/volumesnapshotcontent_action.go职责是清理 CSI 快照背后的云存储快照。其AppliesTo声明作用于volumesnapshotcontents.snapshot.storage.k8s.io见 internal/delete/actions/csi/volumesnapshotcontent_action.gofunc (p *volumeSnapshotContentDeleteItemAction) AppliesTo() (velero.ResourceSelector, error) { return velero.ResourceSelector{ IncludedResources: []string{volumesnapshotcontents.snapshot.storage.k8s.io}, }, nil }它的Execute展示了删除插件的典型三步走模式把 Unstructured item 转换为强类型对象VolumeSnapshotContent校验资源归属——不删除 Velero 之外创建的 VolumeSnapshotContent通过检查其标签是否带有所删备份的名称kubeutil.HasBackupLabel见 internal/delete/actions/csi/volumesnapshotcontent_action.go执行真实清理——优先尝试删除集群中遗留的原始 VSC若不存在则创建一个临时的、DeletionPolicyDelete、指向原SnapshotHandle的 VSC以触发云厂商删除底层快照见 internal/delete/actions/csi/volumesnapshotcontent_action.go。这两个实例共同印证了设计文档的核心主张删除插件负责跨系统的资源清理而 Velero 核心只提供调度与执行框架。兼容性设计文档在 Compatibility 一节明确指出向后兼容应当是直截了当的——如果没有安装任何DeleteAction插件备份删除流程将和今天完全一致。这一点在实现中得到双重印证控制器层GetDeleteItemActions()返回空列表时len(actions) 0为假整个下载备份包、调用插件的分支被跳过见 pkg/controller/backup_deletion_controller.go处理器层InvokeDeleteActions中len(ctx.resolvedActions) 0 err nil时直接返回No delete item actions present, proceeding with rest of backup deletion process见 internal/delete/delete_item_action_handler.go。因此升级到带有删除插件的 Velero 版本不会改变既有备份的删除行为。已知问题与后续演进设计文档的 Open Issues 一节记录了一个当时尚未解决的问题为了给 Backup 打上自定义标签供AppliesTo的标签选择器使用Backup 对象必须在BackupItemAction与RestoreItemAction插件内部可被修改——而当时它不可修改。文档给出的临时变通方案是用户在创建备份时手动打标签但这并不理想。从落地实现看这个问题实际上被重定向解决了最终接口并未依赖给 Backup 打标签而是把AppliesTo的匹配粒度下沉到了备份包内的资源对象标签——插件在Execute中拿到具体 item 后再通过资源自身的标签如velero.io/backup-name做归属校验。设计文档中按 Backup 标签匹配的思路演化为按资源选择器 资源标签归属校验的双重机制既保证了插件只处理自己创建的资源也避免了对核心 Backup 对象修改能力的依赖。另外设计文档中标记为 TODO 的Alternatives Considered备选方案与Security Considerations安全考量部分在公开设计阶段未补充细节从实现看安全性主要体现在插件执行错误会被聚合上报不吞错、资源归属校验不误删他人资源、以及临时目录的清理defer ctx.Filesystem.RemoveAll(dir)等方面。小结回顾整个设计到落地的过程可以提炼出删除插件机制的三大设计支柱设计要点设计文档表述落地实现位置新插件类型DeleteActionDeleteItemAction见 pkg/plugin/velero/delete_item_action.go匹配机制AppliesTo标签选择器AppliesTo() (ResourceSelector, error) 资源标签校验调用时机备份删除时执行备份删除控制器 pkg/controller/backup_deletion_controller.go插件注册注册表 Manager 方法GetDeleteItemActions见 pkg/plugin/clientmgmt/manager.go执行引擎高层面言删除时运行internal/delete/delete_item_action_handler.go兼容性无插件则流程不变空列表短路行为不变对于希望为 Velero 生态贡献删除插件的开发者参考路径非常清晰实现AppliesTo()声明目标资源、在Execute()中编写跨系统清理逻辑、用RegisterDeleteItemAction注册插件然后由 Velero 在每次备份删除时自动为你清理外部遗留资源。相关的测试用例如 internal/delete/delete_item_action_handler_test.go、pkg/plugin/clientmgmt/manager_test.go完整覆盖了选择器匹配标签过滤多插件协同插件报错不中断等关键场景可作为理解行为边界的补充阅读材料。【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价