脚本中灾备切换脚本如何验证

wen 实用脚本 3

本文目录导读:

脚本中灾备切换脚本如何验证

  1. 核心验证维度
  2. 具体验证方法(分阶段进行)
  3. 自动化验证框架(最佳实践)
  4. 常见问题与检查清单

在脚本中进行灾备切换的验证非常关键,因为如果切换脚本本身有缺陷,不仅无法完成切换,还可能造成数据丢失或业务中断。

下面从验证维度具体方法两个层面,为你拆解如何验证灾备切换脚本。

核心验证维度

验证灾备切换脚本是否合格,需要覆盖以下四个核心领域:

  1. 功能正确性

    • 切换:脚本能否顺利将VIP、DNS、数据库、应用服务从主节点切换到备节点?
    • 回切:切换后,脚本能否在指定条件下,顺利将业务切回主节点?
    • 异常处理:脚本是否处理了各种可能的中断(如网络超时、权限不足、磁盘空间满)?
  2. 原子性与幂等性

    • 原子性:脚本要么全部成功,要么全部失败,不会停留在中间状态(只切换了前两个步骤,第三个步骤失败)。
    • 幂等性:脚本可以重复执行多次,不会产生副作用(重复执行不会导致VIP绑定到多个节点)。
  3. 数据一致性

    • 切换前:确保主备数据同步完全一致(如数据库日志应用完毕)。
    • 切换后:验证备节点数据是否完整可用,且无脏数据。
  4. 执行效率与健壮性

    • 超时控制:脚本中的每个操作都应有超时机制,防止因某一步卡死导致整个切换失败。
    • 日志与通知:脚本应输出详细的执行日志,并在关键节点发送告警/通知。

具体验证方法(分阶段进行)

建议按照从简单到复杂、从单元到整体的顺序进行验证。

单元测试(离线验证)

不依赖真实环境,只测试脚本的逻辑片段。

  • 方法
    • 将脚本中的核心函数(如check_healthfloating_ip_modifyread_config)独立出来。
    • 对函数输入各种边界值(空参数、非法IP、超长字符串)。
    • 使用Mock技术模拟远程命令、API调用,验证脚本的异常处理分支是否正常。
  • 示例
    • 测试check_ssh_connectivity函数:当SSH连接超时时,脚本是否会重试?重试次数是否正确?最终返回false还是抛出异常?

集成测试(沙箱/测试环境)

在完全隔离的测试环境(最好是与生产环境网络、配置完全一致的副本)中执行。

  • 方法
    1. 冷启动验证:在测试环境从零开始部署主、备节点,执行切换脚本。
    2. 监控脚本输出:实时观察脚本输出的日志,检查每一步的状态。
    3. 状态检查点:在每次切换后,手动检查以下项:
      • VIP是否漂移到备节点IP?
      • DNS解析是否指向备节点IP?
      • 数据库连接是否正常?
      • 应用能否正常启动?
    4. 故障注入:在脚本执行过程中,人为制造故障:
      • 切断网络连接。
      • 模拟磁盘空间满。
      • 停止关键服务(如数据库)。
      • 模拟SSH密钥失效。
  • 示例
    • 执行切换脚本到第5步(停止主库),此时手动kill掉主库进程,观察脚本能否正确识别并转到备库接管流程。

压力与并发测试(稳定性验证)

模拟真实故障时的并发压力。

  • 方法
    • 使用压测工具(如abwrkJMeter)持续向主节点发送请求。
    • 在压测进行到一半时,触发切换脚本。
    • 检查指标:
      • 切换时间:从脚本执行到业务恢复的时间(RTO,恢复时间目标)。
      • 丢包率:切换过程中,有多少请求失败或超时。
      • 数据一致性:切换后,检查数据库是否丢失了最后几秒的事务。
  • 示例

    录制生产环境的流量,在测试环境重放,在流量高峰期执行切换,验证服务中断时间是否在SLA(服务水平协议)范围内(< 30秒)。

回切与循环验证(可逆性验证)

灾备不仅要能切过去,更要能切回来。

  • 方法
    • 编写回切脚本,并采用与切换脚本相同的方法进行验证。
    • 循环测试:执行 切换 -> 回切 -> 再切换 -> 再回切 循环5-10次,每次循环后,手工验证数据一致性和业务可用性。
  • 痛点

    很多脚本只在第一次切换好使,第二次切换后状态就乱了(配置文件被修改,但下次没重置)。

自动化验证框架(最佳实践)

手动验证效率低,建议编写一个验证脚本来自动验证切换脚本

# 伪代码示例:验证切换脚本的框架
import subprocess
import time
import logging
logger = logging.getLogger("DisasterRecovery_Validator")
def run_switchover(script_path):
    """执行切换脚本并返回结果"""
    logger.info(f"正在执行切换脚本: {script_path}")
    result = subprocess.run(
        [script_path, "--mode", "switchover"],
        capture_output=True,
        text=True,
        timeout=300  # 设置5分钟超时
    )
    if result.returncode != 0:
        logger.error(f"脚本执行失败: {result.stderr}")
        return False
    logger.info(f"脚本输出: {result.stdout}")
    return True
def verify_vip(target_ip):
    """验证VIP是否生效"""
    # 使用ping或arping检查VIP是否在目标机器上
    output = subprocess.run(...)
    # 解析输出逻辑
    if "from " + target_ip in output:
        return True
    return False
def verify_database_connection(host, port, user, password):
    """验证数据库连接"""
    import pymysql
    try:
        conn = pymysql.connect(host=host, port=port, user=user, password=password, timeout=5)
        # 执行一条查询,确认可读写
        cursor = conn.cursor()
        cursor.execute("SELECT 1")
        conn.close()
        return True
    except Exception as e:
        logger.error(f"数据库连接失败: {e}")
        return False
def verify_application_health(url):
    """验证应用健康检查接口"""
    import requests
    try:
        response = requests.get(url, timeout=5)
        if response.status_code == 200 and response.json().get("status") == "healthy":
            return True
    except Exception as e:
        logger.error(f"健康检查失败: {e}")
        return False
def main():
    # 1. 初始状态检查
    assert verify_vip("10.0.0.1")  # 假设VIP在主节点
    # 2. 执行切换
    assert run_switchover("/opt/scripts/dr_switch.sh")
    # 3. 验证切换后状态
    time.sleep(2)  # 给服务一些时间恢复
    assert verify_vip("10.0.0.100")   # VIP现在应该在备节点
    assert verify_database_connection("10.0.0.100", 3306, "appuser", "pass")
    assert verify_application_health("http://10.0.0.100:8080/health")
    # 4. 执行回切
    assert run_switchover("/opt/scripts/dr_switch.sh", mode="failback")
    # 5. 验证回切后状态
    time.sleep(2)
    assert verify_vip("10.0.0.1")
    assert verify_database_connection("10.0.0.1", 3306, "appuser", "pass")
    assert verify_application_health("http://10.0.0.1:8080/health")
    logger.info("所有灾备切换脚本验证通过!")
if __name__ == "__main__":
    main()

常见问题与检查清单

在验证过程中,你一定会遇到以下问题,请重点检查:

  1. SSH/密钥问题
    • 脚本运行账户的SSH免密登录是否配置正确且针对所有节点?
    • StrictHostKeyChecking 是否设置为 no(或使用已知主机文件)?
  2. 权限问题
    • 执行脚本的用户是否有 sudo 权限?是否需要NOPASSWD?
    • 脚本中是否有针对特定路径(如 /var/log/opt/app)的写入权限?
  3. 依赖包/命令缺失
    • 备节点是否安装了所有脚本依赖的命令(如 jqawkmysql-client)?
  4. 网络隔离
    • 如果VIP跨网段,是否需要静态ARP配置?
    • 防火墙规则是否允许VIP在节点间漂移?
  5. 日志不可见
    • 脚本是否将错误输出到 stderr?你的验证脚本是否捕获了 stderr
  • 不要仅仅在“一切正常”时验证,好的验证必须包含故障注入
  • 自动化验证比手动点击更可靠、可重复。
  • 循环测试才能发现幂等性问题。
  • 最终验证标准是:脚本执行后,你的监控系统(如Prometheus、Zabbix)是否显示所有指标恢复正常? 这是最客观的验证。

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