Velero Restore API 的 ExistingResourcePolicy:控制已存在 Kubernetes 资源的还原行为
【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero
导读
当目标集群中已存在与备份中同名的 Kubernetes 资源时,Velero 默认会直接跳过还原——即使集群里的版本与备份中的版本不一致。ExistingResourcePolicy是 Velero 为 Restore API 新增的可选字段,它让用户能够显式决定:备份中的资源是否应覆盖(patch)集群中已存在的同名资源。本文基于 existing-resource-policy_design.md 设计文档,结合当前仓库中的 API 类型定义、CLI 实现与还原执行逻辑,完整讲解该策略的设计演进、最终落地形态与实战用法。读完本文,你将掌握none与update两种策略的语义差异、如何通过 YAML 或velero create restore命令配置它,以及 Velero 在还原阶段如何处理"已存在且内容不同"的资源。
背景:Velero 原有的"已存在即跳过"行为
在设计该功能之前,Velero 对集群中已存在资源的还原流程(以备份中的Service为例)如下:
- Velero 尝试还原该
Service; - 先从集群中获取同名的
Service; - 若该
Service已存在,则比较集群实例与备份实例:- 两者不相同:跳过还原,并添加一条还原警告(
ServiceAccount对象除外); - 两者相同:跳过还原,并在日志中记录"还原被跳过"。
- 两者不相同:跳过还原,并添加一条还原警告(
也就是说,无论备份中的资源与集群中的资源是否一致,Velero 都一律跳过,用户没有任何手段让备份中的版本覆盖集群中的版本。相关需求记录于 Velero 的 issue #4066。这一局限使得 Velero 难以胜任"用备份持续同步/刷新目标集群"这类场景,因此社区提出了为 Restore API 增加ExistingResourcePolicy的增强提案。
设计目标与明确排除的范围
设计文档对本提案划定了清晰的边界:
目标
- 为 Restore API 增加
ExistingResourcePolicy,让用户决定备份资源是否覆盖集群中已存在的资源。
非目标(本次不实现)
- 不改变
ServiceAccount对象既有的还原工作流; - 不支持
recreate(先删除再重建)策略,该选项仅作为未来扩展方向保留。
与提案无关、明确排除的功能
- 对非 Kubernetes 资源的还原策略;
- 对
PersistentVolume数据的还原策略(PV 数据策略由独立的existingVolumeDataPolicy字段负责)。
典型使用场景
场景 A:用备份集群持续同步生产集群
假设存在一个与生产集群相同的备份集群。经过一段时间的运行,生产集群发生了变化——新增了 Deployment、部分 Secret 被更新。为了让备份集群的 Kubernetes 资源(PV 数据除外)与生产集群保持同步,用户可以定期创建新备份,再通过 Velero restore 将最新状态还原到备份集群。此时必须允许备份中的资源覆盖备份集群中已存在的旧版本,这正是update策略的用武之地。
场景 B:帮助识别资源增量(delta)
这里"delta"指的是上一次备份还原出来的资源、但已不在最新备份中的资源。例如:
- 集群 A 有 P1、P2、P3 三个资源,先创建 Backup1(含 P1、P2、P3)并还原到集群 B;
- 随后集群 A 删除了 P1、更新了 P2;
- 再创建 Backup2(此时只含 P2' 和 P3);
- 于是 delta = |集群 B − Backup2|,即"删除 P1、更新 P2"。
在第二次还原时,用户希望借助还原过程来识别这些资源增量,从而判断集群 B 与最新备份之间的差异。
三种候选设计方案的权衡
设计文档提出了三种 API 形态,最终实现选择了方案一。
方案一:在 Restore API 增加existingResourcePolicy字段
不改变 Velero 既有行为,仅新增一个可选的existingResourcePolicy字段,取值如下:
| 取值 | 行为 |
|---|---|
none | 维持现有行为:资源在集群中已存在时直接跳过还原 |
update | 对已存在的资源尝试打补丁(patch):若补丁成功,集群资源被更新为备份版本并打上最新的 backup/restore 标签;若补丁失败,记录还原警告,并退而求其次仅更新资源的 backup/restore 标签,标签更新再失败则记录还原错误 |
recreate | 若资源已存在则先删除再重建(本提案的非目标,列为未来范围) |
需要强调的是:该设计中任何策略都不会删除资源,update策略只是通过 patch 更新资源。
示例 A——对velero-protection命名空间中的services和deployments执行none策略:
Kind: Restore … includeNamespaces: velero-protection includeResources: - services - deployments existingResourcePolicy: none示例 B——对gdpr-application命名空间中的secrets和daemonsets执行update策略:
Kind: Restore … includeNamespaces: gdpr-application includeResources: - secrets - daemonsets existingResourcePolicy: update方案二:增加existingResourcePolicyConfig字段(按资源类型细粒度配置)
该方案允许用户为不同的资源类型指定不同的行为,形成"资源类型 → 行为"的映射:
existingResourcePolicyConfig: - patch: includedResources: [ ]string - recreate: includedResources: [ ]string注意:
- 该方案中没有
none行为,因为不配置即等价于当前/默认的 Velero 还原行为; recreate同样被列为未来范围。
示例——对inventory-app命名空间中的secrets与daemonsets执行patch:
Kind: Restore … includeNamespaces: inventory-app existingResourcePolicyConfig: patch: includedResources: - secrets - daemonsets方案三:方案一与方案二的组合(默认策略 + 按资源覆盖)
同时新增existingResourceDefaultPolicy与existingResourcePolicyOverrides两个字段:前者描述本次还原的默认行为,后者可针对特定资源显式覆盖默认策略。
示例——默认patch,但对secrets覆盖为none:
Kind: Restore … includeNamespaces: inventory-app existingResourceDefaultPolicy: patch existingResourcePolicyOverrides: none: includedResources: - secrets实现决策
最终团队选择实现方案一,理由如下:
- 更易于实现;
- 更易于扩展,为未来演进到方案三保留了空间;
- 提供了保留 Velero 既有还原工作流的选项。
当前仓库中的最终落地实现
Restore API 类型定义
方案一落地为RestoreSpec中的ExistingResourcePolicy字段,类型为ResourcePolicyType,定义在 pkg/apis/velero/v1/restore_types.go:
// ExistingResourcePolicy specifies the restore behavior for the Kubernetes resource to be restored // +optional // +nullable ExistingResourcePolicy ResourcePolicyType `json:"existingResourcePolicy,omitempty"`策略常量定义于同一文件的第 337–343 行附近:
// ResourcePolicyTypeNone means velero will not overwrite the resource ResourcePolicyTypeNone ResourcePolicyType = "none" // ResourcePolicyTypeUpdate means velero will try to attempt a patch on // the resource to overwrite the in-cluster one ResourcePolicyTypeUpdate ResourcePolicyType = "update"ResourcePolicyType本身是字符串别名类型:
// ResourcePolicyType helps specify the ExistingResourcePolicy type ResourcePolicyType string需要注意的是,设计文档中草案曾使用PolicyType与PolicyTypeUpdate = "update"的命名,而当前仓库实际落地的类型名为ResourcePolicyType,字符串取值仍为none/update。
CRD 中的字段声明
该字段同样体现在生成的 CRD 定义中,见 config/crd/v1/bases/velero.io_restores.yaml:
existingResourcePolicy: description: ExistingResourcePolicy specifies the restore behavior for the Kubernetes resource to be restored nullable: true type: string还原执行逻辑
还原时对"已存在资源"的处理集中在 pkg/restore/restore.go 中。核心分支逻辑如下:
- 若
existingResourcePolicy存在且为none:记录警告"could not restore, ... already exists",将该项标记为ItemRestoreResultSkipped(跳过); - 若存在且为
update:调用processUpdateResourcePolicy对已存在的资源执行 patch; - 若未设置该字段:保留 Velero 原有的行为——跳过还原并记录警告。
对未变化资源(集群内版本与备份版本相同)的处理在 pkg/restore/restore.go:当策略为update时,先移除旧的 restore 标签,再调用updateBackupRestoreLabels仅更新 backup/restore 标签。
processUpdateResourcePolicy的具体流程见 pkg/restore/restore.go:
- 从集群实例上移除 restore 标签,以便应用最新的 backup/restore 名称;
- 用
generatePatch(fromCluster, obj)生成集群实例与备份实例之间的补丁; - 若补丁为空,说明集群内与期望状态一致,直接跳过;
- 调用
resourceClient.Patch打补丁(同时包含资源差异与最新标签); - 若 patch 失败:记录警告,并降级为仅更新 backup/restore 标签(
updateBackupRestoreLabels),标签更新也失败则追加还原错误。
此外,ServiceAccount分支(pkg/restore/restore.go)同样考虑了update策略:patch 失败时先移除 restore 标签再尝试仅更新标签,成功则将该项标记为ItemRestoreResultUpdated。
测试验证
仓库中的单元测试对两种策略均有覆盖,例如 pkg/restore/restore_test.go 中通过 Builder 分别构造ExistingResourcePolicy("update")与ExistingResourcePolicy("none")的 Restore 来验证不同策略下的还原结果。对应 Builder 方法定义于 pkg/builder/restore_builder.go:
// ExistingResourcePolicy sets the Restore's resource policy. func (b *RestoreBuilder) ExistingResourcePolicy(policy string) *RestoreBuilder { b.object.Spec.ExistingResourcePolicy = velerov1api.ResourcePolicyType(policy) return b }CLI:--existing-resource-policy标志
标志定义与校验
设计文档规划了 CLI 变更(落地位置 pkg/cmd/cli/restore/create.go),当前实现中标志定义如下:
flags.StringVar(&o.ExistingResourcePolicy, "existing-resource-policy", "", "Restore Policy to be used during the restore workflow for Kubernetes resources, can be - none or update")并在还原创建前进行取值校验(pkg/cmd/cli/restore/create.go):
if len(o.ExistingResourcePolicy) > 0 && !restore.IsResourcePolicyValid(o.ExistingResourcePolicy) { return errors.New("existing-resource-policy has invalid value, it accepts only none, update as value") }即传入除none、update之外的值会被直接拒绝。
使用示例
velero create restore <restore_name> --existing-resource-policy=update描述器(describer)输出
velero restore describe也会展示该策略。普通描述器在 pkg/cmd/util/output/restore_describer.go 中仅在策略非空时输出;结构化描述器(-o json等)在 pkg/cmd/util/output/restore_structured_describer.go 中将其写入specInfo["existingResourcePolicy"]。
实战配置指南
方式一:通过 Restore YAML 配置
直接创建/编辑 Restore 对象并指定策略,例如对gdpr-application命名空间中的secrets与daemonsets执行覆盖更新:
apiVersion: velero.io/v1 kind: Restore metadata: name: my-restore namespace: velero spec: backupName: my-backup includeNamespaces: - gdpr-application includeResources: - secrets - daemonsets existingResourcePolicy: update应用后执行velero restore describe my-restore(或带-o json的结构化输出)即可确认策略是否生效。
方式二:通过 CLI 创建
velero create restore my-restore \ --from-backup my-backup \ --include-namespaces gdpr-application \ --include-resources secrets,daemonsets \ --existing-resource-policy=update行为速查
| 集群内资源状态 | 未设置策略(默认) | none | update |
|---|---|---|---|
| 不存在 | 正常创建 | 正常创建 | 正常创建 |
| 存在且与备份相同 | 跳过(日志记录) | 跳过(日志记录) | 仅更新 backup/restore 标签 |
| 存在且与备份不同 | 跳过 + 警告 | 跳过 + 警告(标记为 Skipped) | 先 patch 资源差异与标签;patch 失败则降级仅更新标签并给出警告 |
总结
ExistingResourcePolicy在不破坏 Velero 既有"已存在即跳过"默认行为的前提下,为用户提供了显式覆盖已存在资源的入口:none保持默认跳过语义,update则通过"先 patch 资源差异、失败后降级仅更新标签"的容错链路尽量将集群资源同步为备份版本。该功能已在 Restore API(existingResourcePolicy字段)、CRD(config/crd/v1/bases/velero.io_restores.yaml)、CLI(--existing-resource-policy)与还原执行逻辑(pkg/restore/restore.go)中完整落地,可用于备份集群同步、资源增量识别等场景;而recreate(先删后建)与"按资源类型细粒度指定策略"则被明确列为未来扩展方向。
【免费下载链接】veleroBackup and migrate Kubernetes applications and their persistent volumes项目地址: https://gitcode.com/GitHub_Trending/ve/velero
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考