The backup center feature of Container Service for Kubernetes (ACK) captures all runtime configurations when backing up cluster resources. During restoration, you can modify specific resource configurations to adapt to the target cluster's environment — for example, switching image registries when migrating across clusters in a hybrid cloud setup.
This topic walks through backing up a StatefulSet and applying three types of resource modifications during restoration.
How it works
Three mechanisms control resource modifications during restoration:
| Mechanism | How it works | Configuration required |
|---|---|---|
| Automatic modifications | The backup center component automatically adjusts temporary metadata (UIDs), converts FlexVolume to Container Storage Interface (CSI), updates APIs, and applies cross-cloud compatibility changes. | None |
| Restore task parameters | Pass parameters in the ApplicationRestore spec to remap namespaces, change StorageClasses, switch image registries, or overwrite Service and Ingress annotations. | Configure namespaceMapping, imageRegistryMapping, and similar fields in the restore task. |
| Velero resource modifiers | Create a ConfigMap specifying JSON patch rules to add, remove, or replace resource fields using JSON patches. |
Create a ConfigMap and reference it from the restore task. |
By default, the CSI plug-in dynamically provisions a new volume of the same StorageClass when restoring a volume. Resource modifiers cannot modify volume configurations directly — convert the StorageClass to change volume configurations instead. To use resource modifiers, you must have role-based access control (RBAC) administrator permissions. Resource modifiers cannot modify resource groups or security-sensitive parameters. For more information, see Use RBAC to manage the operation permissions on resources in a cluster.
Prerequisites
Before you begin, ensure that you have:
-
migrate-controller 1.8.1 or later installed in both the backup cluster and the restore cluster. For more information, see Install migrate-controller and grant permissions.
-
A backup vault created in the backup cluster. For more information, see Create a backup vault.
-
(For resource modifiers) Kubernetes 1.20 or later and the CSI plug-in enabled in the cluster.
If the backup cluster, restore cluster, and backup vault reside in different regions, enable public network access on both clusters.
Example scenario
This example backs up an NGINX StatefulSet and restores it with four modifications applied simultaneously:
-
Namespace remapped from
defaulttodefault1 -
Image registry switched from the OpenAnolis community registry to Container Registry
-
Volume mount path changed from
/usr/share/nginx/html/to/data -
Node affinity rules removed
The StatefulSet runs in the default namespace of the backup cluster. It uses the image anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6 and mounts a 20 GiB persistent volume claim (PVC) using the alicloud-disk-topology-alltype StorageClass.
<details> <summary>Show StatefulSet YAML</summary>
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
namespace: default
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
serviceName: nginx
template:
metadata:
labels:
app: nginx
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: Exists
weight: 1
containers:
- image: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: web
protocol: TCP
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /usr/share/nginx/html/
name: www
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: nginx
name: www
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: alicloud-disk-topology-alltype
volumeMode: Filesystem
</details>
Run the following command to verify that the application is running and the volume is mounted:
kubectl get pod && kubectl get pvc
Expected output:
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 39s
web-1 1/1 Running 0 30s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
www-web-0 Bound d-2zefofsg0uzoiaxxxxxx 20Gi RWO alicloud-disk-topology-alltype <unset> 3m18s
www-web-1 Bound d-2zedgdlghw2oudxxxxxx 20Gi RWO alicloud-disk-topology-alltype <unset> 30s
Step 1: Back up the application
Back up the StatefulSet in the backup cluster using kubectl or the ACK console.
kubectl
-
Create a file named
applicationbackup.yamlwith the following content:apiVersion: csdr.alibabacloud.com/v1beta1 kind: ApplicationBackup metadata: # If the backup vault is already registered in this cluster, omit the csdr.alibabacloud.com/backuplocations annotation. annotations: csdr.alibabacloud.com/backuplocations: '{"name":"<backuplocationName>","region":"<bucketRegion>","bucket":"<bucketName>","prefix":"<path>","provider":"alibabacloud"}' name: <backupName> namespace: csdr spec: backupType: AppAndPvBackup includedNamespaces: - default pvBackup: defaultPvBackup: true storageLocation: <backuplocationName> ttl: 720h0m0s -
Apply the manifest to create the backup task:
kubectl apply -f applicationbackup.yaml -
Watch the backup task until it reaches the
Completedphase:kubectl -n csdr get applicationbackup <backupName> --watchExpected output:
NAME PHASE AGE <backupName> Completed 18s
ACK console
-
Log on to the ACK console. In the left-side navigation pane, click Clusters.
-
Click the name of the backup cluster. In the left-side navigation pane, choose Operations > Application Backup. If the backup service component is not installed, follow the on-screen instructions to install it. For registered clusters and ACK dedicated clusters, also configure permissions. For more information, see Install migrate-controller and grant permissions.
-
On the Application Backup page, click Instant Backup. In the Instant Backup panel, configure the following parameters and click OK. For more information about advanced parameters, see Create a backup plan or back up instantly.
Parameter Description Example Name Name of the instant backup task (required) backupBackup vault The backup vault to use (required) cnfs-oss-testBackup type Application Backup backs up cluster resources and their volumes. Data Protection backs up only PVCs and persistent volumes (PVs). For more information, see What are the scenarios for application backup and data protection? Application BackupBackup namespaces One or more namespaces to back up (required). The kube-system,kube-publish,kube-node-lease, andcsdrnamespaces cannot be backed up.defaultBackup volume Mounted Volumes backs up volume data to ECS snapshots or Cloud Backup. Disable restores only YAML files without backing up the underlying storage. Mounted Volumes -
On the Backup Records tab, confirm that the backup record's Status column shows Completed.
(Optional) Step 2: View the backup JSON file in OSS
The backup task generates a JSON file listing all backed-up resources, including their names, types, sizes, and creation dates. Check this file to confirm the resources you want to restore are included.
The backup vault used in this example has the following configuration:
| Backup vault name | OSS bucket name | OSS bucket subdirectory | OSS bucket region |
|---|---|---|---|
vault |
cnfs-oss-test |
subpath |
cn-beijing |
-
Log on to the OSS console.
-
In the left-side navigation pane, click Buckets, then click the
cnfs-oss-testbucket. -
Choose Object Management > Objects.
-
Navigate to
/subpath/backups/<backupName>/, download the<backupName>.tar.gzpackage, and decompress it to view the resource list.
Step 3: Configure resource modification rules in the restore cluster
A resource modifier ConfigMap has two sections: conditions (which resources to target) and patches (what changes to apply). The ConfigMap must reside in the csdr namespace and follow the naming convention <backupName>-resource-modifier.
The following ConfigMap removes node affinity rules and changes the volume mount path to /data for StatefulSets and pods with the label app: nginx:
apiVersion: v1
kind: ConfigMap
metadata:
name: <backupName>-resource-modifier
namespace: csdr
data:
modifier: |
version: v1
resourceModifierRules:
- conditions:
groupResource: statefulsets.apps
namespaces:
- default
labelSelector:
matchLabels:
app: nginx
patches:
- operation: remove
path: "/spec/template/spec/affinity/nodeAffinity"
- operation: replace
path: "/spec/template/spec/containers/0/volumeMounts/0/mountPath"
value: "/data"
- conditions:
groupResource: pods
resourceNameRegex: "^web.*$"
namespaces:
- default
labelSelector:
matchLabels:
app: nginx
patches:
- operation: remove
path: "/spec/affinity"
- operation: replace
path: "/spec/containers/0/volumeMounts/0/mountPath"
value: "/data"
conditions fields
| Field | Description | Required | Example |
|---|---|---|---|
groupResource |
The Kubernetes resource group. Use the format <resourceName>.<groupName>. Omit the group name for core group resources. Run kubectl api-resources to look up resource group names. |
Yes | statefulsets.apps, pods |
namespaces |
Namespaces containing the resources to modify. If blank, resources in all namespaces are modified. When namespace mapping is active, the original (source) namespace applies here. | No | default |
labelSelector |
A standard Kubernetes label selector (matchLabels or matchExpressions). Only resources that match are modified. |
No | matchLabels: {app: nginx} |
resourceNameRegex |
A regular expression to match resource names. Only matching resources are modified. | No | "^web.*$" |
patches fields
| Field | Description | Required | Example |
|---|---|---|---|
operation |
The JSON patch operation to perform. Supported values: add, remove, replace. |
Yes | replace |
path |
The JSON path of the field to modify. | Yes | "/spec/template/spec/containers/0/volumeMounts/0/mountPath" |
value |
The value to set. Applies to add and replace only — not used with remove. |
No | "/data" |
(Optional) Step 4: Initialize the backup vault in the restore cluster
Skip this step if the backup vault is already initialized in the restore cluster.
Apply the following backuplocation.yaml to register the backup vault:
apiVersion: csdr.alibabacloud.com/v1beta1
kind: BackupLocation
metadata:
name: <backup-vault-name>
namespace: csdr
spec:
backupSyncPeriod: 0s
config:
network: internal
region: <oss-bucket-region> # Must match the backup vault's initial configuration
objectStorage:
bucket: <oss-bucket-name>
prefix: <oss-bucket-subpath>
provider: alibabacloud
Step 5: Restore the application with modifications
Create a restore task in the restore cluster. The task below references the ConfigMap from Step 3 and uses namespaceMapping and imageRegistryMapping to remap the namespace and switch the image registry.
Sync the image to the new registry before changing the image address. The restore task references registry.cn-beijing.aliyuncs.com/<acrRegistry> — make sure the image exists there before applying.
-
Create a file named
applicationrestore.yamlwith the following content:apiVersion: csdr.alibabacloud.com/v1beta1 kind: ApplicationRestore metadata: name: <restoreName> namespace: csdr spec: backupName: <backupName> namespaceMapping: default: default1 # Restore resources into the default1 namespace imageRegistryMapping: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis: registry.cn-beijing.aliyuncs.com/<acrRegistry> resourceModifier: kind: ConfigMap name: <backupName>-resource-modifier # Must match the ConfigMap created in Step 3 -
Apply the manifest to start the restore task:
kubectl apply -f applicationrestore.yaml -
Watch the restore task until it reaches the
Completedphase:kubectl -n csdr get applicationrestore <restoreName> --watchExpected output:
NAME PHASE AGE <restoreName> Completed 18hTo view the restore task in the ACK console instead, see Step 3: Restore applications and volumes.
Step 6: Verify the restored application
Query the restored StatefulSet to confirm all modifications were applied:
kubectl -n default1 get sts web -o yaml
<details> <summary>Show expected output</summary>
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"web","namespace":"default"},"spec":{"replicas":2,"selector":{"matchLabels":{"app":"nginx"}},"serviceName":"nginx","template":{"metadata":{"labels":{"app":"nginx"}},"spec":{"affinity":{"nodeAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"preference":{"matchExpressions":[{"key":"node-role.kubernetes.io/master","operator":"Exists"}]},"weight":1}]}},"containers":[{"image":"anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.2","imagePullPolicy":"IfNotPresent","name":"nginx","ports":[{"containerPort":80,"name":"web","protocol":"TCP"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/usr/share/nginx/html/","name":"www"}]}]}},"volumeClaimTemplates":[{"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"labels":{"app":"nginx"},"name":"www"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"20Gi"}},"storageClassName":"alicloud-disk-topology-alltype","volumeMode":"Filesystem"}}]}}
creationTimestamp: "2024-10-09T08:32:50Z"
generation: 1
labels:
app: nginx
velero.io/backup-name: <backupName>
velero.io/restore-name: <restoreName>
name: web
namespace: default1
resourceVersion: "119622"
uid: d23878ea-0b9f-40ba-b61b-1ff6bb77eb43
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
podManagementPolicy: OrderedReady
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx
serviceName: nginx
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
affinity: {}
containers:
- image: registry.cn-beijing.aliyuncs.com/<acrRegistry>/nginx:1.14.1-8.6
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: web
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /data
name: www
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
app: nginx
name: www
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: alicloud-disk-topology-alltype
volumeMode: Filesystem
status:
phase: Pending
status:
availableReplicas: 0
collisionCount: 0
currentRevision: web-7b454646b4
observedGeneration: 1
replicas: 2
updateRevision: web-7b454646b4
</details>
The output confirms all four modifications were applied:
-
Namespace:
default→default1 -
Image:
anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6→registry.cn-beijing.aliyuncs.com/<acrRegistry>/nginx:1.14.1-8.6 -
Volume mount path:
/usr/share/nginx/html/→/data -
Node affinity: removed (
affinity: {})
Some fields in the output (such asresourceVersion,uid, andcreationTimestamp) are automatically added by Kubernetes and were not present in the original backup.