Shell脚本如何配置容器恢复演练

wen 实用脚本 2

本文目录导读:

Shell脚本如何配置容器恢复演练

  1. 恢复演练的核心原则
  2. Shell脚本设计与实现
  3. 更健壮的恢复策略(针对不同场景)
  4. 结果验证与报告

为Shell脚本配置容器恢复演练,核心目标是验证备份数据的完整性、检查恢复流程的自动化程度、以及评估RTO(恢复时间目标)和RPO(恢复点目标)

以下是一个完整的、可落地的Shell脚本恢复演练框架,包含原理讲解和代码示例。


恢复演练的核心原则

  1. 隔离环境:千万不要在生产环境直接演练,必须在测试环境预发布环境隔离命名空间中进行。
  2. 数据无痕:演练结束后,必须彻底清理恢复出来的数据和容器,避免污染后续测试。
  3. 自动验证:脚本不能只恢复成功,还需要自动化验证服务是否可用、数据是否完整。
  4. 日志记录:所有操作步骤、耗时、结果都必须记录到日志,用于后续复盘。

Shell脚本设计与实现

脚本结构

graph TD
    A[开始] --> B{检查环境变量与依赖}
    B -- 通过 --> C[创建隔离命名空间/环境]
    C --> D[恢复数据卷/数据库备份]
    D --> E[启动容器应用]
    E --> F[启动健康检查与数据校验]
    F -- 成功 --> G[记录演练成功并清理]
    F -- 失败 --> H[记录失败详情并回滚]
    G --> I[结束]
    H --> I

详细Shell脚本示例 (基于Docker/Podman容器)

以下脚本假设:

  • 备份文件在本地 /backup/ 目录下。
  • 应用是 WordPress + MySQL。
  • 恢复演练在 独立的 Docker Compose 项目docker-compose-recovery.yaml)中运行,与生产隔离。
#!/bin/bash
# ============================================
# 容器恢复演练脚本 (Recovery Drill Script)
# 用途: 自动化恢复备份并验证服务可用性
# 警告: 切勿在生产环境执行!
# ============================================
set -euo pipefail  # 严格模式:出错即停、未定义变量报错、管道错误检测
# --- 配置变量 (请根据实际情况修改) ---
NAMESPACE="drill-$(date +%Y%m%d%H%M%S)"    # 唯一的环境名称,用于隔离
BACKUP_DIR="/backup"
BACKUP_FILE="${BACKUP_DIR}/latest_wordpress_backup.tar.gz"
DB_BACKUP_FILE="${BACKUP_DIR}/latest_mysql_backup.sql"
RESTORE_DIR="/tmp/${NAMESPACE}_restore"
COMPOSE_FILE="docker-compose-recovery.yaml"  # 专用的演练 Compose 文件
HEALTH_CHECK_URL="http://localhost:8080/wp-admin/install.php"
LOG_FILE="./recovery_drill_$(date +%Y%m%d).log"
TIMEOUT_SECONDS=60
# --- 函数定义 ---
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
cleanup() {
    log "INFO: 开始清理演练环境: ${NAMESPACE}"
    # 停止并删除演练用的容器和网络
    docker-compose -p "$NAMESPACE" -f "$COMPOSE_FILE" down -v 2>/dev/null || true
    # 删除临时恢复目录
    rm -rf "$RESTORE_DIR"
    log "INFO: 清理完成"
}
# 退出时自动清理(无论是正常结束还是错误退出)
trap cleanup EXIT
# 健康检查 + 数据验证函数
check_health() {
    log "INFO: 等待应用启动并检查健康状态..."
    local retries=0
    while [ $retries -lt $TIMEOUT_SECONDS ]; do
        # 1. 检查容器是否都在运行
        if docker-compose -p "$NAMESPACE" ps --status running 2>/dev/null | grep -q "Up"; then
            # 2. 检查 HTTP 返回码
            local http_code
            http_code=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL" || echo "000")
            if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 500 ]; then
                log "INFO: 健康检查通过 (HTTP Code: $http_code)"
                # 3. 数据完整性校验(示例:检查 WordPress 数据库中是否有文章)
                local site_count
                site_count=$(docker-compose -p "$NAMESPACE" exec -T db sh -c "mysql -uwordpress -pwordpress -e 'SELECT COUNT(*) FROM wp_posts;' 2>/dev/null | tail -1")
                if [ "$site_count" -gt 0 ]; then
                    log "INFO: 数据完整性校验通过 (共有 $site_count 条文章记录)"
                    return 0
                else
                    log "ERROR: 数据校验失败:文章数为零"
                    return 1
                fi
            fi
        fi
        log "DEBUG: 等待服务就绪 (${retries}s)..."
        sleep 2
        retries=$((retries + 2))
    done
    log "ERROR: 健康检查超时失败"
    return 1
}
# --- 主流程 ---
main() {
    log "=========================================="
    log "容器恢复演练开始"
    log "隔离命名空间: ${NAMESPACE}"
    log "备份文件: ${BACKUP_FILE}"
    log "=========================================="
    # 1. 前置检查
    log "STEP 1: 检查前提条件"
    if [ ! -f "$BACKUP_FILE" ]; then
        log "ERROR: 备份文件不存在: ${BACKUP_FILE}"
        exit 1
    fi
    if [ ! -f "$DB_BACKUP_FILE" ]; then
        log "ERROR: 数据库备份文件不存在: ${DB_BACKUP_FILE}"
        exit 1
    fi
    command -v docker-compose >/dev/null 2>&1 || { log "ERROR: docker-compose 未安装"; exit 1; }
    # 2. 准备恢复目录
    log "STEP 2: 恢复文件系统备份"
    mkdir -p "$RESTORE_DIR"
    tar -xzf "$BACKUP_FILE" -C "$RESTORE_DIR"
    log "INFO: 文件系统备份解压完成"
    # 3. 修改 Compose 配置以使用恢复的数据(利用环境变量覆盖)
    log "STEP 3: 启动隔离环境下的容器"
    export DRILL_NAME="$NAMESPACE"
    export DRILL_DATA_DIR="$RESTORE_DIR"
    export DRILL_DB_FILE="$DB_BACKUP_FILE"
    # 重要: 使用 -p 参数指定项目名称为隔离的命名空间
    docker-compose -p "$NAMESPACE" -f "$COMPOSE_FILE" up -d 2>&1 | tee -a "$LOG_FILE"
    # 4. 执行健康检查与数据验证
    log "STEP 4: 执行应用健康检查与数据完整性验证"
    if check_health; then
        log "SUCCESS: 恢复演练成功!环境可用,数据完整。"
        log "演练报告: "
        log "   - 命名空间: ${NAMESPACE}"
        log "   - 恢复耗时: 约 $((SECONDS / 60)) 分 $((SECONDS % 60)) 秒"
        log "   - 使用备份: ${BACKUP_FILE}"
    else
        log "FAILURE: 恢复演练失败!请检查日志。"
        log "停止并清理失败环境..."
        cleanup
        exit 1
    fi
}
# 记录起始时间
SECONDS=0
main "$@"

专用的 Compose 文件 docker-compose-recovery.yaml

这个文件专门用于演练,通过环境变量动态指定数据卷和数据库。

version: '3.8'
services:
  db:
    image: mysql:8.0
    container_name: "drill_db_${DRILL_NAME}"
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    volumes:
      # 挂载恢复的数据库 SQL 文件到自动初始化目录
      - ./mysql-init:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
  wordpress:
    depends_on:
      db:
        condition: service_healthy
    image: wordpress:latest
    container_name: "drill_wp_${DRILL_NAME}"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
    volumes:
      # 使用恢复的文件系统数据
      - ${DRILL_DATA_DIR}:/var/www/html
    ports:
      - "8080:80"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

注意: 数据库恢复方法有很多种,这里为了简化,在启动前将 SQL 文件放入 mysql-init 目录,MySQL 会在首次启动时自动导入,更严谨的做法是先启动一个临时数据库容器,执行 docker cpmysql 命令导入。


更健壮的恢复策略(针对不同场景)

脚本适合单体应用,对于微服务或Kubernetes环境,需要进行调整:

针对 Kubernetes (K8s) 的恢复演练

#!/bin/bash
# K8s 环境恢复演练核心逻辑
NAMESPACE="drill-$(date +%Y%m%d%H%M%S)"  
BACKUP_FILE="/backup/db_dump.sql"
# 1. 创建隔离的命名空间
kubectl create namespace "$NAMESPACE"
# 2. 恢复数据库(示例:从备份创建 Job)
kubectl -n "$NAMESPACE" run restore-db \
  --image=mysql:8.0 \
  --restart=Never \
  --command -- \
  sh -c "mysql -h <prod-db-host> -u user -p'pass' db_name < /backup/db_dump.sql"
# 3. 从备份恢复 PersistentVolumeClaim (PVC) 数据
# 假设有专门的备份控制器或 Velero
velero restore create --from-backup <backup-name> --namespace-mappings <prod-ns>:${NAMESPACE}
# 4. 启动应用并暴露服务
kubectl -n "$NAMESPACE" apply -f k8s/deployment.yaml
kubectl -n "$NAMESPACE" expose deployment app --type=NodePort --port=80
# 5. 健康检查
if curl -f "http://$(minikube ip):$(kubectl -n "$NAMESPACE" get svc app -o jsonpath='{.spec.ports[0].nodePort}')"; then
  echo "恢复演练成功"
else
  echo "恢复演练失败"
fi
# 6. 清理
kubectl delete namespace "$NAMESPACE"

针对数据库(MySQL / Postgres)的时间点恢复(PITR)

如果演练目标是验证 PITR,脚本需要额外处理:

# 备份为 xtrabackup + binlog
# 恢复时指定特定的时间戳
RESTORE_TIME="2023-12-20 14:30:00"
# 使用 xtrabackup 恢复到临时目录
xtrabackup --prepare --target-dir=/tmp/restore --apply-log-only
# 应用增量 binlog 到指定时间点
mysqlbinlog --stop-datetime="$RESTORE_TIME" binlog.* | mysql -u root -p

关键配置建议

  • 备份文件的元数据: 在备份文件名或伴随的 manifest.json 中记录RPO信息,演练时脚本自动校验。
  • 网络隔离: 在Compose文件中,为演练环境使用独立的网络,避免端口冲突。
  • 资源限制: 在演练容器中添加资源限制(mem_limit, cpus),避免影响宿主机性能。
  • 自动清理: 设置一个全局的超时机制(例如脚本运行总时长超过30分钟则强制清理),防止僵尸环境消耗资源。

结果验证与报告

脚本执行后,生成的日志文件 recovery_drill_YYYYMMDD.log 应该包含:

[2023-12-21 10:00:00] 容器恢复演练开始
[2023-12-21 10:00:05] 文件系统备份解压完成
[2023-12-21 10:00:08] 启动隔离环境下的容器
[2023-12-21 10:00:15] 健康检查通过 (HTTP Code: 200)
[2023-12-21 10:00:16] 数据完整性校验通过 (共有 1250 条文章记录)
[2023-12-21 10:00:16] SUCCESS: 恢复演练成功!
[2023-12-21 10:00:16] 恢复耗时: 约 0 分 16 秒

脚本的退出码可以用于 CI/CD 流水线集成:

  • exit 0 : 演练成功
  • exit 1 : 演练失败(文件缺失、健康检查失败、数据校验失败)

配置容器恢复演练的 Shell 脚本,关键在于 隔离、自动化、验证、清理,以上脚本提供了一个可立即修改使用的框架,覆盖了从环境准备到最终清理的全流程,建议每周或每月定时执行该脚本,并将结果输出到监控系统(如 Prometheus + Alertmanager),在恢复演练失败时自动告警。

抱歉,评论功能暂时关闭!