备份正在运行的 MySQL 容器数据卷的最小停机方案

解读

国内生产环境普遍要求 7×24 可用,MySQL 又是多数业务的核心库,直接 docker stoptar 会触发 主备切换、告警风暴、客诉升级。因此“最小停机”≠零停机,而是把 不可写窗口压缩到秒级甚至毫秒级,并保证 备份一致性、可验证、可回滚。面试官想看到:

  1. 你能用 Docker 原生能力 解决,而不是简单甩给 DBA;
  2. 你熟悉 MySQL 事务日志、锁、刷盘机制
  3. 你能给出 回滚预案自动化脚本,而不是只背命令。

知识点

  • 数据卷类型:named volume、bind mount、tmpfs,备份前必须 docker inspect 确认 驱动类型(local、nfs、cephfs)与 挂载点
  • 一致性模型:MySQL 默认 REPEATABLE READ,需借助 FLUSH TABLES WITH READ LOCK(FTWRL)快照备份(xtrabackup) 保证 InnoDB 页级一致性
  • Docker 卷快照:overlay2 不支持原子快照,必须 冻结 IO 或使用 外部存储快照(如 LVM thin-pool、阿里云云盘快照、Ceph RBD snapshot)。
  • 二进制日志坐标:备份瞬间需记录 binlog file & position,用于 主从增量补偿
  • 非 root 运行:官方镜像 mysql:8 默认 mysql(999) 用户,备份脚本需 --user $(id -u mysql) 避免权限漂移。
  • 安全传输:国内合规要求 备份落盘即加密,使用 aes-256-cbcKMS 信封加密,再通过 内网 OSS 或 MinIO 转储。

答案

核心思路:“快照+增量”双保险,秒级冻结,秒级恢复写
步骤如下,全部用 Docker CLI 完成,不依赖宿主机 root 权限(满足企业 最小权限 审计):

  1. 预检

    docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD -e "SELECT @@global.binlog_format,@@global.gtid_mode;"'
    

    确认 binlog=ROWgtid_mode=ON,为后续 增量补偿 做准备。

  2. 创建一致性快照(秒级停机)
    a. 进入容器加 全局读锁

    docker exec mysql mysql -uroot -p$MYSQL_ROOT_PASSWORD -e "
    FLUSH TABLES WITH READ LOCK;
    SHOW MASTER STATUS\G
    system echo 'lock-$(date +%s)' > /var/lib/mysql/backup_lock_point;"
    

    b. 在 宿主机 对数据卷做 原子快照(以 LVM 为例):

    lvcreate -s -n mysql-snap -L 5G /dev/mapper/vg-mysql
    

    耗时 <1s,锁期间 IO 冻结,但 不关闭连接
    c. 立即 解锁

    docker exec mysql mysql -uroot -p$MYSQL_ROOT_PASSWORD -e "UNLOCK TABLES;"
    

    不可写窗口 ≈ 快照创建时间,生产实测 300~800 ms

  3. 备份快照
    挂载快照到临时容器,流式压缩加密 直传对象存储:

    docker run --rm \
      -v mysql-snap:/snapshot:ro \
    -v /backup:/backup \
    -e PASSPHRASE=$BACKUP_KEY \
    alpine \
    tar -C /snapshot -czf - . | \
    gpg --symmetric --cipher-algo AES256 --compress-algo 1 --passphrase-fd 3 3<<<"$PASSPHRASE" | \
    rclone rcat minio:mysql-backup/full-$(date +%F).tar.gz.gpg
    
  4. 清理快照

    lvremove -y /dev/mapper/vg-mysql-snap
    
  5. 验证
    启动 临时实例crash-recovery

    docker run --rm \
      -e MYSQL_ALLOW_EMPTY_PASSWORD=1 \
    -v mysql-snap-recovery:/var/lib/mysql \
    mysql:8 \
    --initialize-insecure \
    --skip-networking &>/dev/null && \
    docker logs mysql-recovery 2>&1 | grep -q "ready for connections"
    

    确保 ibd 文件无损坏gtid 集合连续

  6. 增量补偿(可选)
    若窗口 >1s业务敏感,用 binlog 补偿:

    docker exec mysql mysqlbinlog --start-position=$POSITION --stop-datetime="$(date -d '+1 hour' -Iseconds)" mysql-bin.000123 > inc.sql
    

    恢复时先全量 snapshot,再 source inc.sql,达到 秒级 RPO

回滚预案:

  • 快照失败 → 解锁 后重试,最多 3 次,仍失败则降级到 xtrabackup 热备(无锁,但 CPU 飙升 30%)。
  • 上传中断 → rclone 支持 chunked retry,断点续传。
  • 加密 key 丢失 → KMS 定期轮转备份头 32 byte 存于 Vault,可解密历史文件。

拓展思考

  1. Serverless 场景:阿里云 FC + NAS 无 LVM,可用 NAS 快照 API 替代,快照时间 100 ms,费用 0.02 元/GB/月
  2. K8s sidecar 模式:用 Velero + ResticPod 级备份,但 Restic 不支持 FTWRL,需改 xtrabackup sidecar,镜像体积 +180 MB,需评估 调度压力
  3. 跨 Region 容灾:国内 华北-华东 延迟 25 ms,可 异步复制 binlog 到 OSS,再用 DLA(数据湖分析)即时查询,实现 15 min 级容灾演练
  4. 合规审计:等保 2.0 要求 备份保留 6 个月加密密钥与数据分离存储,建议 **备份文件命名带 shard-idhash,防止 勒索篡改