跳到主要内容

业务镜像替换

业务镜像替换用于处理主备集群镜像仓库不一致的问题。它只修改恢复到目标集群的 Kubernetes 资源中的镜像字段,不会复制镜像,也不会自动创建镜像拉取 Secret。

典型场景:

  • 主集群使用 10.10.10.170:32070/e2e/...,备集群只能从 10.10.10.171:32071/e2e/... 拉取。
  • 生产仓库是内网地址,灾备仓库是 Harbor、Registry 或离线仓库。
  • 镜像 tag 经常变化,不希望每次版本发布后都重新维护完整镜像替换规则。
信息

这里说的是业务应用镜像替换,不是添加集群时的 Velero 镜像源。Velero 镜像源只影响 Velero 组件安装,业务应用镜像应通过恢复策略中的批量修改或 rewriteImage 配置。

选择哪种方式

方式动作适用场景是否推荐用于高频发版
完全替换replaceExactValue源镜像完整值固定,或者只想替换一个确定的镜像。
前缀重写rewriteImage主备仓库前缀不同,但镜像路径和 tag 需要跟随源端当前值。
手写自定义规则modifierRules需要精确控制某个资源、某个 JSON Patch 路径。只适合少量固定路径

如果业务镜像 tag 变化频率高,优先使用 rewriteImage。它会在 ResourceSync 或 Drill 恢复构建阶段读取源集群当前 workload spec 中的镜像,再按前缀动态生成修改规则,不依赖提交实例时保存的旧 tag。

前置条件

镜像替换只改资源清单,不负责镜像分发。配置前需要确认:

  • 目标仓库中已经存在替换后的镜像 tag 或 digest。
  • 目标集群节点可以访问目标镜像仓库。
  • 私有仓库需要认证时,目标命名空间中有可用的 imagePullSecret,或者应用资源中已经引用了正确的 Secret。
  • restorePolicy.useUnifiedDirectionResolver 已开启。

完全替换

完全替换适合镜像值稳定的场景。例如只把一个固定镜像:

10.10.10.170:32070/e2e/disaster-web-test:v0.1

替换为:

10.10.10.171:32071/e2e/disaster-web-test:v0.1

在控制台的 资源批量修改/删除 输入框中填写数组:

[
{
"id": "replace-170-image-to-171-image",
"action": "replaceExactValue",
"enabled": true,
"applyTo": ["resourceSync", "drill"],
"sourceValue": "10.10.10.170:32070/e2e/disaster-web-test:v0.1",
"targetValue": "10.10.10.171:32071/e2e/disaster-web-test:v0.1",
"directionPolicy": "Auto"
}
]

如果直接编辑 DisasterInstance,完整片段如下:

spec:
restorePolicy:
useUnifiedDirectionResolver: true
bulkModifierActions:
- id: replace-170-image-to-171-image
action: replaceExactValue
enabled: true
applyTo:
- resourceSync
- drill
sourceValue: 10.10.10.170:32070/e2e/disaster-web-test:v0.1
targetValue: 10.10.10.171:32071/e2e/disaster-web-test:v0.1
directionPolicy: Auto

replaceExactValue 会扫描保护范围内资源的字符串叶子节点,命中完整 sourceValue 后生成可逆修改规则。它会跳过 /status/**/metadata/finalizers/**/metadata/ownerReferences/** 等禁止修改路径。

限制:

  • 镜像 tag 一旦从 v0.1 变成 v0.2,这条完全替换规则就不再命中新镜像。
  • 如果手写 modifierRules,不要把 patch path 指向 /status/containerStatuses/*/image,该路径属于 Pod status,恢复时禁止修改。

前缀重写

前缀重写适合镜像版本频繁变化的场景。只维护稳定的源仓库前缀和目标仓库前缀,tag 跟随源端当前 workload。

例如源端当前镜像是:

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

希望恢复到目标集群时变成:

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

在控制台的 资源批量修改/删除 输入框中填写数组:

[
{
"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"
}
}
]

170/171 真实环境中,源端镜像前缀类似 10.134.81.9:5000/,目标仓库需要保留源端路径并加上归档目录时,可以直接写:

[
{
"id": "rewrite-primary-registry",
"action": "rewriteImage",
"enabled": true,
"applyTo": ["resourceSync", "drill"],
"directionPolicy": "Auto",
"imageRewrite": {
"sourcePrefix": "10.134.81.9:5000/",
"targetPrefix": "registry-tke.szmacloud.csg:30088/dr_images/10.134.81.9_5000/",
"unmatchedPolicy": "Keep",
"digestPolicy": "Preserve"
}
}
]

例如源端 initContainer 镜像:

10.134.81.9:5000/groundnuty/k8s-wait-for:v1.5.1

恢复到目标集群时会生成:

registry-tke.szmacloud.csg:30088/dr_images/10.134.81.9_5000/groundnuty/k8s-wait-for:v1.5.1

如果源端后来升级为:

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

下一次资源同步、演练或切换触发的最终同步会动态生成目标镜像:

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

完整 DisasterInstance 片段:

spec:
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

字段说明

字段说明
sourcePrefix源端镜像前缀。建议以 / 结尾,系统会做前缀规范化。
targetPrefix目标端镜像前缀。替换时保留源镜像前缀之后的路径、tag 和 digest。
unmatchedPolicy未匹配前缀时的处理方式。Keep 表示保持原值,Fail 表示编译失败。默认 Keep
digestPolicydigest 处理方式。当前支持 Preserve,表示保留原镜像 digest。
directionPolicy建议使用 Auto,由运行时方向决定正向或反向替换。
applyTo支持 resourceSyncdrill,不支持 dataSync

多条前缀同时匹配时,最长前缀优先。如果存在长度相同但目标不同的冲突规则,编译会失败。

生效范围

rewriteImage 会读取源集群当前 workload spec 中的镜像字段,并在恢复构建阶段生成修改规则。覆盖的常见字段包括:

  • Deployment.spec.template.spec.containers[*].image
  • StatefulSet.spec.template.spec.containers[*].image
  • DaemonSet.spec.template.spec.containers[*].image
  • Job.spec.template.spec.containers[*].image
  • CronJob.spec.jobTemplate.spec.template.spec.containers[*].image
  • Pod.spec.containers[*].image
  • 对应的 initContainersephemeralContainers

它不会修改:

  • Pod.status.containerStatuses[*].image
  • Pod.status.containerStatuses[*].imageID
  • 任意 /status/** 字段

Pod status 是运行时观测状态,不是期望状态。镜像替换是否成功,应看 Deployment、StatefulSet、Job 或 Pod 的 spec 镜像字段,而不是只看 status.containerStatuses[*].image

和资源选择的关系

不要为了避开 Pod status 镜像而配置:

{
"resourceSelection": {
"excludedResources": ["pods"]
}
}

镜像替换逻辑本身会跳过 /status/**,不需要通过排除 Pod 来规避 status patch。是否排除 pods 应由容灾策略决定:如果业务只通过 Deployment/StatefulSet 管理 Pod,通常可以不单独恢复裸 Pod;如果保护范围中存在需要恢复的裸 Pod,排除 pods 会影响这些资源恢复。

验证方式

保存实例后,先确认恢复策略已经写入:

kubectl -n disaster-system get disasterinstance <instance-name> \
-o jsonpath='{.spec.restorePolicy.bulkModifierActions}{"\n"}'

触发资源同步、演练或切换后,在目标集群检查 workload spec:

kubectl --context <target-context> -n <namespace> get deploy <name> \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'

也可以检查 Pod spec:

kubectl --context <target-context> -n <namespace> get pod <pod-name> \
-o jsonpath='{.spec.containers[0].image}{"\n"}'

检查 initContainers:

kubectl --context <target-context> -n <namespace> get pod <pod-name> \
-o jsonpath='{range .spec.initContainers[*]}{.name}{"="}{.image}{"\n"}{end}'

如果 Pod status 中仍显示旧 tag,但 spec 已经是目标镜像,优先以 spec 为准。使用同一镜像内容重新打 tag 时,容器运行时可能在 status 中展示原始镜像名或旧 tag。

常见问题

现象原因处理
ModifierRuleRejected ... /status/containerStatuses/0/image is forbidden手写规则或旧规则命中了 Pod status。不要 patch /status/**;改用批量修改或 rewriteImage
initContainer 镜像没有变化使用了旧镜像源映射入口、完整值规则未命中,或没有触发新的 ResourceSync/Drill 恢复构建。使用 bulkModifierActions[].action=rewriteImage,确认 applyTo 包含 resourceSyncdrill,然后检查 .spec.initContainers[*].image
tag 升级后仍恢复旧镜像使用了完整值 replaceExactValue,规则只匹配旧 tag。改用 rewriteImage,只维护源/目标前缀。
modifierRuleSnapshot 为空仅配置 rewriteImage 时,规则在运行时动态生成。这是预期行为,检查 ResourceSync/Drill 结果即可。
目标 Pod ImagePullBackOff目标镜像不存在、Secret 缺失或节点无法访问仓库。先把镜像推到目标仓库,并确认 imagePullSecret 和网络。
conditions.groupResource is required把手写 modifierRules 当成批量修改写法使用。批量修改入口填写 bulkModifierActions 数组;手写 modifierRules 必须包含 conditions.groupResource
前缀没有生效sourcePrefix 与源端实际镜像不匹配。kubectl get deploy -o jsonpath 查看源端 spec 镜像,按稳定前缀重新配置。

推荐实践

  • 新配置优先使用 rewriteImage,不要再依赖旧的业务镜像源映射入口。
  • 只把稳定仓库前缀写入 DSL,不要把频繁变化的 tag 写进 sourceValue
  • 每次切换前确认目标仓库已经有对应 tag。
  • 使用 directionPolicy: Auto,让 Failover、Undo、Reprotect 按运行时方向自动选择替换方向。
  • 验证时看 workload spec 镜像字段,不用 Pod status 判断规则是否生效。