From 6ee5f5a2bcb7f6154f1742d9073a6e034747f6b0 Mon Sep 17 00:00:00 2001 From: gnolong <2391353625@qq.com> Date: Mon, 23 Dec 2024 18:41:46 +0800 Subject: [PATCH 1/7] feat: xtrabackup incremental backup for mysql --- .../apecloud-mysql/templates/_helpers.tpl | 9 +++ .../xtrabackup-incremental-backup.sh | 48 ++++++++++++++++ .../xtrabackup-incremental-restore.sh | 56 +++++++++++++++++++ addons/apecloud-mysql/templates/_names.tpl | 11 ++++ .../templates/actionset-incremental.yaml | 42 ++++++++++++++ .../templates/backuppolicytemplate.yaml | 12 ++++ 6 files changed, 178 insertions(+) create mode 100644 addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh create mode 100644 addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh create mode 100644 addons/apecloud-mysql/templates/actionset-incremental.yaml diff --git a/addons-cluster/apecloud-mysql/templates/_helpers.tpl b/addons-cluster/apecloud-mysql/templates/_helpers.tpl index 4a508828f..d5a7e25fb 100644 --- a/addons-cluster/apecloud-mysql/templates/_helpers.tpl +++ b/addons-cluster/apecloud-mysql/templates/_helpers.tpl @@ -116,4 +116,13 @@ schedulingPolicy: app.kubernetes.io/instance: {{ include "kblib.clusterName" . }} apps.kubeblocks.io/component-name: mysql topologyKey: kubernetes.io/hostname + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - k3d-multi-agent-2 + {{- end -}} \ No newline at end of file diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh new file mode 100644 index 000000000..92fa0d686 --- /dev/null +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -e +set -o pipefail + +# if the script exits with a non-zero exit code, touch a file to indicate that the backup failed, +# the sync progress container will check this file and exit if it exists +function handle_exit() { + exit_code=$? + if [ $exit_code -ne 0 ]; then + echo "failed with exit code $exit_code" + touch "${DP_BACKUP_INFO_FILE}.exit" + exit 1 + fi +} +trap handle_exit EXIT + +export PATH="$PATH:$DP_DATASAFED_BIN_PATH" + +# 1. check parent backup name +if [[ -z ${DP_PARENT_BACKUP_NAME} ]]; then + echo "DP_PARENT_BACKUP_NAME is empty" + exit 1 +fi + +# 2. get parent backup +mkdir -p ${DATA_DIR} +PARENT_DIR=${DATA_MOUNT_DIR}/parent +mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} +export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}${DP_TARGET_RELATIVE_PATH}" +datasafed pull "${DP_PARENT_BACKUP_NAME}.xbstream" - | xbstream -x +xtrabackup --decompress --remove-original --target-dir=${PARENT_DIR} + +# compatible with version 0.6 +export DATASAFED_BACKEND_BASE_PATH="$DP_BACKUP_BASE_PATH" +if [ -f ${DATA_DIR}/mysql-bin.index ]; then + echo "" | datasafed push - "apecloud-mysql.old" +fi + +# 3. do incremental xtrabackup +START_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ') +xtrabackup --compress=zstd --backup --safe-slave-backup --slave-info --stream=xbstream \ + --host=${DP_DB_HOST} --port=${DP_DB_PORT} \ + --user=${DP_DB_USER} --password=${DP_DB_PASSWORD} \ + --datadir=${DATA_DIR} --incremental-basedir=${PARENT_DIR} | datasafed push - "/${DP_BACKUP_NAME}.xbstream" +STOP_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ') +TOTAL_SIZE=$(datasafed stat / | grep TotalSize | awk '{print $2}') +echo "{\"totalSize\":\"$TOTAL_SIZE\",\"timeRange\":{\"start\":\"${START_TIME}\",\"end\":\"${STOP_TIME}\"}}" >"${DP_BACKUP_INFO_FILE}" +rm -rf ${PARENT_DIR} diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh new file mode 100644 index 000000000..64621d2c8 --- /dev/null +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -e +set -o pipefail +export PATH="$PATH:$DP_DATASAFED_BIN_PATH" + +# 1. check base backup name +if [[ -z ${DP_BASE_BACKUP_NAME} ]]; then + echo "DP_BASE_BACKUP_NAME is empty" + exit 1 +fi + +# 2. download backup files +# download base backup file +mkdir -p ${DATA_DIR} +BASE_DIR=${DATA_MOUNT_DIR}/base +mkdir -p ${BASE_DIR} && cd ${BASE_DIR} +export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_BASE_BACKUP_NAME}${DP_TARGET_RELATIVE_PATH}" +datasafed pull "${DP_BASE_BACKUP_NAME}.xbstream" - | xbstream -x +xtrabackup --decompress --remove-original --target-dir=${BASE_DIR} +# download parent backup files +if [ -n "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES}" ]; then + read -r -a ANCESTOR_INCREMENTAL_BACKUP_NAMES <<< "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES//,/ }" +fi +INCS_DIR=${DATA_MOUNT_DIR}/incs +mkdir -p ${INCS_DIR} +for parent_name in "${ANCESTOR_INCREMENTAL_BACKUP_NAMES[@]}"; do + export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${parent_name}${DP_TARGET_RELATIVE_PATH}" + mkdir -p ${INCS_DIR}/${parent_name} && cd ${INCS_DIR}/${parent_name} + datasafed pull "${parent_name}.xbstream" - | xbstream -x + xtrabackup --decompress --remove-original --target-dir=${INCS_DIR}/${parent_name} +done +# download target backup file +export DATASAFED_BACKEND_BASE_PATH="$DP_BACKUP_BASE_PATH" +mkdir -p ${INCS_DIR}/${DP_BACKUP_NAME} && cd ${INCS_DIR}/${DP_BACKUP_NAME} +datasafed pull "${DP_BACKUP_NAME}.xbstream" - | xbstream -x +xtrabackup --decompress --remove-original --target-dir=${INCS_DIR}/${DP_BACKUP_NAME} + +old_signal="apecloud-mysql.old" +log_bin=${LOG_BIN} +if [ "$(datasafed list ${old_signal})" == "${old_signal}" ]; then + log_bin="${DATA_DIR}/mysql-bin" +fi + +# 3. prepare data +xtrabackup --prepare --apply-log-only --target-dir=${BASE_DIR} +for parent_name in "${ANCESTOR_INCREMENTAL_BACKUP_NAMES[@]}"; do + xtrabackup --prepare --apply-log-only --target-dir=${BASE_DIR} --incremental-dir=${INCS_DIR}/${parent_name} +done +xtrabackup --prepare --target-dir=${BASE_DIR} --incremental-dir=${INCS_DIR}/${DP_BACKUP_NAME} + +# 4. restore +xtrabackup --move-back --target-dir=${BASE_DIR} --datadir=${DATA_DIR}/ --log-bin=${log_bin} +touch ${DATA_DIR}/${SIGNAL_FILE} +rm -rf ${BASE_DIR} +rm -rf ${INCS_DIR} +chmod -R 0777 ${DATA_DIR} \ No newline at end of file diff --git a/addons/apecloud-mysql/templates/_names.tpl b/addons/apecloud-mysql/templates/_names.tpl index 2a6951d57..7a55406f1 100644 --- a/addons/apecloud-mysql/templates/_names.tpl +++ b/addons/apecloud-mysql/templates/_names.tpl @@ -9,6 +9,17 @@ apecloud-mysql-xtrabackup {{- end -}} {{- end -}} +{{/* +Define xtrabackup actionSet name for incremental backups +*/}} +{{- define "apecloud-mysql.xtrabackupIncActionSetName" -}} +{{- if eq (len .Values.resourceNamePrefix) 0 -}} +apecloud-mysql-xtrabackup-incremental +{{- else -}} +{{- .Values.resourceNamePrefix -}}-xtrabackup-incremental +{{- end -}} +{{- end -}} + {{/* Define volume snapshot actionSet name */}} diff --git a/addons/apecloud-mysql/templates/actionset-incremental.yaml b/addons/apecloud-mysql/templates/actionset-incremental.yaml new file mode 100644 index 000000000..22ff51dcf --- /dev/null +++ b/addons/apecloud-mysql/templates/actionset-incremental.yaml @@ -0,0 +1,42 @@ +apiVersion: dataprotection.kubeblocks.io/v1alpha1 +kind: ActionSet +metadata: + name: {{ include "apecloud-mysql.xtrabackupIncActionSetName" . }} + labels: + clusterdefinition.kubeblocks.io/name: apecloud-mysql +spec: + backupType: Incremental + env: + - name: DATA_DIR + value: {{ .Values.mysqlConfigs.dataDir }} + - name: LOG_BIN + value: {{ .Values.mysqlConfigs.logBin }} + - name: DP_DB_PORT + value: "3306" + - name: DATA_MOUNT_DIR + value: {{ .Values.mysqlConfigs.dataMountPath }} + - name: SIGNAL_FILE + value: .xtrabackup_restore_new_cluster + backup: + preBackup: [] + postBackup: [] + backupData: + image: {{ include "apecloud-mysql.bakcupToolImage" . }} + runOnTargetPodNode: true + command: + - sh + - -c + - | + {{- .Files.Get "dataprotection/xtrabackup-incremental-backup.sh" | nindent 8 }} + syncProgress: + enabled: true + intervalSeconds: 5 + restore: + prepareData: + image: {{ include "apecloud-mysql.bakcupToolImage" . }} + command: + - sh + - -c + - | + {{- .Files.Get "dataprotection/xtrabackup-incremental-restore.sh" | nindent 8 }} + postReady: [] \ No newline at end of file diff --git a/addons/apecloud-mysql/templates/backuppolicytemplate.yaml b/addons/apecloud-mysql/templates/backuppolicytemplate.yaml index 792c500f2..5c7db5541 100644 --- a/addons/apecloud-mysql/templates/backuppolicytemplate.yaml +++ b/addons/apecloud-mysql/templates/backuppolicytemplate.yaml @@ -20,6 +20,14 @@ spec: volumeMounts: - name: data mountPath: {{ .Values.mysqlConfigs.dataMountPath }} + - name: xtrabackup-inc + compatibleMethod: xtrabackup + snapshotVolumes: false + actionSetName: {{ include "apecloud-mysql.xtrabackupIncActionSetName" . }} + targetVolumes: + volumeMounts: + - name: data + mountPath: {{ .Values.mysqlConfigs.dataMountPath }} - name: volume-snapshot snapshotVolumes: true actionSetName: {{ include "apecloud-mysql.vsActionSetName" . }} @@ -34,6 +42,10 @@ spec: enabled: false cronExpression: "0 18 * * *" retentionPeriod: 7d + - backupMethod: xtrabackup-inc + enabled: false + cronExpression: "0 19 * * *" + retentionPeriod: 7d - backupMethod: volume-snapshot enabled: false cronExpression: "0 18 * * *" From e10e99a0164932bbd4224be042d0b122a41d8f54 Mon Sep 17 00:00:00 2001 From: gnolong <2391353625@qq.com> Date: Mon, 30 Dec 2024 11:44:54 +0800 Subject: [PATCH 2/7] fix target relative path --- .../dataprotection/xtrabackup-incremental-backup.sh | 2 +- .../dataprotection/xtrabackup-incremental-restore.sh | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh index 92fa0d686..51a49487d 100644 --- a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh @@ -26,7 +26,7 @@ fi mkdir -p ${DATA_DIR} PARENT_DIR=${DATA_MOUNT_DIR}/parent mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} -export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}${DP_TARGET_RELATIVE_PATH}" +export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" datasafed pull "${DP_PARENT_BACKUP_NAME}.xbstream" - | xbstream -x xtrabackup --decompress --remove-original --target-dir=${PARENT_DIR} diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh index 64621d2c8..04d97f454 100644 --- a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh @@ -3,6 +3,11 @@ set -e set -o pipefail export PATH="$PATH:$DP_DATASAFED_BIN_PATH" +function change_path() { + backup_name=$1 + export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${backup_name}/${DP_TARGET_RELATIVE_PATH}" +} + # 1. check base backup name if [[ -z ${DP_BASE_BACKUP_NAME} ]]; then echo "DP_BASE_BACKUP_NAME is empty" @@ -14,7 +19,7 @@ fi mkdir -p ${DATA_DIR} BASE_DIR=${DATA_MOUNT_DIR}/base mkdir -p ${BASE_DIR} && cd ${BASE_DIR} -export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_BASE_BACKUP_NAME}${DP_TARGET_RELATIVE_PATH}" +change_path "${DP_BASE_BACKUP_NAME}" datasafed pull "${DP_BASE_BACKUP_NAME}.xbstream" - | xbstream -x xtrabackup --decompress --remove-original --target-dir=${BASE_DIR} # download parent backup files @@ -24,13 +29,13 @@ fi INCS_DIR=${DATA_MOUNT_DIR}/incs mkdir -p ${INCS_DIR} for parent_name in "${ANCESTOR_INCREMENTAL_BACKUP_NAMES[@]}"; do - export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${parent_name}${DP_TARGET_RELATIVE_PATH}" + change_path "${parent_name}" mkdir -p ${INCS_DIR}/${parent_name} && cd ${INCS_DIR}/${parent_name} datasafed pull "${parent_name}.xbstream" - | xbstream -x xtrabackup --decompress --remove-original --target-dir=${INCS_DIR}/${parent_name} done # download target backup file -export DATASAFED_BACKEND_BASE_PATH="$DP_BACKUP_BASE_PATH" +change_path "${DP_BACKUP_NAME}" mkdir -p ${INCS_DIR}/${DP_BACKUP_NAME} && cd ${INCS_DIR}/${DP_BACKUP_NAME} datasafed pull "${DP_BACKUP_NAME}.xbstream" - | xbstream -x xtrabackup --decompress --remove-original --target-dir=${INCS_DIR}/${DP_BACKUP_NAME} From 4df1e18ca410d671e4d7f45b84508dd1cf65d88d Mon Sep 17 00:00:00 2001 From: gnolong <2391353625@qq.com> Date: Mon, 30 Dec 2024 14:45:12 +0800 Subject: [PATCH 3/7] fix note --- .../dataprotection/xtrabackup-incremental-restore.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh index 04d97f454..f6f762060 100644 --- a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh @@ -3,6 +3,7 @@ set -e set -o pipefail export PATH="$PATH:$DP_DATASAFED_BIN_PATH" +# function change_path takes one argument, the backup name, and changes the DATASAFED_BACKEND_BASE_PATH function change_path() { backup_name=$1 export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${backup_name}/${DP_TARGET_RELATIVE_PATH}" @@ -58,4 +59,5 @@ xtrabackup --move-back --target-dir=${BASE_DIR} --datadir=${DATA_DIR}/ --log-bin touch ${DATA_DIR}/${SIGNAL_FILE} rm -rf ${BASE_DIR} rm -rf ${INCS_DIR} -chmod -R 0777 ${DATA_DIR} \ No newline at end of file +chmod -R 0777 ${DATA_DIR} +echo "Restore completed!" From 031139d36f88557585facab963b82999736efe53 Mon Sep 17 00:00:00 2001 From: gnolong <2391353625@qq.com> Date: Mon, 30 Dec 2024 15:07:40 +0800 Subject: [PATCH 4/7] tidy --- .../xtrabackup-incremental-restore.sh | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh index f6f762060..c704243ed 100644 --- a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh @@ -3,12 +3,22 @@ set -e set -o pipefail export PATH="$PATH:$DP_DATASAFED_BIN_PATH" -# function change_path takes one argument, the backup name, and changes the DATASAFED_BACKEND_BASE_PATH +# function change_path changes the DATASAFED_BACKEND_BASE_PATH by backup name function change_path() { backup_name=$1 export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${backup_name}/${DP_TARGET_RELATIVE_PATH}" } +# function download_file downloads a backup file to a local target directory +function download_file() { + backup_name=$1 + local_target_dir=$2 + mkdir -p ${local_target_dir} && cd ${local_target_dir} + change_path "${backup_name}" + datasafed pull "${backup_name}.xbstream" - | xbstream -x + xtrabackup --decompress --remove-original --target-dir=${local_target_dir} +} + # 1. check base backup name if [[ -z ${DP_BASE_BACKUP_NAME} ]]; then echo "DP_BASE_BACKUP_NAME is empty" @@ -19,10 +29,7 @@ fi # download base backup file mkdir -p ${DATA_DIR} BASE_DIR=${DATA_MOUNT_DIR}/base -mkdir -p ${BASE_DIR} && cd ${BASE_DIR} -change_path "${DP_BASE_BACKUP_NAME}" -datasafed pull "${DP_BASE_BACKUP_NAME}.xbstream" - | xbstream -x -xtrabackup --decompress --remove-original --target-dir=${BASE_DIR} +download_file "${DP_BASE_BACKUP_NAME}" "${BASE_DIR}" # download parent backup files if [ -n "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES}" ]; then read -r -a ANCESTOR_INCREMENTAL_BACKUP_NAMES <<< "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES//,/ }" @@ -30,16 +37,10 @@ fi INCS_DIR=${DATA_MOUNT_DIR}/incs mkdir -p ${INCS_DIR} for parent_name in "${ANCESTOR_INCREMENTAL_BACKUP_NAMES[@]}"; do - change_path "${parent_name}" - mkdir -p ${INCS_DIR}/${parent_name} && cd ${INCS_DIR}/${parent_name} - datasafed pull "${parent_name}.xbstream" - | xbstream -x - xtrabackup --decompress --remove-original --target-dir=${INCS_DIR}/${parent_name} + download_file "${parent_name}" "${INCS_DIR}/${parent_name}" done # download target backup file -change_path "${DP_BACKUP_NAME}" -mkdir -p ${INCS_DIR}/${DP_BACKUP_NAME} && cd ${INCS_DIR}/${DP_BACKUP_NAME} -datasafed pull "${DP_BACKUP_NAME}.xbstream" - | xbstream -x -xtrabackup --decompress --remove-original --target-dir=${INCS_DIR}/${DP_BACKUP_NAME} +download_file "${DP_BACKUP_NAME}" "${INCS_DIR}/${DP_BACKUP_NAME}" old_signal="apecloud-mysql.old" log_bin=${LOG_BIN} From 2125d74f0147521ab2e3a76256a6fee3c0f2bce1 Mon Sep 17 00:00:00 2001 From: gnolong <2391353625@qq.com> Date: Mon, 6 Jan 2025 14:14:41 +0800 Subject: [PATCH 5/7] community mysql inc backup --- .../xtrabackup-incremental-backup.sh | 5 +- .../xtrabackup-incremental-restore.sh | 16 ++--- .../xtrabackup-incremental-backup.sh | 56 ++++++++++++++++ .../xtrabackup-incremental-restore.sh | 66 +++++++++++++++++++ .../templates/actionset-xtrabackup-inc.yaml | 46 +++++++++++++ .../mysql/templates/backuppolicytemplate.yaml | 25 +++++++ 6 files changed, 205 insertions(+), 9 deletions(-) create mode 100644 addons/mysql/dataprotection/xtrabackup-incremental-backup.sh create mode 100644 addons/mysql/dataprotection/xtrabackup-incremental-restore.sh create mode 100644 addons/mysql/templates/actionset-xtrabackup-inc.yaml diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh index 51a49487d..ca86303ed 100644 --- a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh @@ -26,12 +26,15 @@ fi mkdir -p ${DATA_DIR} PARENT_DIR=${DATA_MOUNT_DIR}/parent mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} +# set the datasafed backend base path for the parent backup export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" datasafed pull "${DP_PARENT_BACKUP_NAME}.xbstream" - | xbstream -x xtrabackup --decompress --remove-original --target-dir=${PARENT_DIR} -# compatible with version 0.6 +# set the datasafed backend base path for the current backup +# it is equal to ${DP_BACKUP_ROOT_PATH}/${DP_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH} export DATASAFED_BACKEND_BASE_PATH="$DP_BACKUP_BASE_PATH" +# compatible with version 0.6 if [ -f ${DATA_DIR}/mysql-bin.index ]; then echo "" | datasafed push - "apecloud-mysql.old" fi diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh index c704243ed..64f05f0c1 100644 --- a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh @@ -3,18 +3,18 @@ set -e set -o pipefail export PATH="$PATH:$DP_DATASAFED_BIN_PATH" -# function change_path changes the DATASAFED_BACKEND_BASE_PATH by backup name -function change_path() { +# function change_backend_path changes the DATASAFED_BACKEND_BASE_PATH by backup name +function change_backend_path() { backup_name=$1 export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${backup_name}/${DP_TARGET_RELATIVE_PATH}" } -# function download_file downloads a backup file to a local target directory -function download_file() { +# function download_backup_file downloads a backup file to a local target directory +function download_backup_file() { backup_name=$1 local_target_dir=$2 mkdir -p ${local_target_dir} && cd ${local_target_dir} - change_path "${backup_name}" + change_backend_path "${backup_name}" datasafed pull "${backup_name}.xbstream" - | xbstream -x xtrabackup --decompress --remove-original --target-dir=${local_target_dir} } @@ -29,7 +29,7 @@ fi # download base backup file mkdir -p ${DATA_DIR} BASE_DIR=${DATA_MOUNT_DIR}/base -download_file "${DP_BASE_BACKUP_NAME}" "${BASE_DIR}" +download_backup_file "${DP_BASE_BACKUP_NAME}" "${BASE_DIR}" # download parent backup files if [ -n "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES}" ]; then read -r -a ANCESTOR_INCREMENTAL_BACKUP_NAMES <<< "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES//,/ }" @@ -37,10 +37,10 @@ fi INCS_DIR=${DATA_MOUNT_DIR}/incs mkdir -p ${INCS_DIR} for parent_name in "${ANCESTOR_INCREMENTAL_BACKUP_NAMES[@]}"; do - download_file "${parent_name}" "${INCS_DIR}/${parent_name}" + download_backup_file "${parent_name}" "${INCS_DIR}/${parent_name}" done # download target backup file -download_file "${DP_BACKUP_NAME}" "${INCS_DIR}/${DP_BACKUP_NAME}" +download_backup_file "${DP_BACKUP_NAME}" "${INCS_DIR}/${DP_BACKUP_NAME}" old_signal="apecloud-mysql.old" log_bin=${LOG_BIN} diff --git a/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh b/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh new file mode 100644 index 000000000..084266bfa --- /dev/null +++ b/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -e +set -o pipefail + +# if the script exits with a non-zero exit code, touch a file to indicate that the backup failed, +# the sync progress container will check this file and exit if it exists +function handle_exit() { + exit_code=$? + if [ $exit_code -ne 0 ]; then + echo "failed with exit code $exit_code" + touch "${DP_BACKUP_INFO_FILE}.exit" + exit 1 + fi +} +trap handle_exit EXIT + +export PATH="$PATH:$DP_DATASAFED_BIN_PATH" + +# 1. check parent backup name +if [[ -z ${DP_PARENT_BACKUP_NAME} ]]; then + echo "DP_PARENT_BACKUP_NAME is empty" + exit 1 +fi + +# 2. get parent backup +mkdir -p ${DATA_DIR} +PARENT_DIR=${DATA_MOUNT_DIR}/parent +mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} +# set the datasafed backend base path for the parent backup +export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" +xbstreamFile="${DP_PARENT_BACKUP_NAME}.xbstream.zst" +if [ "$(datasafed list ${xbstreamFile})" == "${xbstreamFile}" ]; then + datasafed pull -d zstd-fastest "${xbstreamFile}" - | xbstream -x +else + datasafed pull "${DP_PARENT_BACKUP_NAME}.xbstream" - | xbstream -x +fi +xtrabackup --decompress --remove-original --target-dir=${PARENT_DIR} + +# set the datasafed backend base path for the current backup +# it is equal to ${DP_BACKUP_ROOT_PATH}/${DP_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH} +export DATASAFED_BACKEND_BASE_PATH="$DP_BACKUP_BASE_PATH" + +# compatible with version 2.4 +lock_per_table_ddl="" +if [ "${IMAGE_TAG}" == "2.4" ]; then + lock_per_table_ddl="--lock-ddl-per-table" +fi + +# 3. do incremental xtrabackup +xtrabackup --backup --safe-slave-backup --slave-info ${lock_per_table_ddl} --stream=xbstream \ + --host=${DP_DB_HOST} --port=${DP_DB_PORT} \ + --user=${DP_DB_USER} --password=${DP_DB_PASSWORD} \ + --datadir=${DATA_DIR} --incremental-basedir=${PARENT_DIR} | datasafed push -z zstd-fastest - "/${DP_BACKUP_NAME}.xbstream.zst" +TOTAL_SIZE=$(datasafed stat / | grep TotalSize | awk '{print $2}') +echo "{\"totalSize\":\"$TOTAL_SIZE\"}" >"${DP_BACKUP_INFO_FILE}" +rm -rf ${PARENT_DIR} diff --git a/addons/mysql/dataprotection/xtrabackup-incremental-restore.sh b/addons/mysql/dataprotection/xtrabackup-incremental-restore.sh new file mode 100644 index 000000000..d2ca33a03 --- /dev/null +++ b/addons/mysql/dataprotection/xtrabackup-incremental-restore.sh @@ -0,0 +1,66 @@ +#!/bin/bash +set -e +set -o pipefail +export PATH="$PATH:$DP_DATASAFED_BIN_PATH" + +# function change_backend_path changes the DATASAFED_BACKEND_BASE_PATH by backup name +function change_backend_path() { + backup_name=$1 + export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${backup_name}/${DP_TARGET_RELATIVE_PATH}" +} + +# function download_backup_file downloads a backup file to a local target directory +function download_backup_file() { + backup_name=$1 + local_target_dir=$2 + mkdir -p ${local_target_dir} && cd ${local_target_dir} + change_backend_path "${backup_name}" + xbstreamFile="${backup_name}.xbstream.zst" + if [ "$(datasafed list ${xbstreamFile})" == "${xbstreamFile}" ]; then + datasafed pull -d zstd-fastest "${xbstreamFile}" - | xbstream -x + else + datasafed pull "${backup_name}.xbstream" - | xbstream -x + fi + xtrabackup --decompress --remove-original --target-dir=${local_target_dir} +} + +# 1. check base backup name +if [[ -z ${DP_BASE_BACKUP_NAME} ]]; then + echo "DP_BASE_BACKUP_NAME is empty" + exit 1 +fi + +# 2. download backup files +# download base backup file +mkdir -p ${DATA_DIR} +BASE_DIR=${DATA_MOUNT_DIR}/base +download_backup_file "${DP_BASE_BACKUP_NAME}" "${BASE_DIR}" +# download parent backup files +if [ -n "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES}" ]; then + read -r -a ANCESTOR_INCREMENTAL_BACKUP_NAMES <<< "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES//,/ }" +fi +INCS_DIR=${DATA_MOUNT_DIR}/incs +mkdir -p ${INCS_DIR} +for parent_name in "${ANCESTOR_INCREMENTAL_BACKUP_NAMES[@]}"; do + download_backup_file "${parent_name}" "${INCS_DIR}/${parent_name}" +done +# download target backup file +download_backup_file "${DP_BACKUP_NAME}" "${INCS_DIR}/${DP_BACKUP_NAME}" + +# 3. prepare data +xtrabackup --prepare --apply-log-only --target-dir=${BASE_DIR} +for parent_name in "${ANCESTOR_INCREMENTAL_BACKUP_NAMES[@]}"; do + xtrabackup --prepare --apply-log-only --target-dir=${BASE_DIR} --incremental-dir=${INCS_DIR}/${parent_name} +done +xtrabackup --prepare --target-dir=${BASE_DIR} --incremental-dir=${INCS_DIR}/${DP_BACKUP_NAME} + +# 4. restore +xtrabackup --move-back --target-dir=${BASE_DIR} --datadir=${DATA_DIR}/ --log-bin=${log_bin} +touch ${DATA_DIR}/.xtrabackup_restore +if [ "${BACKUP_FOR_STANDBY}" != "true" ]; then + touch ${DATA_DIR}/.restore_new_cluster +fi +rm -rf ${BASE_DIR} +rm -rf ${INCS_DIR} +chmod -R 0777 ${DATA_DIR} +echo "Restore completed!" diff --git a/addons/mysql/templates/actionset-xtrabackup-inc.yaml b/addons/mysql/templates/actionset-xtrabackup-inc.yaml new file mode 100644 index 000000000..ec3522143 --- /dev/null +++ b/addons/mysql/templates/actionset-xtrabackup-inc.yaml @@ -0,0 +1,46 @@ +apiVersion: dataprotection.kubeblocks.io/v1alpha1 +kind: ActionSet +metadata: + name: mysql-xtrabackup-inc-br + labels: + {{- include "mysql.labels" . | nindent 4 }} +spec: + backupType: Incremental + env: + - name: DATA_DIR + value: "{{ .Values.dataMountPath }}/data" + - name: MYSQL_DIR + value: {{ .Values.dataMountPath }} + - name: IMAGE_TAG + value: "8.0" + - name: BACKUP_FOR_STANDBY + value: "false" + backup: + preBackup: [] + postBackup: [] + backupData: + image: {{ .Values.image.xtraBackup.registry | default ( .Values.image.registry | default "docker.io" ) }}/{{ .Values.image.xtraBackup.repository }}:$(IMAGE_TAG) + runOnTargetPodNode: true + command: + - bash + - -c + - | + {{- .Files.Get "dataprotection/xtrabackup-incremental-backup.sh" | nindent 8 }} + syncProgress: + enabled: true + intervalSeconds: 5 + restore: + prepareData: + image: {{ .Values.image.xtraBackup.registry | default ( .Values.image.registry | default "docker.io" ) }}/{{ .Values.image.xtraBackup.repository }}:$(IMAGE_TAG) + command: + - bash + - -c + - | + {{- .Files.Get "dataprotection/xtrabackup-incremental-restore.sh" | nindent 8 }} + postReady: + - exec: + command: + - sh + - -c + - | + rm -rf {{ .Values.dataMountPath }}/data/.restore_new_cluster \ No newline at end of file diff --git a/addons/mysql/templates/backuppolicytemplate.yaml b/addons/mysql/templates/backuppolicytemplate.yaml index ce5526d34..6b3990fc0 100644 --- a/addons/mysql/templates/backuppolicytemplate.yaml +++ b/addons/mysql/templates/backuppolicytemplate.yaml @@ -32,6 +32,27 @@ spec: volumeMounts: - name: data mountPath: {{ .Values.dataMountPath }} + - name: xtrabackup-inc + compatibleMethod: xtrabackup + snapshotVolumes: false + actionSetName: mysql-xtrabackup-inc-br + env: + - name: IMAGE_TAG + valueFrom: + versionMapping: + - serviceVersions: + - "8.4" + mappedValue: "8.4" + - serviceVersions: + - "8.0" + mappedValue: "8.0" + - serviceVersions: + - "5.7" + mappedValue: "2.4" + targetVolumes: + volumeMounts: + - name: data + mountPath: {{ .Values.dataMountPath }} - name: volume-snapshot snapshotVolumes: true actionSetName: mysql-volume-snapshot @@ -48,6 +69,10 @@ spec: enabled: false cronExpression: "0 18 * * 0" retentionPeriod: 7d + - backupMethod: xtrabackup-inc + enabled: false + cronExpression: "0 19 * * 0" + retentionPeriod: 7d - backupMethod: volume-snapshot enabled: false cronExpression: "0 18 * * 0" From 453107ad5668924f85bf96b912fc1040bf226d82 Mon Sep 17 00:00:00 2001 From: gnolong <2391353625@qq.com> Date: Mon, 6 Jan 2025 15:16:46 +0800 Subject: [PATCH 6/7] based on comment --- .../dataprotection/xtrabackup-incremental-backup.sh | 3 ++- .../dataprotection/xtrabackup-incremental-restore.sh | 4 ++-- addons/mysql/dataprotection/xtrabackup-incremental-backup.sh | 3 ++- addons/mysql/dataprotection/xtrabackup-incremental-restore.sh | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh index ca86303ed..5a15800fd 100644 --- a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh @@ -24,7 +24,8 @@ fi # 2. get parent backup mkdir -p ${DATA_DIR} -PARENT_DIR=${DATA_MOUNT_DIR}/parent +PARENT_DIR=${DATA_MOUNT_DIR}/xtrabackup-parent +rm -rf ${PARENT_DIR} mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} # set the datasafed backend base path for the parent backup export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh index 64f05f0c1..bbcdc7bc1 100644 --- a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-restore.sh @@ -28,13 +28,13 @@ fi # 2. download backup files # download base backup file mkdir -p ${DATA_DIR} -BASE_DIR=${DATA_MOUNT_DIR}/base +BASE_DIR=${DATA_MOUNT_DIR}/xtrabackup-base download_backup_file "${DP_BASE_BACKUP_NAME}" "${BASE_DIR}" # download parent backup files if [ -n "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES}" ]; then read -r -a ANCESTOR_INCREMENTAL_BACKUP_NAMES <<< "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES//,/ }" fi -INCS_DIR=${DATA_MOUNT_DIR}/incs +INCS_DIR=${DATA_MOUNT_DIR}/xtrabackup-incs mkdir -p ${INCS_DIR} for parent_name in "${ANCESTOR_INCREMENTAL_BACKUP_NAMES[@]}"; do download_backup_file "${parent_name}" "${INCS_DIR}/${parent_name}" diff --git a/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh b/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh index 084266bfa..e0003fb6b 100644 --- a/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh +++ b/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh @@ -24,7 +24,8 @@ fi # 2. get parent backup mkdir -p ${DATA_DIR} -PARENT_DIR=${DATA_MOUNT_DIR}/parent +PARENT_DIR=${MYSQL_DIR}/xtrabackup-parent +rm -rf ${PARENT_DIR} mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} # set the datasafed backend base path for the parent backup export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" diff --git a/addons/mysql/dataprotection/xtrabackup-incremental-restore.sh b/addons/mysql/dataprotection/xtrabackup-incremental-restore.sh index d2ca33a03..f42b5a17c 100644 --- a/addons/mysql/dataprotection/xtrabackup-incremental-restore.sh +++ b/addons/mysql/dataprotection/xtrabackup-incremental-restore.sh @@ -33,13 +33,13 @@ fi # 2. download backup files # download base backup file mkdir -p ${DATA_DIR} -BASE_DIR=${DATA_MOUNT_DIR}/base +BASE_DIR=${MYSQL_DIR}/xtrabackup-base download_backup_file "${DP_BASE_BACKUP_NAME}" "${BASE_DIR}" # download parent backup files if [ -n "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES}" ]; then read -r -a ANCESTOR_INCREMENTAL_BACKUP_NAMES <<< "${DP_ANCESTOR_INCREMENTAL_BACKUP_NAMES//,/ }" fi -INCS_DIR=${DATA_MOUNT_DIR}/incs +INCS_DIR=${MYSQL_DIR}/xtrabackup-incs mkdir -p ${INCS_DIR} for parent_name in "${ANCESTOR_INCREMENTAL_BACKUP_NAMES[@]}"; do download_backup_file "${parent_name}" "${INCS_DIR}/${parent_name}" From 0a706cebc3fb264fb2d2caa73e1f5c733010a593 Mon Sep 17 00:00:00 2001 From: gnolong <2391353625@qq.com> Date: Wed, 8 Jan 2025 15:24:10 +0800 Subject: [PATCH 7/7] use lsn --- .../apecloud-mysql/dataprotection/backup.sh | 13 ++++- .../xtrabackup-incremental-backup.sh | 50 ++++++++++++++--- .../templates/actionset-incremental.yaml | 2 +- .../apecloud-mysql/templates/actionset.yaml | 2 +- addons/mysql/dataprotection/backup.sh | 12 +++- .../xtrabackup-incremental-backup.sh | 56 +++++++++++++++---- .../mysql/templates/backuppolicytemplate.yaml | 2 +- 7 files changed, 112 insertions(+), 25 deletions(-) diff --git a/addons/apecloud-mysql/dataprotection/backup.sh b/addons/apecloud-mysql/dataprotection/backup.sh index deed4e9c7..bea88b1d3 100644 --- a/addons/apecloud-mysql/dataprotection/backup.sh +++ b/addons/apecloud-mysql/dataprotection/backup.sh @@ -22,11 +22,20 @@ if [ -f ${DATA_DIR}/mysql-bin.index ]; then echo "" | datasafed push - "apecloud-mysql.old" fi # do xtrabackup +TMP_DIR=${DATA_MOUNT_DIR}/xtrabackup-temp +mkdir -p ${TMP_DIR} START_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ') xtrabackup --compress=zstd --backup --safe-slave-backup --slave-info --stream=xbstream \ --host=${DP_DB_HOST} --port=${DP_DB_PORT} \ - --user=${DP_DB_USER} --password=${DP_DB_PASSWORD} \ - --datadir=${DATA_DIR} | datasafed push - "/${DP_BACKUP_NAME}.xbstream" + --user=${DP_DB_USER} --password=${DP_DB_PASSWORD} --datadir=${DATA_DIR} \ + 2> >(tee ${TMP_DIR}/xtrabackup.log >&2) \ + | datasafed push - "/${DP_BACKUP_NAME}.xbstream" +# record lsn for incremental backups +cat "${TMP_DIR}/xtrabackup.log" \ + | grep "The latest check point (for incremental)" \ + | awk -F"'" '{print $2}' \ + | datasafed push - "/${DP_BACKUP_NAME}.lsn" STOP_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ') TOTAL_SIZE=$(datasafed stat / | grep TotalSize | awk '{print $2}') echo "{\"totalSize\":\"$TOTAL_SIZE\",\"timeRange\":{\"start\":\"${START_TIME}\",\"end\":\"${STOP_TIME}\"}}" >"${DP_BACKUP_INFO_FILE}" +rm -rf ${TMP_DIR} diff --git a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh index 5a15800fd..b22dc958b 100644 --- a/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh +++ b/addons/apecloud-mysql/dataprotection/xtrabackup-incremental-backup.sh @@ -22,15 +22,39 @@ if [[ -z ${DP_PARENT_BACKUP_NAME} ]]; then exit 1 fi -# 2. get parent backup +# 2. get parent backup lsn or download backup file mkdir -p ${DATA_DIR} PARENT_DIR=${DATA_MOUNT_DIR}/xtrabackup-parent -rm -rf ${PARENT_DIR} -mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} -# set the datasafed backend base path for the parent backup -export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" -datasafed pull "${DP_PARENT_BACKUP_NAME}.xbstream" - | xbstream -x -xtrabackup --decompress --remove-original --target-dir=${PARENT_DIR} + +# function get_last_lsn gets parent backup lsn from datasafed +function get_last_lsn() { + export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" + lsnFile="${DP_PARENT_BACKUP_NAME}.lsn" + if [ "$(datasafed list ${lsnFile})" == "${lsnFile}" ]; then + echo "$(datasafed pull "/${lsnFile}" - | awk '{print $1}')" + fi +} + +# function download_parent_backup_file downloads the parent backup file from datasafed +function download_parent_backup_file() { + rm -rf ${PARENT_DIR} + mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} + export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" + datasafed pull "${DP_PARENT_BACKUP_NAME}.xbstream" - | xbstream -x + xtrabackup --decompress --remove-original --target-dir=${PARENT_DIR} +} + +# build incremental cmd +incremental_cmd="" +last_lsn=$(get_last_lsn) +if [ -n "${last_lsn}" ]; then + incremental_cmd="--incremental-lsn=${last_lsn}" + echo "create incremental backup based on parent backup lsn" +else + download_parent_backup_file + incremental_cmd="--incremental-basedir=${PARENT_DIR}" + echo "create incremental backup based on parent backup file" +fi # set the datasafed backend base path for the current backup # it is equal to ${DP_BACKUP_ROOT_PATH}/${DP_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH} @@ -41,12 +65,22 @@ if [ -f ${DATA_DIR}/mysql-bin.index ]; then fi # 3. do incremental xtrabackup +TMP_DIR=${DATA_MOUNT_DIR}/xtrabackup-temp +mkdir -p ${TMP_DIR} START_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ') xtrabackup --compress=zstd --backup --safe-slave-backup --slave-info --stream=xbstream \ --host=${DP_DB_HOST} --port=${DP_DB_PORT} \ --user=${DP_DB_USER} --password=${DP_DB_PASSWORD} \ - --datadir=${DATA_DIR} --incremental-basedir=${PARENT_DIR} | datasafed push - "/${DP_BACKUP_NAME}.xbstream" + --datadir=${DATA_DIR} ${incremental_cmd} \ + 2> >(tee ${TMP_DIR}/xtrabackup.log >&2) \ + | datasafed push - "/${DP_BACKUP_NAME}.xbstream" +# record lsn for incremental backups +cat "${TMP_DIR}/xtrabackup.log" \ + | grep "The latest check point (for incremental)" \ + | awk -F"'" '{print $2}' \ + | datasafed push - "/${DP_BACKUP_NAME}.lsn" STOP_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ') TOTAL_SIZE=$(datasafed stat / | grep TotalSize | awk '{print $2}') echo "{\"totalSize\":\"$TOTAL_SIZE\",\"timeRange\":{\"start\":\"${START_TIME}\",\"end\":\"${STOP_TIME}\"}}" >"${DP_BACKUP_INFO_FILE}" rm -rf ${PARENT_DIR} +rm -rf ${TMP_DIR} diff --git a/addons/apecloud-mysql/templates/actionset-incremental.yaml b/addons/apecloud-mysql/templates/actionset-incremental.yaml index 22ff51dcf..9055ccbc2 100644 --- a/addons/apecloud-mysql/templates/actionset-incremental.yaml +++ b/addons/apecloud-mysql/templates/actionset-incremental.yaml @@ -24,7 +24,7 @@ spec: image: {{ include "apecloud-mysql.bakcupToolImage" . }} runOnTargetPodNode: true command: - - sh + - bash - -c - | {{- .Files.Get "dataprotection/xtrabackup-incremental-backup.sh" | nindent 8 }} diff --git a/addons/apecloud-mysql/templates/actionset.yaml b/addons/apecloud-mysql/templates/actionset.yaml index a954bda12..cfb4fcc99 100644 --- a/addons/apecloud-mysql/templates/actionset.yaml +++ b/addons/apecloud-mysql/templates/actionset.yaml @@ -24,7 +24,7 @@ spec: image: {{ include "apecloud-mysql.bakcupToolImage" . }} runOnTargetPodNode: true command: - - sh + - bash - -c - | {{- .Files.Get "dataprotection/backup.sh" | nindent 8 }} diff --git a/addons/mysql/dataprotection/backup.sh b/addons/mysql/dataprotection/backup.sh index bdf067aa6..36bb4ce31 100644 --- a/addons/mysql/dataprotection/backup.sh +++ b/addons/mysql/dataprotection/backup.sh @@ -21,7 +21,17 @@ if [ "${IMAGE_TAG}" == "2.4" ]; then lock_per_table_ddl="--lock-ddl-per-table" fi +TMP_DIR=${MYSQL_DIR}/xtrabackup-temp +mkdir -p ${TMP_DIR} xtrabackup --backup --safe-slave-backup --slave-info ${lock_per_table_ddl} --stream=xbstream \ - --host=${DP_DB_HOST} --user=${DP_DB_USER} --password=${DP_DB_PASSWORD} --datadir=${DATA_DIR} | datasafed push -z zstd-fastest - "/${DP_BACKUP_NAME}.xbstream.zst" + --host=${DP_DB_HOST} --user=${DP_DB_USER} --password=${DP_DB_PASSWORD} --datadir=${DATA_DIR} \ + 2> >(tee ${TMP_DIR}/xtrabackup.log >&2) \ + | datasafed push -z zstd-fastest - "/${DP_BACKUP_NAME}.xbstream.zst" +# record lsn for incremental backups +cat "${TMP_DIR}/xtrabackup.log" \ + | grep "The latest check point (for incremental)" \ + | awk -F"'" '{print $2}' \ + | datasafed push - "/${DP_BACKUP_NAME}.lsn" TOTAL_SIZE=$(datasafed stat / | grep TotalSize | awk '{print $2}') echo "{\"totalSize\":\"$TOTAL_SIZE\"}" >"${DP_BACKUP_INFO_FILE}" +rm -rf ${TMP_DIR} diff --git a/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh b/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh index e0003fb6b..3370243ca 100644 --- a/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh +++ b/addons/mysql/dataprotection/xtrabackup-incremental-backup.sh @@ -22,20 +22,44 @@ if [[ -z ${DP_PARENT_BACKUP_NAME} ]]; then exit 1 fi -# 2. get parent backup +# 2. get parent backup lsn or download backup file mkdir -p ${DATA_DIR} PARENT_DIR=${MYSQL_DIR}/xtrabackup-parent -rm -rf ${PARENT_DIR} -mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} -# set the datasafed backend base path for the parent backup -export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" -xbstreamFile="${DP_PARENT_BACKUP_NAME}.xbstream.zst" -if [ "$(datasafed list ${xbstreamFile})" == "${xbstreamFile}" ]; then - datasafed pull -d zstd-fastest "${xbstreamFile}" - | xbstream -x + +# function get_last_lsn gets parent backup lsn from datasafed +function get_last_lsn() { + export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" + lsnFile="${DP_PARENT_BACKUP_NAME}.lsn" + if [ "$(datasafed list ${lsnFile})" == "${lsnFile}" ]; then + echo "$(datasafed pull "/${lsnFile}" - | awk '{print $1}')" + fi +} + +# function download_parent_backup_file downloads the parent backup file from datasafed +function download_parent_backup_file() { + rm -rf ${PARENT_DIR} + mkdir -p ${PARENT_DIR} && cd ${PARENT_DIR} + export DATASAFED_BACKEND_BASE_PATH="${DP_BACKUP_ROOT_PATH}/${DP_PARENT_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH}" + xbstreamFile="${DP_PARENT_BACKUP_NAME}.xbstream.zst" + if [ "$(datasafed list ${xbstreamFile})" == "${xbstreamFile}" ]; then + datasafed pull -d zstd-fastest "${xbstreamFile}" - | xbstream -x + else + datasafed pull "${DP_PARENT_BACKUP_NAME}.xbstream" - | xbstream -x + fi + xtrabackup --decompress --remove-original --target-dir=${PARENT_DIR} +} + +# build incremental cmd +incremental_cmd="" +last_lsn=$(get_last_lsn) +if [ -n "${last_lsn}" ]; then + incremental_cmd="--incremental-lsn=${last_lsn}" + echo "create incremental backup based on parent backup lsn" else - datasafed pull "${DP_PARENT_BACKUP_NAME}.xbstream" - | xbstream -x + download_parent_backup_file + incremental_cmd="--incremental-basedir=${PARENT_DIR}" + echo "create incremental backup based on parent backup file" fi -xtrabackup --decompress --remove-original --target-dir=${PARENT_DIR} # set the datasafed backend base path for the current backup # it is equal to ${DP_BACKUP_ROOT_PATH}/${DP_BACKUP_NAME}/${DP_TARGET_RELATIVE_PATH} @@ -48,10 +72,20 @@ if [ "${IMAGE_TAG}" == "2.4" ]; then fi # 3. do incremental xtrabackup +TMP_DIR=${MYSQL_DIR}/xtrabackup-temp +mkdir -p ${TMP_DIR} xtrabackup --backup --safe-slave-backup --slave-info ${lock_per_table_ddl} --stream=xbstream \ --host=${DP_DB_HOST} --port=${DP_DB_PORT} \ --user=${DP_DB_USER} --password=${DP_DB_PASSWORD} \ - --datadir=${DATA_DIR} --incremental-basedir=${PARENT_DIR} | datasafed push -z zstd-fastest - "/${DP_BACKUP_NAME}.xbstream.zst" + --datadir=${DATA_DIR} ${incremental_cmd} \ + 2> >(tee ${TMP_DIR}/xtrabackup.log >&2) \ + | datasafed push -z zstd-fastest - "/${DP_BACKUP_NAME}.xbstream.zst" +# record lsn for incremental backups +cat "${TMP_DIR}/xtrabackup.log" \ + | grep "The latest check point (for incremental)" \ + | awk -F"'" '{print $2}' \ + | datasafed push - "/${DP_BACKUP_NAME}.lsn" TOTAL_SIZE=$(datasafed stat / | grep TotalSize | awk '{print $2}') echo "{\"totalSize\":\"$TOTAL_SIZE\"}" >"${DP_BACKUP_INFO_FILE}" rm -rf ${PARENT_DIR} +rm -rf ${TMP_DIR} diff --git a/addons/mysql/templates/backuppolicytemplate.yaml b/addons/mysql/templates/backuppolicytemplate.yaml index 6b3990fc0..13d3979cf 100644 --- a/addons/mysql/templates/backuppolicytemplate.yaml +++ b/addons/mysql/templates/backuppolicytemplate.yaml @@ -71,7 +71,7 @@ spec: retentionPeriod: 7d - backupMethod: xtrabackup-inc enabled: false - cronExpression: "0 19 * * 0" + cronExpression: "0 19 * * *" retentionPeriod: 7d - backupMethod: volume-snapshot enabled: false