配置应用一致性备份

本文档介绍了如何为在 Compute Engine Linux 虚拟机 (VM) 实例上运行的自行管理的数据库配置定期应用一致性备份和时间点恢复 (PITR) 工作流。

当您备份有状态的数据库工作负载(例如 IBM Db2、SAP HANA、MySQL 或 PostgreSQL)时,Backup and DR 拍摄的标准崩溃一致性快照可以在事务仍在进行时捕获数据库。 这些未完成的事务可能会损坏数据,使表处于不一致的状态,并且在崩溃恢复时需要执行冗长的数据库修复例程。 应用一致性备份通过与虚拟机客机代理集成来解决这些问题,以便在创建快照之前使数据库静默,并选择性地冻结文件系统。

准备工作

在配置应用一致性备份之前,请完成以下任务。

所需角色

如需获得配置应用一致性备份所需的权限,请让管理员向您授予项目的以下 IAM 角色:

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

您也可以通过自定义 角色或其他预定义 角色来获取所需的权限。

前提条件

  1. 在 Google Cloud 控制台的项目选择器页面上,选择或创建 Google Cloud 项目。

    前往“项目选择器”

  2. 验证是否已为您的项目启用结算功能。

    了解如何确认已启用结算功能

  3. 启用 Backup and DR 和 Secret Manager API。

    启用 API

  4. 确保您的数据库数据和日志文件存储在标准 永久性磁盘上。 Google Cloud Hyperdisk 卷不支持客机刷新操作。

  5. 如果您计划配置时间点恢复,请将数据库配置为将其事务和重做日志归档到专用的单独 Persistent Disk(例如,装载在 /db2/ACT/log_archive)。

使用自带脚本 (BYOS) 客机刷新框架

Backup and DR 使用自带脚本 (BYOS) 客机刷新框架在拍摄快照之前使数据库静默。客机环境依赖于 Linux 虚拟机实例上 /etc/google/snapshots/ 目录中的两个脚本:

  • 快照前脚本 (/etc/google/snapshots/pre.sh): 在拍摄快照之前执行。此脚本必须使数据库操作静默,并且可以选择性地冻结文件系统。
  • 快照后脚本 (/etc/google/snapshots/post.sh): 在拍摄快照之后执行。此脚本必须选择性地解冻文件系统并恢复数据库操作。

如需详细了解特定于数据库的脚本模板,请参阅 按数据库类型查看示例脚本

可选:冻结和解冻文件系统

使用标准 Linux 实用程序 fsfreeze 冻结文件系统是可选的,而不是强制性的。使数据库静默可确保应用级事务一致性。但是,如果您需要额外的文件系统级一致性,可以选择在脚本中添加以下文件系统冻结和解冻命令。

确保虚拟机上的 pre.shpost.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 动态提取凭据

避免在 Shell 脚本中硬编码静态数据库凭据或密码。相反,请使用 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 的名称。

按数据库类型查看示例脚本

以下示例为在 Linux 实例上运行的受支持数据库引擎提供了 pre.shpost.sh 实现。

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 系统标识符 (SID),采用大写形式。
  • DBADM:SAP HANA 管理员操作系统用户(例如 <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 系统标识符 (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.shpost.sh 脚本后,将其部署到 Linux 虚拟机实例:

  1. 使用 SSH 连接到运行自行管理的数据库的 Linux 虚拟机实例。

  2. 创建 /etc/google/snapshots/ 目录(如果该目录尚不存在):

    sudo mkdir -p /etc/google/snapshots
    
  3. 将自定义 pre.shpost.sh 脚本保存到虚拟机实例上的 /etc/google/snapshots/ 目录。

  4. 确保脚本具有执行权限:

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

为快照启用客机代理

如需允许 Backup and DR 运行快照前和快照后脚本,请在 Linux 虚拟机实例上的 Compute Engine 客机环境中启用快照集成:

  1. 使用 SSH 连接到运行自行管理的数据库的 Linux 虚拟机实例(如果尚未连接)。

  2. 在文本编辑器中打开客机环境配置文件:

    sudo nano /etc/default/instance_configs.cfg
    
  3. [Snapshots] 部分下添加或修改以下设置以启用快照:

    [Snapshots]
    enabled = true
    timeout_in_seconds = 120
    
  4. 重启虚拟机实例上的客机代理以应用更改:

    sudo systemctl restart google-guest-agent
    

配置备份方案

现在,虚拟机实例已配置客机刷新脚本,并且客机代理 已为快照启用,接下来在 Google Cloud 控制台中配置备份方案:

  1. 在 Google Cloud 控制台中,前往 Backup and DR 页面。

    前往 Backup and DR

  2. 从导航菜单中选择备份方案

  3. 点击创建备份方案,或选择现有备份方案,然后点击 修改

  4. 实例备份 配置步骤下,找到一致性选项。

  5. 选择启用应用一致性 选项。

  6. 保存备份方案并将其应用于虚拟机实例。按照时间表,备份方案会触发客机代理运行 pre.sh 脚本、拍摄快照并运行 post.sh 脚本。

为数据库时间点恢复配置 Persistent Disk 备份

对于自行管理的数据库(例如 Db2 和 SAP HANA),您可以配置独立的 Persistent Disk 备份来捕获数据库归档日志,从而实现时间点恢复。如果您不需要时间点恢复,可以跳到 停用客机刷新脚本

时间点恢复日志备份的最佳实践

  • 频率: 将此专用日志磁盘的备份方案时间表配置为等于您的时间点恢复日志粒度,例如每 15 分钟或每小时。

  • 费用优化: 存储频繁的快照可能会增加存储费用。 如需最大限度地减少开销,请将最短保留期限 和相应的不可变保险柜期限 配置为仍满足时间点恢复窗口的最小允许时间。

将自行管理的数据库恢复到特定时间点

如果您已配置定期虚拟机备份以及专用 Persistent Disk 日志备份,请按照此过程将数据库恢复到特定时间点。如果您未配置专用日志备份或不需要时间点恢复,请参阅从备份保险柜恢复 Compute Engine 实例以直接恢复虚拟机,然后跳到执行恢复后任务

恢复工作流

  1. 恢复基础虚拟机:从备份保险柜中选择目标恢复时间之前的实例备份(机器映像)。按照标准 Compute Engine 恢复程序进行恢复。如需 查看相关说明,请参阅 从备份保险柜恢复 Compute Engine 实例

    如果您未配置专用日志备份或不需要时间点恢复,请跳到第 4 步。

  2. 挂接日志磁盘:识别并恢复在目标恢复时间之前捕获的相关日志磁盘快照 。将这些新创建的磁盘挂接到恢复的实例。

  3. 前滚数据库:使用 SSH 连接到恢复的实例,并使用位于新挂接的 永久性磁盘上的日志执行特定于数据库的前滚命令(例如 Db2 中的 ROLLFORWARD DATABASE)。

  4. 执行恢复后任务:执行使应用处于在线状态所需的任何特定于数据库的步骤。

停用客机刷新脚本

如需移除自定义客机刷新脚本并停用应用一致性,请执行以下操作:

  1. 从实例中取消分配备份方案: 在 Google Cloud 控制台中,前往 Backup and DR 页面,在 受保护的资源下找到虚拟机实例,然后取消分配或删除备份 方案关联。

  2. 移除客机刷新脚本: 使用 SSH 连接到 Linux 虚拟机实例,然后移除自定义客机刷新脚本:

    sudo rm -f /etc/google/snapshots/pre.sh /etc/google/snapshots/post.sh
    
  3. 在客机代理中停用快照集成: 在虚拟机实例上打开 /etc/default/instance_configs.cfg,在 enabled = false 下设置 [Snapshots],然后重启客机代理:

    sudo systemctl restart google-guest-agent
    

后续步骤