設定應用程式一致性備份

本文說明如何為 Compute Engine Linux 虛擬機器 (VM) 執行個體上執行的自行管理資料庫,設定排定的應用程式一致備份和時間點復原 (PITR) 工作流程。

備份有狀態資料庫工作負載 (例如 IBM Db2、SAP HANA、MySQL 或 PostgreSQL) 時,備份和災難復原擷取的標準崩潰一致性快照,可在交易仍在進行時擷取資料庫。這些不完整的交易可能會損毀資料、導致資料表狀態不一致,並在當機復原時需要冗長的資料庫修復程序。應用程式一致備份可解決這些問題,方法是與 VM 訪客代理程式整合,在建立快照前,先靜止資料庫,並視需要凍結檔案系統。

事前準備

設定應用程式一致備份前,請先完成下列工作。

必要的角色

如要取得設定應用程式一致備份所需的權限,請要求管理員在專案中授予您下列 IAM 角色:

如要進一步瞭解如何授予角色,請參閱「管理專案、資料夾和組織的存取權」。

您或許也能透過自訂角色或其他預先定義的角色,取得必要權限。

必要條件

  1. 在 Google Cloud 控制台的專案選擇器頁面中,選取或建立Google Cloud 專案。

    前往專案選取器

  2. 確認專案已啟用計費功能。

    瞭解如何確認已啟用計費功能

  3. 啟用備份和災難復原 API 和 Secret Manager API。

    啟用 API

  4. 請確認資料庫資料和記錄檔儲存在標準永久磁碟。Google Cloud Hyperdisk 磁碟區不支援客體清除作業。

  5. 如果您打算設定時間點復原功能,請將資料庫設為將交易和重做日誌檔封存至專屬的獨立永久磁碟,例如掛接在 /db2/ACT/log_archive。

使用自備指令碼 (BYOS) 訪客清除架構

備份和災難復原會使用自備指令碼 (BYOS) 客體清除架構,在建立快照前暫停資料庫。訪客環境仰賴 Linux VM 執行個體上 /etc/google/snapshots/ 目錄中的兩個指令碼:

  • 快照前指令碼 (/etc/google/snapshots/pre.sh):在建立快照前執行。這個指令碼必須停止資料庫作業,並可選擇凍結檔案系統。
  • 快照後指令碼 (/etc/google/snapshots/post.sh):在建立快照後執行。這個指令碼必須視需要解除凍結檔案系統,並繼續執行資料庫作業。

如要進一步瞭解資料庫專屬指令碼範本,請參閱「依資料庫類型查看指令碼範例」。

選用:凍結及取消凍結檔案系統

您可以使用標準 Linux 公用程式 fsfreeze 凍結檔案系統,但這並非必要操作。將資料庫靜止,可確保應用程式層級的交易一致性。不過,如果您需要額外的檔案系統層級一致性,可以視需要在指令碼中加入下列檔案系統凍結和解凍指令。

請確認 VM 上的 pre.sh 和 post.sh 指令碼已設定為處理記錄磁碟區的選用檔案系統凍結,不會暫停資料庫作業。

將檔案系統凍結指令新增至 pre.sh

如果您選擇凍結檔案系統,請在靜止資料庫後,於 pre.sh 指令碼結尾新增下列探索和凍結邏輯:

# Discover local writeable mount points
MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

# FREEZE FILESYSTEMS (Optional)
for mnt in $MOUNT_POINTS; do
    if ! sudo fsfreeze -f "$mnt"; then
        logger "Error: Failed to freeze $mnt. Cleaning up..."
        # Attempt emergency thaw
        for thaw_mnt in $MOUNT_POINTS; do sudo fsfreeze -u "$thaw_mnt" 2>/dev/null; done
        # Add your database-specific resume/cleanup commands here before exiting
        exit 1
    fi
    logger "Filesystem $mnt is frozen."
done

在 post.sh 中新增檔案系統解除凍結指令

如果您在 pre.sh 中凍結檔案系統,請在 post.sh 指令碼開頭新增下列解除凍結邏輯,然後再繼續執行資料庫:

# Discover local writeable mount points
MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

# UNFREEZE FILESYSTEMS (Optional)
for mnt in $MOUNT_POINTS; do
    sudo fsfreeze -u "$mnt"
    logger "Filesystem $mnt is unfrozen."
done

使用 Secret Manager 動態擷取憑證

請避免在殼層指令碼中硬式編碼靜態資料庫憑證或密碼。請改用 Secret Manager,在執行階段動態擷取憑證。使用下列邏輯,替換指令碼中的靜態密碼變數:

# Fetch the database password from Secret Manager dynamically
DBPASSWORD=$(gcloud secrets versions access latest \
    --secret="SECRET_NAME" \
    --format='get(payload.data)' | tr -d '\n')

if [ $? -ne 0 ]; then
    logger "Critical Error: Unable to fetch the password from Secret Manager."
    exit 1
fi

將 SECRET_NAME 替換為 Secret Manager 中的 Secret 名稱。

依資料庫類型查看指令碼範例

下列範例提供 pre.sh 和 post.sh 實作,適用於在 Linux 執行個體上執行的支援資料庫引擎。

IBM Db2

快照前指令碼 (/etc/google/snapshots/pre.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/pre.sh
 # 1. Define Mount Points for the Database
 MOUNT_POINTS=("DATA_MOUNT_POINT" "LOG_MOUNT_POINT")
 logger "BYOS Framework: Starting pre-snapshot script."

 # 2. QUIESCE DATABASE (Generic Hook)
 # Replace with DB-specific command (e.g., 'db2 set write suspend')
 logger "Suspending Database I/O..."
 # [INSERT DB QUIESCE COMMAND HERE]

 # 3. FREEZE FILESYSTEMS (Optional)
 for mnt in "${MOUNT_POINTS[@]}"; do
     if ! sudo fsfreeze -f "$mnt"; then
         logger "Error: Failed to freeze $mnt."
         # Attempt emergency thaw
         for thaw_mnt in "${MOUNT_POINTS[@]}"; do sudo fsfreeze -u "$thaw_mnt" 2>/dev/null; done
         # [INSERT DB RESUME COMMAND HERE]
         exit 1
     fi
     logger "Filesystem $mnt is frozen."
 done

快照後指令碼 (/etc/google/snapshots/post.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/post.sh
 MOUNT_POINTS=("DATA_MOUNT_POINT" "LOG_MOUNT_POINT")
 logger "BYOS Framework: Starting post-snapshot script."

 # 1. UNFREEZE FILESYSTEMS (Optional)
 for mnt in "${MOUNT_POINTS[@]}"; do
     sudo fsfreeze -u "$mnt"
     logger "Filesystem $mnt is unfrozen."
 done

 # 2. RESUME DATABASE (Generic Hook)
 # Replace with DB-specific command (e.g., 'db2 set write resume')
 logger "Resuming Database I/O..."
 # [INSERT DB RESUME COMMAND HERE]
 logger "BYOS Framework: Cleanup complete."

Oracle

快照前指令碼 (/etc/google/snapshots/pre.sh)

 sudo mkdir -p /etc/google/snapshots
 sudo tee /etc/google/snapshots/pre.sh > /dev/null << 'EOF'
 #!/bin/bash
 # Place Oracle (ASM) into hot backup mode before Backup and DR captures disks
 su - oracle -c "sqlplus -s / as sysdba" << 'SQL'
 WHENEVER SQLERROR EXIT FAILURE;
 ALTER DATABASE BEGIN BACKUP;
 EXIT;
 SQL

 if [ $? -ne 0 ]; then
   echo "Error: Failed to place Oracle in backup mode. Aborting snapshot." >&2
   exit 1
 fi
 EOF

 sudo chmod 750 /etc/google/snapshots/pre.sh

快照後指令碼 (/etc/google/snapshots/post.sh)

 sudo tee /etc/google/snapshots/post.sh > /dev/null << 'EOF'
 #!/bin/bash
 # Release Oracle from backup mode and archive current redo log to +FRA
 su - oracle -c "sqlplus -s / as sysdba" << 'SQL'
 WHENEVER SQLERROR EXIT FAILURE;
 ALTER DATABASE END BACKUP;
 ALTER SYSTEM ARCHIVE LOG CURRENT;
 EXIT;
 SQL

 if [ $? -ne 0 ]; then
   echo "Warning: Failed to execute ALTER DATABASE END BACKUP." >&2
   exit 1
 fi
 EOF

 sudo chmod 750 /etc/google/snapshots/post.sh

SAP HANA

快照前指令碼 (/etc/google/snapshots/pre.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/pre.sh

 DISK_ARG="$1"

 # 1. Configuration & Discovery
 DBSID="DBSID"
 DBADM="DBADM"
 ID_FILE="/tmp/hana_snapshot_id_$DBSID"
 HDB_KEY="HDB_KEY"

 if [[ "$DISK_ARG" == "," ]] || [[ "$DISK_ARG" == "1/0" ]] || [[ "$DISK_ARG" == "sda" ]]; then
     # Dynamically discover all local writeable mount points for full VM image consistency
     # Filters out pseudo, temporary, and read-only filesystems
     MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

     logger "SAP HANA Full VM BYOS: Starting pre-snapshot script for $DBSID."

     # 2. QUIESCE DATABASE (Create SAP HANA Snapshot)
     logger "SAP HANA Full VM BYOS: Creating database snapshot..."

     # Determine HANA Version
     # Versions 2.0+ use 'FOR FULL SYSTEM' syntax
     HANAVERSION=$(su - $DBADM -c "HDB version" | grep "version:" | awk '{print $2}' | cut -d'.' -f1)

     if [ "$HANAVERSION" = "1" ]; then
         SQL="BACKUP DATA CREATE SNAPSHOT COMMENT 'SNAPSHOT_$(date +%Y%m%d)'"
     else
         SQL="BACKUP DATA FOR FULL SYSTEM CREATE SNAPSHOT COMMENT 'SNAPSHOT_$(date +%Y%m%d)'"
     fi

     # Execute the snapshot command via hdbsql
     su - $DBADM -c "hdbsql -j -U $HDB_KEY \"$SQL\""
     if [ $? -ne 0 ]; then
         logger "Error: Failed to create SAP HANA snapshot."
         exit 1
     fi

     # Retrieve the BACKUP_ID for the prepared snapshot
     ID=$(su - $DBADM -c "hdbsql -j -t -U $HDB_KEY \"SELECT BACKUP_ID FROM M_BACKUP_CATALOG WHERE STATE_NAME = 'prepared' ORDER BY SYS_START_TIME DESC\"" | head -n 2 | tr -d '"' | tail -n 1)

     if [ -z "$ID" ]; then
         logger "Error: Could not retrieve BACKUP_ID for prepared snapshot."
         exit 1
     fi

     echo "$ID" > "$ID_FILE"
     logger "SAP HANA Full VM BYOS: Database snapshot prepared with ID $ID."
 fi

快照後指令碼 (/etc/google/snapshots/post.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/post.sh

 DISK_ARG="$1"

 # 1. Configuration & Discovery
 DBSID="DBSID"
 DBADM="DBADM"
 ID_FILE="/tmp/hana_snapshot_id_$DBSID"
 HDB_KEY="HDB_KEY"

 if [[ "$DISK_ARG" == "," ]] || [[ "$DISK_ARG" == "1/0" ]] || [[ "$DISK_ARG" == "sda" ]]; then
     # Discover all local writeable mount points to perform unfreeze
     MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

     logger "SAP HANA Full VM BYOS: Starting post-snapshot script for $DBSID."

     # 3. RESUME DATABASE (Close SAP HANA Snapshot)
     if [ -f "$ID_FILE" ]; then
         ID=$(cat "$ID_FILE")
         logger "SAP HANA Full VM BYOS: Closing database snapshot ID $ID..."

         # Determine HANA version for correct syntax
         HANAVERSION=$(su - $DBADM -c "HDB version" | grep "version:" | awk '{print $2}' | cut -d'.' -f1)

         if [ "$HANAVERSION" = "1" ]; then
             SQL="BACKUP DATA CLOSE SNAPSHOT BACKUP_ID $ID SUCCESSFUL 'SNAPSHOT_COMPLETED'"
         else
             SQL="BACKUP DATA FOR FULL SYSTEM CLOSE SNAPSHOT BACKUP_ID $ID SUCCESSFUL 'SNAPSHOT_COMPLETED'"
         fi

         # Execute the close snapshot command
         RESULT=$(su - $DBADM -c "hdbsql -j -U $HDB_KEY \"$SQL\"")
         if [ $? -ne 0 ]; then
             logger "Error: Failed to close SAP HANA snapshot. $RESULT"
         else
             logger "SAP HANA Full VM BYOS: Database snapshot closed successfully."
         fi

         # Cleanup the temporary ID file
         rm -f "$ID_FILE"
     else
         logger "Error: Snapshot ID file not found. Manual intervention required to close HANA snapshot."
     fi

     logger "SAP HANA Full VM BYOS: Cleanup complete."

 else
     MOUNT_POINTS="/hanabackup"

     for mnt in $MOUNT_POINTS; do
         sudo fsfreeze -u "$mnt"
         logger "Filesystem $mnt is unfrozen."
     done
 fi

更改下列內容:

  • DBSID:大寫的 SAP HANA 系統 ID (SID)。
  • DBADM:SAP HANA 管理員 OS 使用者 (例如 <sid>adm)。
  • HDB_KEY:為資料庫存取權設定的 SAP HANA 使用者儲存庫金鑰。

SAP ASE

快照前指令碼 (/etc/google/snapshots/pre.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/pre.sh
 # 1. Configuration
 OSUSER="OS_USER"
 SYB_SERVER="SYB_SERVER_NAME"
 SYB_USER="SYB_USER"
 SYB_PASS="SYB_PASSWORD"
 SYB_DBLIST="DATABASE_LIST"
 TAG_NAME="SNAPSHOT_$(date +%Y%m%d)"
 MANIFEST_DIR="/var/tmp/sybase_manifests" # Directory for manifest files
 # 2. Discovery
 # Dynamically discover local writeable mount points
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

 logger "Sybase Full VM BYOS: Starting pre-snapshot script."
 # 3. QUIESCE DATABASE
 mkdir -p "$MANIFEST_DIR"
 chown $OSUSER "$MANIFEST_DIR"

 logger "Sybase Full VM BYOS: Quiescing databases: $SYB_DBLIST..."
 # Command logic from act_sybase_pre.sh
 # We use 'hold' to suspend activity and create the manifest file
 su - $OSUSER -c "isql -U$SYB_USER -P$SYB_PASS -S$SYB_SERVER -X --retserverror" <<EOF
 quiesce database $TAG_NAME hold $SYB_DBLIST for external dump to '$MANIFEST_DIR/manifest_$TAG_NAME.dat' with override
 go
 exit
 EOF
 if [ $? -ne 0 ]; then
     logger "Error: Failed to quiesce Sybase databases."
     exit 1
 fi
 # 4. FREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     if ! sudo fsfreeze -f "$mnt"; then
         logger "Error: Failed to freeze $mnt."
         # Attempt emergency thaw
         for thaw_mnt in $MOUNT_POINTS; do sudo fsfreeze -u "$thaw_mnt" 2>/dev/null; done
         # Release the database quiesce
         su - $OSUSER -c "isql -U$SYB_USER -P$SYB_PASS -S$SYB_SERVER -X" <<EOF
         quiesce database $TAG_NAME release
         go
         exit
 EOF
         exit 1
     fi
     logger "Filesystem $mnt is frozen."
 done

快照後指令碼 (/etc/google/snapshots/post.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/post.sh

 # 1. Configuration
 OSUSER="OS_USER"
 SYB_SERVER="SYB_SERVER_NAME"
 SYB_USER="SYB_USER"
 SYB_PASS="SYB_PASSWORD"
 TAG_NAME="SNAPSHOT_$(date +%Y%m%d)"
 MANIFEST_DIR="/var/tmp/sybase_manifests"

 # 2. Discovery
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

 logger "Sybase Full VM BYOS: Starting post-snapshot script."

 # 3. UNFREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     sudo fsfreeze -u "$mnt"
     logger "Filesystem $mnt is unfrozen."
 done

 # 4. RESUME DATABASE
 logger "Sybase Full VM BYOS: Releasing databases for tag $TAG_NAME..."

 # Command logic from act_sybase_post.sh
 su - $OSUSER -c "isql -U$SYB_USER -P$SYB_PASS -S$SYB_SERVER -X" <<EOF
 use master
 go
 quiesce database $TAG_NAME release
 go
 exit
 EOF

 if [ $? -ne 0 ]; then
     logger "Error: Failed to release Sybase databases."
 else
     logger "Sybase Full VM BYOS: Databases released successfully."
     # Cleanup manifest
     rm -f "$MANIFEST_DIR/manifest_$TAG_NAME.dat"
 fi

 logger "Sybase Full VM BYOS: Cleanup complete."

更改下列內容:

  • OS_USER:執行 SAP ASE 的作業系統使用者 (例如 sybase)。
  • SYB_SERVER_NAME:SAP ASE 伺服器名稱。
  • SYB_USER:有權暫停資料庫的資料庫使用者。
  • SYB_PASSWORD:資料庫使用者密碼 (或使用 Secret Manager 動態擷取)。
  • DATABASE_LIST:以半形逗號分隔的資料庫清單,這些資料庫會進入靜止狀態。

SAP IQ

快照前指令碼 (/etc/google/snapshots/pre.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/pre.sh

 # 1. Configuration
 OSUSER="OS_USER"
 DB_NAME="DB_NAME"
 CONN_STR="uid=DB_USER;pwd=DB_PASSWORD;dbn=$DB_NAME;eng=ENGINE_NAME"
 BKP_DIR="/var/tmp/sybaseiq_bkp"
 FULL_BKP_FILE="$BKP_DIR/FULL_VIR_DEC"

 # 2. Discovery
 # Dynamically discover all local writeable mount points for full VM consistency
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

 logger "SybaseIQ Full VM BYOS: Starting pre-snapshot script."

 # 3. QUIESCE DATABASE (Virtual Decoupled Backup)
 mkdir -p "$BKP_DIR"
 chown $OSUSER "$BKP_DIR"

 logger "SybaseIQ Full VM BYOS: Preparing virtual decoupled backup..."

 # Execute the quiesce command logic from act_sybaseiq_pre.sh
 su -m $OSUSER -c "dbisql -nogui -c '$CONN_STR' \"BACKUP DATABASE FULL VIRTUAL DECOUPLED TO '$FULL_BKP_FILE'\""

 if [ $? -ne 0 ]; then
     logger "Error: Failed to freeze (not able to take full_vir_dec backup)."
     exit 1
 fi

 # 4. FREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     if ! sudo fsfreeze -f "$mnt"; then
         logger "Error: Failed to freeze $mnt."
         # Attempt emergency thaw
         for thaw_mnt in $MOUNT_POINTS; do sudo fsfreeze -u "$thaw_mnt" 2>/dev/null; done
         # Cleanup quiesce file as backup is invalid
         rm -f "$FULL_BKP_FILE"
         exit 1
     fi
     logger "Filesystem $mnt is frozen."
 done

快照後指令碼 (/etc/google/snapshots/post.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/post.sh
 # 1. Configuration
 OSUSER="OS_USER"
 DB_NAME="DB_NAME"
 CONN_STR="uid=DB_USER;pwd=DB_PASSWORD;dbn=$DB_NAME;eng=ENGINE_NAME"
 BKP_DIR="/var/tmp/sybaseiq_bkp"
 FULL_BKP_FILE="$BKP_DIR/FULL_VIR_DEC"
 INC_BKP_FILE="$BKP_DIR/INC_BKP"
 # 2. Discovery
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)
 logger "SybaseIQ Full VM BYOS: Starting post-snapshot script."
 # 3. UNFREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     sudo fsfreeze -u "$mnt"
     logger "Filesystem $mnt is unfrozen."
 done
 # 4. RESUME DATABASE (Post-Snapshot Cleanup)
 logger "SybaseIQ Full VM BYOS: Performing incremental since full backup..."
 # Logic from act_sybaseiq_post.sh to finish the decoupled sequence
 su -m $OSUSER -c "dbisql -nogui -c '$CONN_STR' \"BACKUP DATABASE INCREMENTAL SINCE FULL TO '$INC_BKP_FILE'\""
 if [ $? -ne 0 ]; then
     logger "Error: Failed post-quiesce incremental backup."
 fi
 # Remove temporary files created during quiesce
 rm -f "$FULL_BKP_FILE"*
 logger "SybaseIQ Full VM BYOS: Cleanup complete."

更改下列內容:

  • OS_USER:執行 SAP IQ 的作業系統使用者 (例如 sybiq)。
  • DB_NAME:SAP IQ 資料庫名稱。
  • DB_USER:具備 DBA 權限的資料庫使用者。
  • DB_PASSWORD:資料庫使用者密碼 (或使用 Secret Manager 動態擷取)。
  • ENGINE_NAME:SAP IQ 資料庫引擎名稱。

SAP MaxDB

快照前指令碼 (/etc/google/snapshots/pre.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/pre.sh

 # 1. Configuration
 DBSID="DBSID"
 OSUSER="OS_USER"
 MAXDB_KEY="-user DBM_USER,DBM_PASSWORD"
 AUTOLOG_TMPLT="LOG_BACKUP"

 # 2. Discovery
 # Dynamically discover all local writeable mount points for full VM consistency
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

 logger "MaxDB Full VM BYOS: Starting pre-snapshot script for $DBSID."

 # 3. QUIESCE DATABASE
 # Optional: Trigger a log backup first to ensure point-in-time recovery readiness
 logger "MaxDB Full VM BYOS: Triggering log backup..."
 su -m $OSUSER -c "dbmcli -d $DBSID $MAXDB_KEY -uUTL -c backup_start $AUTOLOG_TMPLT LOG"

 # Suspend logwriter to quiesce the database
 logger "MaxDB Full VM BYOS: Suspending logwriter..."
 su -m $OSUSER -c "dbmcli -d $DBSID $MAXDB_KEY -uUTL -c util_execute suspend logwriter"
 if [ $? -ne 0 ]; then
     logger "Error: Failed to suspend MaxDB logwriter."
     exit 1
 fi

 # 4. FREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     if ! sudo fsfreeze -f "$mnt"; then
         logger "Error: Failed to freeze $mnt."
         # Attempt emergency thaw
         for thaw_mnt in $MOUNT_POINTS; do sudo fsfreeze -u "$thaw_mnt" 2>/dev/null; done
         # Resume the logwriter as the backup is no longer consistent
         su -m $OSUSER -c "dbmcli -d $DBSID $MAXDB_KEY -uUTL -c util_execute resume logwriter"
         exit 1
     fi
     logger "Filesystem $mnt is frozen."
 done

快照後指令碼 (/etc/google/snapshots/post.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/post.sh

 # 1. Configuration
 DBSID="DBSID"
 OSUSER="OS_USER"
 MAXDB_KEY="-user DBM_USER,DBM_PASSWORD"

 # 2. Discovery
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

 logger "MaxDB Full VM BYOS: Starting post-snapshot script for $DBSID."

 # 3. UNFREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     sudo fsfreeze -u "$mnt"
     logger "Filesystem $mnt is unfrozen."
 done

 # 4. RESUME DATABASE
 logger "MaxDB Full VM BYOS: Resuming logwriter..."
 su -m $OSUSER -c "dbmcli -d $DBSID $MAXDB_KEY -uUTL -c util_execute resume logwriter"
 if [ $? -ne 0 ]; then
     logger "Error: Failed to resume MaxDB logwriter."
 else
     logger "MaxDB Full VM BYOS: Logwriter resumed successfully."
 fi

 logger "MaxDB Full VM BYOS: Cleanup complete."

更改下列內容:

  • DBSID:大寫的 SAP MaxDB 系統 ID (SID)。
  • OS_USER:執行 MaxDB 的作業系統使用者 (例如 sdba)。
  • DBM_USER:資料庫管理員 (DBM) 使用者。
  • DBM_PASSWORD:DBM 使用者密碼 (或使用儲存的 DBM 金鑰:-k KEY_NAME)。

PostgreSQL

快照前指令碼 (/etc/google/snapshots/pre.sh)

 #!/bin/bash
 # 1. Configuration
 OSUSER="OS_USER"
 PG_HOME="PG_HOME"
 DBUSER="DB_USER"
 PORT=PORT_NUMBER
 DBNAME="DB_NAME"
 FIFO="/tmp/pg_backup_fifo_$PORT"
 PID_FILE="/tmp/pg_backup_pid_$PORT"
 ACT_JOBNAME="SNAPSHOT_$(date +%Y%m%d%H%M%S)"

 # 2. Discovery
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)
 export PATH=$PG_HOME/bin:$PATH

 logger "PostgreSQL Full VM BYOS: Starting pre-snapshot script."

 # 3. QUIESCE DATABASE (Session-Bound)
 rm -f "$FIFO"
 mkfifo "$FIFO"
 chown "$OSUSER" "$FIFO"

 su "$OSUSER" -c "psql -p $PORT -U $DBUSER -d $DBNAME -f $FIFO" > /tmp/pg_backup_output.log 2>&1 &
 echo $! > "$PID_FILE"

 psql_version=$(su "$OSUSER" -c "psql --version" | awk '{print $3}' | cut -d'.' -f1)
 if [ "$psql_version" -ge 15 ]; then
     CMD="SELECT pg_backup_start('$ACT_JOBNAME');"
 else
     CMD="SELECT pg_start_backup('$ACT_JOBNAME');"
 fi

 echo "$CMD" > "$FIFO"
 sleep 2

 # 4. FREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     if ! sudo fsfreeze -f "$mnt"; then
         logger "Error: Failed to freeze $mnt. Cleaning up..."
         for thaw_mnt in $MOUNT_POINTS; do sudo fsfreeze -u "$thaw_mnt" 2>/dev/null; done
         echo "SELECT pg_backup_stop(); exit;" > "$FIFO"
         rm -f "$FIFO" "$PID_FILE"
         exit 1
     fi
     logger "Filesystem $mnt is frozen."
 done

快照後指令碼 (/etc/google/snapshots/post.sh)

 #!/bin/bash
 PORT=PORT_NUMBER
 FIFO="/tmp/pg_backup_fifo_$PORT"
 PID_FILE="/tmp/pg_backup_pid_$PORT"

 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)
 logger "PostgreSQL Full VM BYOS: Starting post-snapshot script."

 # 1. UNFREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     sudo fsfreeze -u "$mnt"
     logger "Filesystem $mnt is unfrozen."
 done

 # 2. RESUME DATABASE (Close Session)
 if [ -p "$FIFO" ]; then
     logger "PostgreSQL Full VM BYOS: Closing backup session via FIFO..."
     echo "SELECT pg_backup_stop(); SELECT pg_stop_backup(); exit;" > "$FIFO"
     if [ -f "$PID_FILE" ]; then
         wait "$(cat "$PID_FILE")" 2>/dev/null
     fi
     rm -f "$FIFO" "$PID_FILE"
     logger "PostgreSQL Full VM BYOS: Database session closed."
 else
     logger "Error: FIFO not found. Session may have terminated unexpectedly."
 fi
 logger "PostgreSQL Full VM BYOS: Cleanup complete."

更改下列內容:

  • OS_USER:執行 PostgreSQL 的作業系統使用者 (例如 postgres)。
  • PG_HOME:PostgreSQL 安裝目錄的路徑。
  • DB_USER:具備超級使用者權限的 PostgreSQL 資料庫使用者。
  • PORT_NUMBER:PostgreSQL 監聽的通訊埠號碼 (預設:5432)。
  • DB_NAME:要連線的資料庫名稱。

MySQL

快照前指令碼 (/etc/google/snapshots/pre.sh)

 #!/bin/bash
 # 1. Configuration
 DBUSER="DB_USER"
 DBPASSWORD="DB_PASSWORD"
 PORTNO=PORT_NUMBER
 SOCKET_FILE="/var/run/mysqld/mysqld.sock"
 MYSQL_PATH="/usr/bin/mysql"
 FIFO="/tmp/mysql_backup_fifo_$PORTNO"
 PID_FILE="/tmp/mysql_backup_pid_$PORTNO"

 # 2. Discovery
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

 logger "MySQL Full VM BYOS: Starting pre-snapshot script."

 # 3. QUIESCE DATABASE (Session-Bound Lock)
 rm -f "$FIFO"
 mkfifo "$FIFO"

 $MYSQL_PATH -u"$DBUSER" -p"$DBPASSWORD" -S"$SOCKET_FILE" -P"$PORTNO" < "$FIFO" > /tmp/mysql_backup_output.log 2>&1 &
 echo $! > "$PID_FILE"

 logger "MySQL Full VM BYOS: Issuing FLUSH TABLES WITH READ LOCK..."
 echo "FLUSH TABLES WITH READ LOCK; SELECT SLEEP(86400);" > "$FIFO"
 sleep 2

 # 4. FREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     if ! sudo fsfreeze -f "$mnt"; then
         logger "Error: Failed to freeze $mnt. Cleaning up..."
         for thaw_mnt in $MOUNT_POINTS; do sudo fsfreeze -u "$thaw_mnt" 2>/dev/null; done
         kill $(cat "$PID_FILE") 2>/dev/null
         rm -f "$FIFO" "$PID_FILE"
         exit 1
     fi
     logger "Filesystem $mnt is frozen."
 done

快照後指令碼 (/etc/google/snapshots/post.sh)

 #!/bin/bash
 PORTNO=PORT_NUMBER
 FIFO="/tmp/mysql_backup_fifo_$PORTNO"
 PID_FILE="/tmp/mysql_backup_pid_$PORTNO"

 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)
 logger "MySQL Full VM BYOS: Starting post-snapshot script."

 # 1. UNFREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     sudo fsfreeze -u "$mnt"
     logger "Filesystem $mnt is unfrozen."
 done

 # 2. RESUME DATABASE (Unlock)
 if [ -f "$PID_FILE" ]; then
     logger "MySQL Full VM BYOS: Releasing locks..."
     kill $(cat "$PID_FILE") 2>/dev/null
     rm -f "$FIFO" "$PID_FILE"
     logger "MySQL Full VM BYOS: Locks released and session closed."
 else
     logger "Error: PID file not found. Lock may have been lost prematurely."
 fi
 logger "MySQL Full VM BYOS: Cleanup complete."

更改下列內容:

  • DB_USER:具備管理員權限的 MySQL 資料庫使用者。
  • DB_PASSWORD:MySQL 資料庫密碼 (或使用 Secret Manager 動態擷取)。
  • PORT_NUMBER:MySQL 監聽的通訊埠號碼 (預設:3306)。

MariaDB

快照前指令碼 (/etc/google/snapshots/pre.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/pre.sh

 # 1. Configuration
 DBUSER="DB_USER"
 DBPASSWORD="DB_PASSWORD"
 PORTNO=PORT_NUMBER
 SOCKET_FILE="/var/run/mysqld/mysqld.sock"
 MARIADB_PATH="/usr/bin/mariadb" # or /usr/bin/mysql
 SRV_TYPE="Master" # Set to "Slave" for standby nodes
 FIFO="/tmp/mariadb_backup_fifo_$PORTNO"
 PID_FILE="/tmp/mariadb_backup_pid_$PORTNO"
 STARTPOS_FILE="/tmp/STARTPOS_FILE_$PORTNO.txt"

 # 2. Discovery
 # Dynamically discover all local writeable mount points for full VM consistency
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)

 logger "MariaDB Full VM BYOS: Starting pre-snapshot script."

 # 3. QUIESCE DATABASE (Session-Bound)
 # Create a FIFO to hold the session open
 rm -f "$FIFO"
 mkfifo "$FIFO"

 # Start a background mariadb session that reads from the FIFO
 # This keeps the session (and locks) active until the process is killed
 $MARIADB_PATH -u"$DBUSER" -p"$DBPASSWORD" -S"$SOCKET_FILE" -P"$PORTNO" < "$FIFO" > /tmp/mariadb_backup_output.log 2>&1 &
 echo $! > "$PID_FILE"

 # Prepare quiesce query (Logic from act_mariadb_pre.sh)
 if [ "$SRV_TYPE" != "Slave" ]; then
     # Primary node: lock all tables
     logger "MariaDB Full VM BYOS: Issuing FLUSH TABLES WITH READ LOCK..."
     echo "FLUSH TABLES WITH READ LOCK; SELECT SLEEP(86400);" > "$FIFO"

     # Capture master status for PITR/replication info
     $MARIADB_PATH -u"$DBUSER" -p"$DBPASSWORD" -S"$SOCKET_FILE" -P"$PORTNO" -e "SHOW MASTER STATUS;" > "$STARTPOS_FILE"
 else
     # Replica node: stop the slave thread
     logger "MariaDB Full VM BYOS: Issuing STOP SLAVE..."
     echo "STOP SLAVE; SELECT SLEEP(86400);" > "$FIFO"

     # Capture slave status
     $MARIADB_PATH -u"$DBUSER" -p"$DBPASSWORD" -S"$SOCKET_FILE" -P"$PORTNO" -e "SHOW SLAVE STATUS \G;" > "$STARTPOS_FILE"
 fi

 # Wait a brief moment to ensure the command is processed
 sleep 2
 # 4. FREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     if ! sudo fsfreeze -f "$mnt"; then
         logger "Error: Failed to freeze $mnt. Cleaning up..."
         # Attempt emergency thaw
         for thaw_mnt in $MOUNT_POINTS; do sudo fsfreeze -u "$thaw_mnt" 2>/dev/null; done
         # Kill the background session to release locks
         kill $(cat "$PID_FILE") 2>/dev/null
         rm -f "$FIFO" "$PID_FILE"
         exit 1
     fi
     logger "Filesystem $mnt is frozen."
 done

快照後指令碼 (/etc/google/snapshots/post.sh)

 #!/bin/bash
 # Location: /etc/google/snapshots/post.sh
 # 1. Configuration
 PORTNO=PORT_NUMBER
 SRV_TYPE="Master"
 DBUSER="DB_USER"
 DBPASSWORD="DB_PASSWORD"
 SOCKET_FILE="/var/run/mysqld/mysqld.sock"
 MARIADB_PATH="/usr/bin/mariadb"
 FIFO="/tmp/mariadb_backup_fifo_$PORTNO"
 PID_FILE="/tmp/mariadb_backup_pid_$PORTNO"
 # 2. Discovery
 MOUNT_POINTS=$(findmnt -n -l -o TARGET -t xfs,ext4,ext3,ext2,btrfs)
 logger "MariaDB Full VM BYOS: Starting post-snapshot script."
 # 3. UNFREEZE FILESYSTEMS (Optional)
 for mnt in $MOUNT_POINTS; do
     sudo fsfreeze -u "$mnt"
     logger "Filesystem $mnt is unfrozen."
 done
 # 4. RESUME DATABASE
 if [ -f "$PID_FILE" ]; then
     logger "MariaDB Full VM BYOS: Resuming database operations..."
     # Kill the background sleep process to automatically release session-bound locks
     kill $(cat "$PID_FILE") 2>/dev/null
     # For slave nodes, explicitly ensure replication is started
     if [ "$SRV_TYPE" = "Slave" ]; then
         $MARIADB_PATH -u"$DBUSER" -p"$DBPASSWORD" -S"$SOCKET_FILE" -P"$PORTNO" -e "START SLAVE;"
     fi
     rm -f "$FIFO" "$PID_FILE"
     logger "MariaDB Full VM BYOS: Cleanup complete."
 else
     logger "Error: PID file not found. Database state might be inconsistent."
 fi

更改下列內容:

  • DB_USER:具備管理權限的 MariaDB 資料庫使用者。
  • DB_PASSWORD:MariaDB 資料庫密碼 (或使用 Secret Manager 動態擷取)。
  • PORT_NUMBER:MariaDB 監聽的通訊埠編號 (預設:3306)。

部署訪客清除指令碼

為資料庫準備好 pre.sh 和 post.sh 指令碼後,請將這些指令碼部署至 Linux VM 執行個體:

  1. 使用 SSH 連線至執行自行管理資料庫的 Linux VM 執行個體。

  2. 如果 /etc/google/snapshots/ 目錄不存在,請建立該目錄:

    sudo mkdir -p /etc/google/snapshots
    
  3. 將自訂 pre.sh 和 post.sh 指令碼儲存至 VM 執行個體的 /etc/google/snapshots/ 目錄。

  4. 確認指令碼具備執行權限:

    sudo chmod 755 /etc/google/snapshots/pre.sh /etc/google/snapshots/post.sh
    

啟用快照的訪客代理

如要允許備份和災難復原服務執行快照前和快照後指令碼,請在 Linux VM 執行個體的 Compute Engine 訪客環境中啟用快照整合功能:

  1. 使用 SSH 連線至執行自行管理資料庫的 Linux VM 執行個體 (如果尚未連線)。

  2. 在文字編輯器中開啟訪客環境設定檔:

    sudo nano /etc/default/instance_configs.cfg
    
  3. 在 [Snapshots] 區段下方新增或修改下列設定,即可啟用快照:

    [Snapshots]
    enabled = true
    timeout_in_seconds = 120
    
  4. 重新啟動 VM 執行個體上的訪客代理程式,套用變更:

    sudo systemctl restart google-guest-agent
    

設定備份方案

現在,VM 執行個體已設定客體清除指令碼,且客體代理程式已啟用快照功能,請在 Google Cloud 控制台設定備份方案:

  1. 前往 Google Cloud 控制台的「Backup and DR」(備份和災難復原) 頁面。

    前往「備份和災難復原」

  2. 從導覽選單中選取「Backup Plans」(備份方案)。

  3. 按一下「Create Backup Plan」(建立備份方案),或選取現有備份方案,然後按一下「Edit」(編輯)。

  4. 在「Instance Backup」(執行個體備份) 設定步驟下方,找出一致性選項。

  5. 選取「啟用應用程式一致性」選項。

  6. 儲存備份方案,並套用至 VM 執行個體。備份方案會按照排程觸發訪客代理程式,執行 pre.sh 指令碼、建立快照,並執行 post.sh 指令碼。

設定 Persistent Disk 備份,以便執行資料庫時間點復原

如果是 Db2 和 SAP HANA 等自行管理的資料庫,您可以設定獨立 Persistent Disk 備份,擷取資料庫封存記錄,啟用時間點復原功能。如果不需要時間點復原功能,可以跳到「停用客體清除指令碼」。

時間點復原記錄備份的最佳做法

  • 頻率:將這個專用記錄磁碟的備份方案排程,設為與時間點復原記錄精細程度相同,例如每 15 分鐘或每小時。

  • 成本最佳化:儲存頻繁的快照可能會增加儲存成本。為盡可能減少負擔,請將「最短保留期限」和對應的「不可變性 vault 期限」設為允許的最短時間,但仍須滿足時間點復原時間範圍。

將自行管理的資料庫復原至特定時間點

如果您已設定定期 VM 備份,以及專屬的 Persistent Disk 記錄備份,請按照下列程序將資料庫還原至特定時間點。如果您未設定專屬記錄備份,或不需要時間點復原功能,請參閱「從備份保存庫還原 Compute Engine 執行個體」一文,直接還原 VM,然後跳到「執行還原後工作」一節。

復原工作流程

  1. 還原基礎 VM:從備份 vault 選取目標復原時間之前的執行個體備份 (機器映像檔)。請按照標準 Compute Engine 還原程序進行還原。如需操作說明,請參閱「從備份保存庫還原 Compute Engine 執行個體」。

    如果您未設定專屬記錄備份,或不需要時間點復原功能,請跳至步驟 4。

  2. 連接記錄磁碟:找出並還原在目標復原時間前擷取的相關記錄磁碟快照。將這些新建立的磁碟附加至還原的執行個體。

  3. 向前復原資料庫:使用 SSH 連線至還原的執行個體,並使用新連接的永久磁碟上的記錄,執行資料庫專屬的向前復原指令,例如 Db2 中的 ROLLFORWARD DATABASE。

  4. 執行還原後工作:執行將應用程式上線所需的任何資料庫特定步驟。

停用訪客清除指令碼

如要移除自訂客體清除指令碼,並停用應用程式一致性,請按照下列步驟操作:

  1. 取消指派執行個體的備份方案:在 Google Cloud 控制台中,前往「Backup and DR」(備份和災難復原) 頁面,在「Protected Resources」(受保護的資源) 下方找到 VM 執行個體,然後取消指派或刪除備份方案關聯。

  2. 移除 guest-flush 指令碼: 使用 SSH 連線至 Linux VM 執行個體,並移除自訂 guest-flush 指令碼:

    sudo rm -f /etc/google/snapshots/pre.sh /etc/google/snapshots/post.sh
    
  3. 停用客體代理程式中的快照整合功能:在 VM 執行個體上開啟 /etc/default/instance_configs.cfg,在 [Snapshots] 下方設定 enabled = false,然後重新啟動客體代理程式:

    sudo systemctl restart google-guest-agent
    

後續步驟