Skip to main content

Application Image Rewrite

Application image rewrite handles different business image registries between primary and standby clusters. It changes image fields in restored Kubernetes resources. It does not copy images and does not create image pull Secrets automatically.

Typical cases:

  • The primary cluster uses 10.10.10.170:32070/e2e/..., while the standby cluster can only pull from 10.10.10.171:32071/e2e/....
  • Production images live in an internal registry, while the DR site uses Harbor, Registry, or an offline registry.
  • Image tags change often, and you do not want to update exact image replacement rules after every release.
info

This page covers business application images. It is different from the Velero image registry configured when adding a cluster. The Velero image registry only affects Velero component installation.

Choose A Method

MethodActionUse caseRecommended for frequent releases
Exact replacementreplaceExactValueA specific full image value is stable.No
Prefix rewriterewriteImageRegistry prefixes differ, while image path and tag should follow the current source workload.Yes
Hand-written modifier rulesmodifierRulesYou need exact control over a resource and JSON Patch path.Only for a few fixed paths

For high-churn tags, prefer rewriteImage. During ResourceSync or Drill restore construction, the operator reads current source workload spec images and generates runtime modifier rules from stable source and target prefixes.

Preconditions

Image rewrite changes manifests only. Before enabling it, verify that:

  • The rewritten image tag or digest already exists in the target registry.
  • Target cluster nodes can access the target registry.
  • Private registries have usable image pull Secrets in target namespaces, or the application resources already reference the correct Secret.
  • restorePolicy.useUnifiedDirectionResolver is enabled.

Exact Replacement

Exact replacement is useful when the full image value is stable. For example, replace:

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

with:

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

In the Bulk modifications/deletions field, enter this array:

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

When editing DisasterInstance directly:

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 scans string leaf nodes inside the protected resource scope and creates reversible rules for values equal to sourceValue. It skips forbidden paths such as /status/**, /metadata/finalizers/**, and /metadata/ownerReferences/**.

Limitations:

  • If the image tag changes from v0.1 to v0.2, this exact rule no longer matches.
  • If you write modifierRules manually, do not patch /status/containerStatuses/*/image. Pod status is not writable during restore.

Prefix Rewrite

Prefix rewrite is the recommended method for frequent image releases. Configure only the stable source and target registry prefixes; the tag follows the current source workload.

For example, source image:

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

should become:

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

In the Bulk modifications/deletions field, enter:

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

In the 170/171 environment, if the source image prefix is 10.134.81.9:5000/ and the target registry keeps the source path under an archive prefix, configure:

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

For example, this source initContainer image:

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

is restored as:

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

If the source workload later upgrades to:

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

the next ResourceSync, Drill, or failover final sync dynamically generates:

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

Full DisasterInstance snippet:

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

Fields

FieldDescription
sourcePrefixSource image prefix. Use a trailing /; the platform normalizes prefixes.
targetPrefixTarget image prefix. The path, tag, and digest after the source prefix are preserved.
unmatchedPolicyKeep leaves unmatched images unchanged. Fail fails compilation. Default is Keep.
digestPolicyCurrently supports Preserve, which keeps the original digest.
directionPolicyUse Auto so runtime direction controls forward or reverse replacement.
applyToSupports resourceSync and drill, not dataSync.

If multiple prefixes match, the longest prefix wins. Equal-length conflicting prefixes fail compilation.

Scope

rewriteImage reads current source workload spec image fields and generates restore-time modifier rules. Common covered fields include:

  • 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
  • Matching initContainers and ephemeralContainers

It does not modify:

  • Pod.status.containerStatuses[*].image
  • Pod.status.containerStatuses[*].imageID
  • Any /status/** field

Pod status is observed runtime state, not desired state. Validate rewrite behavior through workload or Pod spec image fields, not only through status.containerStatuses[*].image.

Resource Selection

Do not exclude Pods only to avoid Pod status image fields:

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

Image rewrite already skips /status/**. Whether to exclude pods should be a DR policy decision. If applications are managed by Deployments or StatefulSets, bare Pods usually do not need to be restored separately. If the protected scope contains bare Pods that must be restored, excluding pods affects those resources.

Verification

After saving an instance, confirm the policy:

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

After ResourceSync, Drill, or failover, check the target workload spec:

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

You can also check Pod spec:

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

Check initContainers:

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

If Pod status still shows an old tag while spec shows the rewritten target image, trust spec first. When the same image content is retagged, container runtimes may show the original image name or old tag in status.

Troubleshooting

SymptomCauseFix
ModifierRuleRejected ... /status/containerStatuses/0/image is forbiddenA manual or legacy rule patched Pod status.Do not patch /status/**; use bulk actions or rewriteImage.
initContainer images are unchangedThe legacy image registry mapping path was used, an exact-value rule did not match, or no new ResourceSync/Drill restore build has run.Use bulkModifierActions[].action=rewriteImage, make sure applyTo includes resourceSync or drill, and inspect .spec.initContainers[*].image.
A new tag still restores as the old imageExact replaceExactValue only matched the old tag.Use rewriteImage and configure prefixes only.
modifierRuleSnapshot is emptyrewriteImage is compiled dynamically at runtime.This is expected; verify ResourceSync or Drill output.
Target Pod is ImagePullBackOffTarget image is missing, Secret is missing, or the node cannot reach the registry.Push the image to the target registry and verify imagePullSecret/network access.
conditions.groupResource is requiredA hand-written modifierRules object was used where a bulk action array was expected.Use bulkModifierActions for bulk changes; hand-written modifierRules must include conditions.groupResource.
Prefix rewrite has no effectsourcePrefix does not match the actual source spec image.Inspect the source workload spec image and configure the stable prefix.

Recommendations

  • Prefer rewriteImage for new business image registry rewrite configurations.
  • Keep frequently changing tags out of sourceValue.
  • Before failover, make sure the target registry contains the corresponding image tag.
  • Use directionPolicy: Auto so failover, undo, and reprotect can choose the correct direction at runtime.
  • Verify workload spec images instead of Pod status images.