跳到主要内容

资源定制化修改与批量修改删除

资源定制化修改用于处理源集群和目标集群之间的资源差异。常见场景包括:

  • 源、目标集群的 IngressClass 不一致。
  • 目标集群不允许继续使用源集群的 nodeSelectoraffinity 或特定注解。
  • 业务配置中写死了源端地址,需要在目标端替换为目标端地址。
  • 演练环境需要移除某些只适用于生产环境的标签或注解。
  • 需要让 ResourceSync 恢复出来的 standby 资源按目标集群规则落地。

资源定制化修改在恢复过程中生效。它不会修改源集群原始资源,而是在 AppRestore 创建 Velero Restore 前生成 Resource Modifier 配置,让目标集群恢复出的资源被修改后再落地。

能力边界

当前实例恢复策略中有两类入口:

能力字段作用
资源定制化修改restorePolicy.modifierRules / restorePolicy.modifierRulesText精确指定匹配条件和 JSON Patch。
资源批量修改/删除restorePolicy.bulkModifierActions / restorePolicy.bulkModifierActionsText按实例保护范围扫描源集群资源,自动生成最终修改规则。

如果只关注业务镜像仓库不一致、镜像 tag 频繁变化或 rewriteImage 配置,请先阅读 业务镜像替换

这里的“删除”指删除资源对象中的字段或键,例如删除某个 annotation、label、nodeSelector 键。它不是删除整个 Kubernetes 资源对象。如果需要不恢复某类资源,应使用 restorePolicy.resourceSelection.excludedResourcesexcludedNamespaceScopedResources

使用 modifierRulesbulkModifierActions 时,建议同时开启:

{
"restorePolicy": {
"useUnifiedDirectionResolver": true
}
}

如果未开启统一方向解析器,新 DSL 规则可能无法被 operator 编译执行。

控制台入口

进入 容灾管理 / 实例配置,创建或编辑容灾实例,展开 高级选项

  • 资源定制化修改:适合输入明确的 JSON Patch 规则。
  • 资源批量修改/删除:适合按值或键批量生成修改规则。

配置完成后保存实例。server 会在写入 DisasterInstance 前校验规则,并在需要时生成 modifierRuleSnapshot

执行时机

资源定制化修改会被应用到实例恢复链路:

链路applyTo说明
数据同步恢复dataSync用于 DataSync 触发的恢复。
资源同步恢复resourceSync用于 ResourceSync 恢复资源骨架。
容灾演练drill用于 Drill 恢复或演练覆盖配置。

modifierRules 可使用 dataSyncresourceSyncdrillbulkModifierActions 当前只支持 resourceSyncdrill,不支持 dataSync

如果 applyTo 为空:

  • modifierRules 由 operator 的默认规则处理。
  • bulkModifierActions 默认等价于 ["resourceSync"]

定制化修改规则

modifierRules 支持两种模式。

模式mode适用场景
Velero 原生规则veleroNative直接下发 JSON Patch,适合单向修改或删除字段。
可逆规则reversible源、目标之间存在成对值,Failover、Reprotect、Drill 需要按方向自动选择值。

匹配条件

每条规则通过 conditions 匹配资源:

字段说明
groupResource必填。资源复数名加 API group,例如 deployments.appsstatefulsets.appsingresses.networking.k8s.iopersistentvolumeclaims
resourceNameRegex可选。按资源名称正则匹配。
namespaces可选。限制命名空间。不能超出实例保护的命名空间范围。
labelSelector可选。Kubernetes LabelSelector

JSON Pointer 路径

path 使用 JSON Pointer 格式:

/spec/template/spec/containers/0/image
/metadata/annotations/example.com~1role

路径规则:

  • 必须以 / 开头。
  • /status/metadata/finalizers/metadata/ownerReferences 禁止修改。
  • 路径中的 / 要写成 ~1~ 要写成 ~0
  • 数组下标从 0 开始。
  • add 到数组尾部可使用 /-,但可逆规则不支持 /-

示例:修改 IngressClass

源集群使用 nginx,目标集群使用 traefik 时,可以配置 veleroNative 规则:

{
"restorePolicy": {
"useUnifiedDirectionResolver": true,
"modifierRules": [
{
"id": "ingress-class-to-traefik",
"mode": "veleroNative",
"enabled": true,
"applyTo": ["resourceSync", "drill"],
"conditions": {
"groupResource": "ingresses.networking.k8s.io",
"namespaces": ["demo"]
},
"veleroRule": {
"patches": [
{
"operation": "replace",
"path": "/spec/ingressClassName",
"value": "traefik"
}
]
},
"directionPolicy": "ForwardOnly",
"onConflict": "Fail"
}
]
}
}

如果源、目标会反向保护,建议使用可逆规则:

{
"restorePolicy": {
"useUnifiedDirectionResolver": true,
"modifierRules": [
{
"id": "ingress-class-reversible",
"mode": "reversible",
"enabled": true,
"applyTo": ["resourceSync", "drill"],
"conditions": {
"groupResource": "ingresses.networking.k8s.io",
"namespaces": ["demo"]
},
"pair": {
"path": "/spec/ingressClassName",
"sourceValue": "nginx",
"targetValue": "traefik"
},
"directionPolicy": "Auto",
"onConflict": "Fail"
}
]
}
}

directionPolicy=Auto 会根据当前恢复方向选择 sourceValuetargetValue。如果只允许源到目标方向生效,使用 ForwardOnly;如果只允许反向保护方向生效,使用 ReverseOnly

示例:修改指定 PVC StorageClass

如果只需要改写少量 PVC 的 spec.storageClassName,可以用可逆规则精确匹配 PVC 名称。下面示例把 blueking 命名空间中的两个 PVC 在 DataSync 恢复时改到目标集群的 nfs-client

{
"restorePolicy": {
"useUnifiedDirectionResolver": true,
"modifierRules": [
{
"id": "rewrite-skywalking-pvc-sc",
"mode": "reversible",
"enabled": true,
"applyTo": ["dataSync"],
"priority": 100,
"conditions": {
"groupResource": "persistentvolumeclaims",
"namespaces": ["blueking"],
"resourceNameRegex": "^bk-skywalking-agent-nfs-pvc$"
},
"pair": {
"path": "/spec/storageClassName",
"sourceValue": "local-storage",
"targetValue": "nfs-client"
},
"directionPolicy": "Auto",
"onConflict": "Fail"
},
{
"id": "rewrite-bkrepo-pvc-sc",
"mode": "reversible",
"enabled": true,
"applyTo": ["dataSync"],
"priority": 100,
"conditions": {
"groupResource": "persistentvolumeclaims",
"namespaces": ["blueking"],
"resourceNameRegex": "^bk-repo-bkrepo-nfs-pvc$"
},
"pair": {
"path": "/spec/storageClassName",
"sourceValue": "bkrepo-nfs",
"targetValue": "nfs-client"
},
"directionPolicy": "Auto",
"onConflict": "Fail"
}
]
}
}

如果是在控制台的 资源定制化修改 文本框中填写,通常只填写 modifierRules 数组本身,不需要外层 restorePolicy。JSON 必须是数组或对象中合法的数组字段,不能直接粘贴两个用逗号分隔的对象片段。

示例:删除 nodeSelector

目标集群节点标签和源集群不同,恢复工作负载时可以移除 nodeSelector

{
"restorePolicy": {
"useUnifiedDirectionResolver": true,
"modifierRules": [
{
"id": "remove-node-selector",
"mode": "veleroNative",
"applyTo": ["resourceSync"],
"conditions": {
"groupResource": "deployments.apps",
"namespaces": ["demo"],
"resourceNameRegex": "^web-.*"
},
"veleroRule": {
"patches": [
{
"operation": "remove",
"path": "/spec/template/spec/nodeSelector"
}
]
},
"directionPolicy": "ForwardOnly",
"onConflict": "Fail"
}
]
}
}

如果部分 Deployment 没有 nodeSelector,提交期 live validation 可能返回路径不存在。此时应缩小 resourceNameRegexlabelSelector,只匹配确实存在该字段的资源。

示例:添加或替换注解

给恢复到目标集群的 Deployment 添加注解:

{
"restorePolicy": {
"useUnifiedDirectionResolver": true,
"modifierRules": [
{
"id": "mark-restored-by-testudo",
"mode": "veleroNative",
"applyTo": ["resourceSync"],
"conditions": {
"groupResource": "deployments.apps",
"namespaces": ["demo"]
},
"veleroRule": {
"patches": [
{
"operation": "add",
"path": "/metadata/annotations/testudo.softcdata.com~1restored-by",
"value": "resourcesync"
}
]
}
}
]
}
}

注意 testudo.softcdata.com/restored-by 中的 / 在 JSON Pointer 中写作 ~1

批量修改

bulkModifierActions 用于把批量修改意图交给平台处理。提交期静态动作会按实例保护范围扫描源集群资源,并生成可执行的 modifierRuleSnapshot;运行时动态动作只保存稳定 DSL,由 operator 在恢复构建阶段读取源集群真实资源后动态生成规则。

支持的动作:

动作action说明
批量替换精确值replaceExactValue扫描所有字符串叶子节点,等于 sourceValue 的字段会生成可逆替换规则。
批量删除键removeKey扫描对象/map 中等于 key 的键,并生成 remove patch。
动态镜像重写rewriteImage按镜像前缀匹配源集群真实 workload 镜像,在 ResourceSync/Drill 运行时动态生成镜像替换规则。

批量扫描范围来自:

  • DisasterInstance.spec.namespaces
  • DisasterInstance.spec.labelSelector
  • restorePolicy.resourceSelection

默认只扫描命名空间资源。只有在 resourceSelection 明确包含集群级资源时,静态批量动作才会扫描集群级资源。静态扫描会跳过 /status/**/metadata/finalizers/**/metadata/ownerReferences/** 等禁止路径;不需要通过排除 pods 来规避 status.containerStatuses[*].image

批量替换固定值

例如业务资源里多处写了源端服务地址 10.10.0.12,目标端需要改为 10.20.0.12

{
"restorePolicy": {
"useUnifiedDirectionResolver": true,
"bulkModifierActions": [
{
"id": "replace-service-ip",
"action": "replaceExactValue",
"enabled": true,
"applyTo": ["resourceSync", "drill"],
"sourceValue": "10.10.0.12",
"targetValue": "10.20.0.12",
"directionPolicy": "Auto"
}
]
}
}

保存实例时,server 会扫描源集群匹配范围内的资源。例如发现:

Deployment demo/web /spec/template/spec/containers/0/env/0/value = 10.10.0.12

会生成类似快照规则:

{
"id": "bulk-replace-service-ip-0001",
"mode": "reversible",
"applyTo": ["resourceSync", "drill"],
"priority": -100,
"conditions": {
"groupResource": "deployments.apps",
"namespaces": ["demo"],
"resourceNameRegex": "^web$"
},
"pair": {
"path": "/spec/template/spec/containers/0/env/0/value",
"sourceValue": "10.10.0.12",
"targetValue": "10.20.0.12"
},
"directionPolicy": "Auto",
"onConflict": "Fail"
}

实际持久化在:

DisasterInstance.spec.restorePolicy.modifierRuleSnapshot
DisasterInstance.spec.restorePolicy.modifierRuleSnapshotHash

批量删除键

例如源端资源中存在只适用于主站点的 annotation:

metadata.annotations.site-role=primary

可以批量删除键 site-role

{
"restorePolicy": {
"useUnifiedDirectionResolver": true,
"bulkModifierActions": [
{
"id": "drop-site-role",
"action": "removeKey",
"enabled": true,
"applyTo": ["resourceSync"],
"key": "site-role"
}
]
}
}

removeKey 默认使用 directionPolicy=ForwardOnly。它会删除所有对象/map 中名为 site-role 的键,例如:

/metadata/annotations/site-role
/metadata/labels/site-role

如果只想删除某类资源中的键,应配合实例命名空间、标签筛选和 resourceSelection 缩小范围。

动态重写镜像前缀

如果镜像 tag 或 digest 变化频率很高,不建议用 replaceExactValue 写完整镜像值。完整值一变化,已经保存的静态 modifierRuleSnapshot 就会失效。此时应使用 rewriteImage,只声明稳定的源/目标镜像前缀。

注意这里的 rewriteImagerestorePolicy.bulkModifierActions 下的动态动作,不是旧的实例级镜像源映射入口。新实例建议使用这个 DSL 表达业务镜像前缀重写:

{
"restorePolicy": {
"useUnifiedDirectionResolver": true,
"bulkModifierActions": [
{
"id": "rewrite-primary-registry",
"action": "rewriteImage",
"enabled": true,
"applyTo": ["resourceSync", "drill"],
"directionPolicy": "Auto",
"imageRewrite": {
"sourcePrefix": "10.11.11.1:5000/",
"targetPrefix": "registry-test.xxx.xxx.com:30088/dr_images/10_11_11_1_5000/",
"unmatchedPolicy": "Keep",
"digestPolicy": "Preserve"
}
}
]
}
}

恢复构建阶段,operator 会从源集群读取当前 workload spec 中的镜像。例如源端当前镜像是:

10.11.11.1:5000/blueking/bcs-bkcmdb-synchronizer:v1.32.0

运行时会生成等价的可逆修改规则,把目标端镜像改为:

registry-test.xxx.xxx.com:30088/dr_images/10_11_11_1_5000/blueking/bcs-bkcmdb-synchronizer:v1.32.0

rewriteImage 的要点:

  • sourcePrefixtargetPrefix 必填,末尾 / 会被归一化处理。
  • applyTo 支持 resourceSyncdrill,不支持 dataSync
  • unmatchedPolicy 支持 KeepFail,为空时默认为 Keep
  • digestPolicy 当前只支持 Preserve,会保留原镜像 tag 或 digest 后缀。
  • 多个前缀同时命中时使用最长前缀;同长度命中多个不同结果会拒绝本次编译。
  • 动态编译覆盖 DeploymentStatefulSetDaemonSetJobCronJobPodspec 镜像字段,包括 containersinitContainersephemeralContainers
  • 动态编译只处理 workload spec 中的镜像字段,不会修改 /status/containerStatuses/*/image

replaceExactValuerewriteImage 的选择原则:

场景推荐动作原因
固定 IP、固定域名、固定注解值散落在多个字段replaceExactValue提交时可扫描出确定路径,并生成长期 modifierRuleSnapshot
镜像仓库前缀不同,但镜像 tag/digest 经常变化rewriteImage运行时按源集群真实镜像动态生成规则,不依赖旧 tag。
需要删除 annotation、label 或 map keyremoveKey语义是删除键,不是替换值。

文本输入字段

控制台或 API 可以使用文本字段提交规则:

文本字段解析结果
restorePolicy.modifierRulesTextJSON 数组字符串,解析到 modifierRules
restorePolicy.bulkModifierActionsTextJSON 对象或 JSON 数组字符串,解析到 bulkModifierActions

modifierRulesText 示例:

[
{
"id": "patch-ingress",
"mode": "veleroNative",
"applyTo": ["resourceSync"],
"conditions": {
"groupResource": "ingresses.networking.k8s.io",
"namespaces": ["demo"]
},
"veleroRule": {
"patches": [
{
"operation": "replace",
"path": "/spec/ingressClassName",
"value": "traefik"
}
]
}
}
]

bulkModifierActionsText 可以是单个对象:

{
"id": "replace-service-ip",
"action": "replaceExactValue",
"sourceValue": "10.10.0.12",
"targetValue": "10.20.0.12"
}

也可以是数组:

[
{
"id": "replace-service-ip",
"action": "replaceExactValue",
"sourceValue": "10.10.0.12",
"targetValue": "10.20.0.12"
},
{
"id": "drop-site-role",
"action": "removeKey",
"key": "site-role"
}
]

不要同时提交语义不一致的结构化字段和文本字段。例如 modifierRulesmodifierRulesText 同时存在但内容不同,server 会返回:

ModifierRulesInputConflict

bulkModifierActionsbulkModifierActionsText 冲突时会返回:

BulkModifierActionsInputConflict

更新实例

可以通过实例更新接口修改恢复策略:

curl -X PUT "$TESTUDO_SERVER/apis/disasterinstances.testudo.softcdata.com/v1/instances/demo-dr?namespace=disaster-system" \
-H "Content-Type: application/json" \
-d '{
"restorePolicy": {
"useUnifiedDirectionResolver": true,
"modifierRules": [
{
"id": "remove-node-selector",
"mode": "veleroNative",
"applyTo": ["resourceSync"],
"conditions": {
"groupResource": "deployments.apps",
"namespaces": ["demo"]
},
"veleroRule": {
"patches": [
{
"operation": "remove",
"path": "/spec/template/spec/nodeSelector"
}
]
}
}
]
}
}'

更新后建议触发一次资源同步,让目标集群重新生成 standby 资源。

curl -X POST "$TESTUDO_SERVER/apis/disasterinstances.testudo.softcdata.com/v1/instances/demo-dr/actions?namespace=disaster-system" \
-H "Content-Type: application/json" \
-d '{
"action": "syncresource"
}'

也可以在控制台实例详情页执行 同步资源

验证

查看实例策略

kubectl -n disaster-system get disasterinstance demo-dr -o yaml

重点查看:

spec:
restorePolicy:
useUnifiedDirectionResolver: true
modifierRules:
bulkModifierActions:
modifierRuleSnapshot:
modifierRuleSnapshotHash:

如果配置了 replaceExactValueremoveKey,但没有 modifierRuleSnapshotmodifierRuleSnapshotHash,说明提交期静态批量规则还没有成功生成快照。仅配置 rewriteImage 时,modifierRuleSnapshotmodifierRuleSnapshotHash 可以为空,这是预期行为,因为镜像规则会在 ResourceSync/Drill 恢复构建阶段动态生成。

查看 ResourceSync 和 AppRestore

kubectl -n disaster-system get resourcesync
kubectl -n disaster-system describe resourcesync <resourcesync-name>
kubectl -n disaster-system get apprestore
kubectl -n disaster-system describe apprestore <apprestore-name>

AppRestore.spec.resourceModifierRules 是 operator 最终传给 Velero 的可执行规则。

查看 Velero ResourceModifier ConfigMap

operator 会在目标集群的 velero 命名空间创建 Resource Modifier ConfigMap:

kubectl --kubeconfig=<target-kubeconfig> -n velero get configmap
kubectl --kubeconfig=<target-kubeconfig> -n velero get configmap <modifier-configmap-name> -o yaml

ConfigMap 中会包含:

version: v1
resourceModifierRules:
- conditions:
groupResource: deployments.apps
patches:
- operation: remove
path: /spec/template/spec/nodeSelector

查看目标资源

资源同步完成后,在目标集群检查实际资源:

kubectl --kubeconfig=<target-kubeconfig> -n demo get deploy web -o yaml
kubectl --kubeconfig=<target-kubeconfig> -n demo get ingress -o yaml

确认被修改字段已经符合预期。

常见错误

错误常见原因处理方式
ModifierRulesTextInvalidmodifierRulesText 不是合法 JSON 数组字符串。校验 JSON 格式,确保最外层是数组。
BulkModifierActionsTextInvalidbulkModifierActionsText 不是合法 JSON 对象或数组。校验 JSON 格式。
ModifierRulesInputConflict同时提交了 modifierRulesmodifierRulesText,且内容不一致。只保留一个入口,或确保两者语义一致。
BulkModifierActionsInputConflict同时提交了 bulkModifierActionsbulkModifierActionsText,且内容不一致。只保留一个入口,或确保两者语义一致。
matched zero resources规则或批量动作没有匹配到任何资源。检查命名空间、资源类型、名称正则和标签选择器。
path segment not foundJSON Pointer 指向的字段不存在。缩小匹配范围,或先确认源资源中该字段存在。
applyTo=dataSync is not supportedbulkModifierActions 配置了 dataSync批量动作只用于 resourceSyncdrill
imageRewrite.sourcePrefix is requiredrewriteImage 没有配置源镜像前缀。填写 imageRewrite.sourcePrefix
imageRewrite.targetPrefix is requiredrewriteImage 没有配置目标镜像前缀。填写 imageRewrite.targetPrefix
imageRewrite.digestPolicy must be Preserve配置了当前不支持的 digest 策略。删除该字段或设置为 Preserve
unified direction resolver is disabled使用新 DSL 但未开启 useUnifiedDirectionResolver设置 restorePolicy.useUnifiedDirectionResolver=true

生产建议

  • 先在演练实例或演练命名空间验证规则,不要直接在生产切换链路首次启用复杂规则。
  • 优先使用 resourceNameRegexnamespaceslabelSelector 缩小作用范围。
  • 对需要反向保护的字段优先使用 reversible,不要写死单向 replace
  • 删除类规则先从 annotation、label 这类低风险字段开始验证。
  • 批量替换只匹配字符串叶子节点,不能替换数字、布尔、对象或数组。
  • 静态批量动作匹配不到资源会失败,这是为了避免规则静默无效。
  • 使用静态批量动作时,保存配置后检查 modifierRuleSnapshotHash,确认 server 已生成最终快照。
  • 使用 rewriteImage 时,不要把当前 tag 写进 sourceValue/targetValue;只维护稳定前缀,并在资源同步或演练后检查目标 workload 镜像。
  • 资源同步完成后必须到目标集群查看实际资源 YAML,而不是只看实例状态。