Cluster Snapshot & Restore
A cluster snapshot writes the cluster's metadata to object storage. Table data already lives in object storage; the snapshot is what makes it readable again. If a cluster is lost, you restore it by creating a new one that loads the snapshot on startup.
This page covers two things:
- How Anywhere runs a restore.
- A worked example, from taking a snapshot through to a recovered cluster.
How Anywhere runs a restore
A restore is driven by these fields on the cluster:
spec:
disasterRecovery:
generation: 1
enabled: true
status:
disasterRecoveryStatus:
phase: todo/doing/done
reason: ""
observedGeneration: 1
startTimestamp: xxx # unix timestamp
endTimestamp: yyy # unix timestamp
When a restore is triggered
- The
enabledfield must be true. - The
generationis a monotonically increasing integer that represents the expected state (spec) change version of the resource object.observedGenerationrepresents the version of the restore that has been executed. The Operator compares the values ofgenerationandobservedGeneration: whendisasterRecoveryStatusis empty, orobservedGeneration < generation, a new restore is triggered. Ifgeneration == observedGeneration&&disasterRecovery.Phase!=v1.DRPhaseDone, it means that after the last reconcile, the cluster has entered restore mode. If both conditions are met, the cluster enters restore mode.
How the restore runs
For the CN component, reconciliation is paused.
The reconcile process for FE is as follows:
- Traverse
spec.phoenixAIFeSpec.ConfigMapsto confirm thatcluster_snapshot.yamlhas been mounted. Currently, this check is relatively simple, mainly to check whether theSubPathfield is equal tocluster_snapshot.yaml. - Modify the FE Statefulset, including:
- Start a single-replica FE.
- Inject the
RESTORE_CLUSTER_GENERATIONandRESTORE_CLUSTER_SNAPSHOTenvironment variables. The former is used to determine the Generation to which the Pod belongs, and the latter is an environment variable passed to the FE module to trigger the restore on the FE Pod. - Delete the startup/liveness configuration, because a restore can take a long time.
- Modify the Readiness configuration. The configuration for normal cluster is to send an HTTP request to FE 8030; the new configuration is used to detect whether the FE 9030 port is connected. Once connected, it means that the FE Pod restore is complete.
- Once the FE Pod restore is complete, the cluster starts normally, following the PhoenixAICluster spec.
How the restore phase is reported
disasterRecoveryStatus.phase reports how far the restore has got: todo, doing, or done.
The status update logic is as follows:
- Restore mode is entered for the first time (
disasterRecoveryStatusis empty) orobservedGeneration < generation. The phase becomestodo. - Once the modified StatefulSet is applied, the phase becomes
doing. How long it stays there depends on how long the restore takes. - The FE Pod is checked periodically: first its
generation, then whether it is Ready. - Once the FE Pod is Ready, the phase becomes
done.
Example
Inorder to keep it simple and easy for users to follow this document, we use the kube-anywhere Helm Chart to deploy.
Please note:
- Be sure to use at least v1.10.0 version of Operator and CRD.
- Sensitive information is replaced by xxx, please set it to a reasonable value.
1. Create a normal working cluster
Prepare the ./phoenixai-values.yaml file:
Note: we set
automated_cluster_snapshot_interval_secondsto configure every minute to take a snapshot.
operator:
phoenixAIOperator:
image:
repository: us-west1-docker.pkg.dev/phoenix-ai-images/enterprise/operator
tag: v1.10.0
imagePullPolicy: IfNotPresent
replicaCount: 1
resources:
requests:
cpu: 1m
memory: 20Mi
phoenixai:
phoenixAICluster:
enabledCn: true
phoenixAICnSpec:
config: |
sys_log_level = INFO
# ports for admin, web, heartbeat service
thrift_port = 9060
webserver_port = 8040
heartbeat_service_port = 9050
brpc_port = 8060
image:
repository: us-west1-docker.pkg.dev/phoenix-ai-images/enterprise/cn-ubuntu
tag: 4.1-latest
replicas: 1
resources:
limits:
cpu: 8
memory: 8Gi
requests:
cpu: 1m
memory: 10Mi
storageSpec:
name: cn
logStorageSize: 1Gi
storageSize: 10Gi
phoenixAIFeSpec:
feEnvVars:
- name: LOG_CONSOLE
value: "1"
config: |
LOG_DIR = ${STARROCKS_HOME}/log
DATE = "$(date +%Y%m%d-%H%M%S)"
JAVA_OPTS="-Dlog4j2.formatMsgNoLookups=true -Xmx8192m -XX:+UseG1GC -Xlog:gc*:${LOG_DIR}/fe.gc.log.$DATE:time -XX:ErrorFile=${LOG_DIR}/hs_err_pid%p.log -Djava.security.policy=${STARROCKS_HOME}/conf/udf_security.policy"
http_port = 8030
rpc_port = 9020
query_port = 9030
edit_log_port = 9010
mysql_service_nio_enabled = true
sys_log_level = INFO
run_mode = shared_data
cloud_native_meta_port = 6090
enable_load_volume_from_conf = true
cloud_native_storage_type = S3
aws_s3_path = xxx
aws_s3_region = xxx
aws_s3_endpoint = xxx
aws_s3_access_key = xxx
aws_s3_secret_key = xxx
# we add this configuration because we want to get cluster snapshot quickly
automated_cluster_snapshot_interval_seconds = 60
replicas: 3
image:
repository: us-west1-docker.pkg.dev/phoenix-ai-images/enterprise/fe-ubuntu
tag: 4.1-latest
resources:
limits:
cpu: 2
memory: 4Gi
requests:
cpu: 1m
memory: 20Mi
storageSpec:
logStorageSize: 1Gi
name: fe-storage
storageSize: 10Gi
Create the cluster using Helm:
helm install -f ./phoenixai-values.yaml kube-anywhere phoenixai/kube-anywhere
# make sure the cluster has been successfully deployed
kubectl get pods
NAME READY STATUS RESTARTS AGE
kube-anywhere-cn-0 1/1 Running 0 23s
kube-anywhere-fe-0 1/1 Running 0 79s
kube-anywhere-fe-1 1/1 Running 0 79s
kube-anywhere-fe-2 1/1 Running 0 79s
2. Create a table and insert data
Connect to the FE Pod:
# enter FE pod
kubectl exec -it kube-anywhere-fe-0 bash
# use mysql client to login
mysql -h 127.0.0.1 -P9030 -uroot
...
mysql>
Execute the following SQL statement:
CREATE
DATABASE IF NOT EXISTS quickstart;
USE
quickstart;
-- create table
CREATE TABLE source_wiki_edit
(
event_time DATETIME,
channel VARCHAR(32) DEFAULT '',
user VARCHAR(128) DEFAULT '',
is_anonymous TINYINT DEFAULT '0',
is_minor TINYINT DEFAULT '0',
is_new TINYINT DEFAULT '0',
is_robot TINYINT DEFAULT '0',
is_unpatrolled TINYINT DEFAULT '0',
delta INT DEFAULT '0',
added INT DEFAULT '0',
deleted INT DEFAULT '0'
) DUPLICATE KEY(
event_time,
channel,user,
is_anonymous,
is_minor,
is_new,
is_robot,
is_unpatrolled
)
PARTITION BY RANGE(event_time)(
PARTITION p06 VALUES LESS THAN ('2015-09-12 06:00:00'),
PARTITION p12 VALUES LESS THAN ('2015-09-12 12:00:00'),
PARTITION p18 VALUES LESS THAN ('2015-09-12 18:00:00'),
PARTITION p24 VALUES LESS THAN ('2015-09-13 00:00:00')
)
DISTRIBUTED BY HASH(user);
-- insert data
INSERT INTO source_wiki_edit
VALUES ("2015-09-12 00:00:00", "#en.wikipedia", "AustinFF", 0, 0, 0, 0, 0, 21, 5, 0),
("2015-09-12 00:00:00", "#ca.wikipedia", "helloSR", 0, 1, 0, 1, 0, 3, 23, 0),
("2015-09-12 08:00:00", "#ca.wikipedia", "helloSR", 0, 1, 0, 1, 0, 3, 23, 0);
-- select data
select *
from source_wiki_edit;
3. Generate Cluster Snapshot
Begin backup:
mysql
> ADMIN SET AUTOMATED CLUSTER SNAPSHOT ON STORAGE VOLUME builtin_storage_volume;
Query
OK, 0 rows affected (0.10 sec)
Wait for the backup to complete:
SELECT *
FROM INFORMATION_SCHEMA.CLUSTER_SNAPSHOT_JOBS \ G;
SNAPSHOT_NAME
: automated_cluster_snapshot_1739864377140
JOB_ID: 13018
CREATED_TIME: 2025-02-18 15:39:37
FINISHED_TIME: 2025-02-18 15:40:27
STATE: FINISHED
DETAIL_INFO:
ERROR_MESSAGE:
mysql>
SELECT *
FROM INFORMATION_SCHEMA.CLUSTER_SNAPSHOTS \ G;
*
************************** 1. row ***************************
SNAPSHOT_NAME: automated_cluster_snapshot_1739864488333
SNAPSHOT_TYPE: AUTOMATED
CREATED_TIME: 2025-02-18 15:41:28
FE_JOURNAL_ID: 1776
STARMGR_JOURNAL_ID: 126
PROPERTIES:
STORAGE_VOLUME: builtin_storage_volume
STORAGE_PATH: s3://xxx/data/7351ce6a-f4a4-4937-a876-cb8801085aea/meta/image/automated_cluster_snapshot_1739864488333
1 row in set (0.03 sec)
Please note: because we set the backup interval to 1 minute, the backup path may be different from the above. The final result can be viewed through s3:
s3cmd ls s3://xxx/data/7351ce6a-f4a4-4937-a876-cb8801085aea/meta/image/
sDIR s3://xxx/data/7351ce6a-f4a4-4937-a876-cb8801085aea/meta/image/automated_cluster_snapshot_1739858235830/
4. Delete the created cluster
helm uninstall kube-anywhere
# delete pvcs
kubectl get pvc | awk '{if (NR>1){print $1}}' | xargs kubectl delete pvc
persistentvolumeclaim "cn-data-kube-anywhere-cn-0" deleted
persistentvolumeclaim "cn-log-kube-anywhere-cn-0" deleted
persistentvolumeclaim "fe-storage-log-kube-anywhere-fe-0" deleted
persistentvolumeclaim "fe-storage-log-kube-anywhere-fe-1" deleted
persistentvolumeclaim "fe-storage-log-kube-anywhere-fe-2" deleted
persistentvolumeclaim "fe-storage-meta-kube-anywhere-fe-0" deleted
persistentvolumeclaim "fe-storage-meta-kube-anywhere-fe-1" deleted
persistentvolumeclaim "fe-storage-meta-kube-anywhere-fe-2" deleted
5. Create a new cluster to restore into
We will reuse the previous phoenixai-values.yaml file, so be sure to ensure the security of this configuration file.
Prepare a new file named override.yaml, which contains the configuration required for the restore.
phoenixai:
phoenixAICluster: # enable restore
disasterRecovery:
enabled: true
generation: 1
phoenixAIFeSpec: # mount the cluster_snapshot.yaml
configMaps:
- name: cluster-snapshot
mountPath: /opt/starrocks/fe/conf/cluster_snapshot.yaml
subPath: cluster_snapshot.yaml
configMaps:
- name: cluster-snapshot
data:
cluster_snapshot.yaml: |
# information about the cluster snapshot to be downloaded and restored
cluster_snapshot:
cluster_snapshot_path: s3://xxx/data/7351ce6a-f4a4-4937-a876-cb8801085aea/meta/image/automated_cluster_snapshot_1739858235830
storage_volume_name: builtin_storage_volume
# Operator will add the other FE followers automatically
# just leave it blank
frontends:
# Operator will add the CN nodes automatically
# just leave it blank
compute_nodes:
# used for restoring a cloned snapshot
storage_volumes:
- name: builtin_storage_volume
type: S3
location: s3://xxx/data
comment: my s3 volume
properties:
- key: aws.s3.region
value: xxx
- key: aws.s3.endpoint
value: xxx
- key: aws.s3.access_key
value: xxx
- key: aws.s3.secret_key
value: xxx
This time, we will deploy the cluster with the following command:
# Note:
# 1. make sure you are using the at least v1.10.0 version of Operator and CRD
# 2. the command to deploy the cluster is different from the first time. We specify two files, and the override.yaml
# is used for the recovery configuration.
helm install -f ./phoenixai-values.yaml -f override.yaml kube-anywhere phoenixai/kube-anywhere --version 1.10.0
The restore runs as follows:
-
Anywhere starts a single FE Pod and begins the restore.
kubectl get podsNAME READY STATUS RESTARTS AGEkube-anywhere-fe-0 1/1 Running 0 4m37sIf you check the status of the PhoenixAICluster at this time, you will see:
kubectl get pac kube-anywhere -oyaml | lessstatus:phase: runningdisasterRecovery:observedGeneration: 1phase: doingreason: disaster recovery is in progressstartTimestamp: "1739860263" -
Once the restore is complete, the remaining Pods start automatically.
kubectl get podsNAME READY STATUS RESTARTS AGEkube-anywhere-cn-0 1/1 Running 0 7m54skube-anywhere-fe-0 1/1 Running 0 7m1skube-anywhere-fe-1 1/1 Running 0 7m54skube-anywhere-fe-2 1/1 Running 0 7m54sThe cluster status is as follows:
status:phase: runningdisasterRecoveryStatus:endTimestamp: 1739861262observedGeneration: 1phase: donereason: disaster recovery is donestartTimestamp: 1739860263
Verify the restore
# enter the pod
kubectl exec -it kube-anywhere-fe-0 bash
# connect mysql
mysql -h 127.0.0.1 -P9030 -uroot
# get the data
mysql >USE quickstart
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql >select * from source_wiki_edit