使用Ansible实现MySQL数据库自动备份与恢复的完整脚本与实战指南

引言:为什么选择Ansible管理MySQL备份
在现代运维环境中,数据库备份与恢复是保障数据安全的核心环节。MySQL作为最流行的开源数据库,其备份策略通常涉及
mysqldump
、
xtrabackup
等工具。然而,手动执行备份任务不仅效率低下,而且容易出错。Ansible作为一款强大的自动化工具,能够以声明式的方式管理备份任务,实现定时备份、远程存储、自动清理和一键恢复。
本文将详细介绍如何使用Ansible构建一套完整的MySQL自动备份与恢复系统,涵盖从基础配置到高级策略的完整流程。
一、环境准备与Ansible基础配置
1.1 环境要求
控制节点
:安装Ansible的Linux服务器(建议CentOS 7+/Ubuntu 18.04+)
被管理节点
:运行MySQL服务的服务器
网络
:控制节点与被管理节点之间SSH免密登录
1.2 安装Ansible
# CentOS/RHEL
yum install epel-release -y
yum install ansible -y

# Ubuntu/Debian
apt update
apt install ansible -y
1.3 配置Inventory文件
创建
inventory/hosts
文件,定义MySQL服务器组:
[mysql_servers]
db1 ansible_host=192.168.1.101 ansible_user=root
db2 ansible_host=192.168.1.102 ansible_user=root

[mysql_servers:vars]
ansible_ssh_private_key_file=/root/.ssh/id_rsa
1.4 测试连接
ansible mysql_servers -i inventory/hosts -m ping
二、MySQL备份策略设计
2.1 备份类型选择
全量备份
:使用
mysqldump
导出所有数据库
增量备份
:使用
Percona XtraBackup
(需额外安装)
备份频率
:每日全量备份 + 每小时增量备份(根据业务需求调整)
2.2 备份存储策略
本地存储
:/backup/mysql/
远程存储
:AWS S3、NFS或rsync到备份服务器
保留策略
:保留最近7天的备份文件
三、Ansible备份脚本实现
3.1 创建备份Playbook结构
mkdir -p ansible-mysql-backup/{roles,templates,files}
cd ansible-mysql-backup
3.2 定义变量
创建
roles/mysql_backup/defaults/main.yml
:
# MySQL连接信息
mysql_user: “backup_user”
mysql_password: “backup_password”
mysql_host: “localhost”
mysql_port: “3306”

# 备份目录
backup_base_dir: “/backup/mysql”
backup_date: “{{ ansible_date_time.date }}”
backup_file: “{{ backup_base_dir }}/mysql_backup_{{ backup_date }}.sql.gz”

# 备份保留天数
retention_days: 7

# S3配置(可选)
s3_bucket: “my-mysql-backups”
s3_region: “us-east-1”
3.3 创建备份任务Playbook
创建
playbooks/backup.yml
:
—
– name: MySQL数据库自动备份
hosts: mysql_servers
become: yes
roles:
– mysql_backup
3.4 实现备份Role
创建
roles/mysql_backup/tasks/main.yml
:
—
# 1. 安装依赖包
– name: 安装备份所需工具
yum:
name:
– mysql
– gzip
– awscli # 如果使用S3存储
state: present
when: ansible_os_family == “RedHat”

# 2. 创建备份目录
– name: 创建备份目录
file:
path: “{{ backup_base_dir }}”
state: directory
mode: ‘0755’
owner: root
group: root

# 3. 执行MySQL备份
– name: 执行mysqldump备份
shell: |
mysqldump -u{{ mysql_user }} -p{{ mysql_password }} -h{{ mysql_host }} -P{{ mysql_port }} \
–all-databases –single-transaction –quick –lock-tables=false | gzip > {{ backup_file }}
args:
executable: /bin/bash
register: backup_result
failed_when: backup_result.rc != 0

# 4. 验证备份文件
– name: 验证备份文件是否存在
stat:
path: “{{ backup_file }}”
register: backup_stat

– name: 备份文件大小检查
debug:
msg: “备份文件大小: {{ backup_stat.stat.size / 1024 / 1024 | round(2) }} MB”
when: backup_stat.stat.exists

# 5. 上传到S3(可选)
– name: 上传备份到S3
command: >
aws s3 cp {{ backup_file }} s3://{{ s3_bucket }}/mysql/{{ backup_date }}/
when:
– backup_stat.stat.exists
– s3_bucket is defined
environment:
AWS_ACCESS_KEY_ID: “{{ lookup(‘env’, ‘AWS_ACCESS_KEY_ID’) }}”
AWS_SECRET_ACCESS_KEY: “{{ lookup(‘env’, ‘AWS_SECRET_ACCESS_KEY’) }}”

# 6. 清理旧备份
– name: 清理超过保留天数的本地备份
shell: |
find {{ backup_base_dir }} -name “mysql_backup_*.sql.gz” -mtime +{{ retention_days }} -delete
args:
executable: /bin/bash
register: cleanup_result
changed_when: cleanup_result.stdout != “”

# 7. 记录日志
– name: 记录备份日志
lineinfile:
path: “{{ backup_base_dir }}/backup.log”
line: “{{ ansible_date_time.iso8601 }} – Backup: {{ backup_file }} – Size: {{ backup_stat.stat.size }} bytes”
create: yes
mode: ‘0644’
when: backup_stat.stat.exists
3.5 创建备份脚本模板
创建
roles/mysql_backup/templates/backup.sh.j2
:
#!/bin/bash
# MySQL自动备份脚本
# 由Ansible生成

set -e

# 配置
BACKUP_DIR=”{{ backup_base_dir }}”
DATE=”{{ backup_date }}”
BACKUP_FILE=”{{ backup_file }}”
RETENTION_DAYS={{ retention_days }}

# 创建目录
mkdir -p $BACKUP_DIR

# 执行备份
echo “[$(date)] 开始备份MySQL…”
mysqldump -u{{ mysql_user }} -p{{ mysql_password }} -h{{ mysql_host }} -P{{ mysql_port }} \
–all-databases –single-transaction –quick –lock-tables=false | gzip > $BACKUP_FILE

# 验证备份
if [ -f “$BACKUP_FILE” ] && [ -s “$BACKUP_FILE” ]; then
SIZE=$(du -h $BACKUP_FILE | cut -f1)
echo “[$(date)] 备份成功: $BACKUP_FILE ($SIZE)”

# 清理旧备份
find $BACKUP_DIR -name “mysql_backup_*.sql.gz” -mtime +$RETENTION_DAYS -delete

# 记录日志
echo “[$(date)] 备份完成” >> $BACKUP_DIR/backup.log
else
echo “[$(date)] 处理失败: 备份文件为空或不存在”
exit 1
fi
3.6 创建定时任务Role
创建
roles/mysql_backup/tasks/cron.yml
:
—
– name: 创建备份脚本
template:
src: backup.sh.j2
dest: /usr/local/bin/mysql_backup.sh
mode: ‘0755’

– name: 设置每日凌晨2点自动备份
cron:
name: “MySQL自动备份”
minute: “0”
hour: “2”
job: “/usr/local/bin/mysql_backup.sh > /var/log/mysql_backup.log 2>&1”
user: root
state: present
3.7 完整备份Playbook
更新
roles/mysql_backup/tasks/main.yml
包含定时任务:
—
– include_tasks: backup.yml
– include_tasks: cron.yml
四、MySQL恢复脚本实现
4.1 恢复策略设计
场景
:数据误删、数据库崩溃、迁移
原则
:先停止应用写入,再恢复数据,最后验证
要求
:恢复前必须有备份文件验证
4.2 创建恢复Playbook
创建
playbooks/restore.yml
:
—
– name: MySQL数据库恢复
hosts: mysql_servers
become: yes
vars_prompt:
– name: “backup_file”
prompt: “请输入要恢复的备份文件路径(绝对路径)”
private: no
– name: “confirm_restore”
prompt: “确认恢复吗?这将覆盖当前数据库(yes/no)”
private: no
when: false # 在playbook中验证

pre_tasks:
– name: 验证备份文件存在
stat:
path: “{{ backup_file }}”
register: backup_stat

– name: 备份文件不存在则退出
fail:
msg: “备份文件 {{ backup_file }} 不存在!”
when: not backup_stat.stat.exists

– name: 验证备份文件完整性
shell: “gzip -t {{ backup_file }}”
register: gzip_test
failed_when: gzip_test.rc != 0

– name: 确认恢复操作
pause:
prompt: “确认要恢复数据库吗?这将覆盖当前数据(输入yes继续)”
register: confirm
when: false # 实际使用时可启用

roles:
– mysql_restore
4.3 实现恢复Role
创建
roles/mysql_restore/tasks/main.yml
:
—
# 1. 停止应用写入(可选)
– name: 停止相关应用服务
systemd:
name: “{{ item }}”
state: stopped
loop: “{{ application_services | default([]) }}”
when: application_services is defined

# 2. 临时关闭MySQL严格模式(如果需要)
– name: 临时关闭MySQL严格模式
lineinfile:
path: /etc/my.cnf
line: “sql_mode=NO_ENGINE_SUBSTITUTION”
regexp: “^sql_mode=”
insertafter: EOF
notify: restart mysql

# 3. 重启MySQL使配置生效
– name: 重启MySQL服务
systemd:
name: mysqld
state: restarted
when: sql_mode_changed | default(false)

# 4. 执行恢复命令
– name: 执行MySQL恢复
shell: |
gunzip -c {{ backup_file }} | mysql -u{{ mysql_user }} -p{{ mysql_password }} -h{{ mysql_host }} -P{{ mysql_port }}
args:
executable: /bin/bash
register: restore_result
failed_when: restore_result.rc != 0

# 5. 验证恢复结果
– name: 检查关键表是否存在
shell: |
mysql -u{{ mysql_user }} -p{{ mysql_password }} -h{{ mysql_host }} -P{{ mysql_port }} -e “SHOW TABLES;” | grep -E “{{ critical_tables | join(‘|’) }}”
args:
executable: /bin/bash
register: table_check
failed_when: table_check.rc != 0
when: critical_tables is defined

# 6. 恢复MySQL严格模式
– name: 恢复MySQL严格模式
lineinfile:
path: /etc/my.cnf
line: “sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION”
regexp: “^sql_mode=”
insertafter: EOF
notify: restart mysql
when: sql_mode_changed | default(false)

# 7. 启动应用服务
– name: 启动相关应用服务
systemd:
name: “{{ item }}”
state: started
loop: “{{ application_services | default([]) }}”
when: application_services is defined

# 8. 记录恢复日志
– name: 记录恢复日志
lineinfile:
path: “{{ backup_base_dir }}/restore.log”
line: “{{ ansible_date_time.iso8601 }} – Restored: {{ backup_file }}”
create: yes
mode: ‘0644’
4.4 创建恢复脚本模板
创建
roles/mysql_restore/templates/restore.sh.j2
:
#!/bin/bash
# MySQL恢复脚本
# 由Ansible生成

set -e

# 配置
BACKUP_FILE=”{{ backup_file }}”
MYSQL_USER=”{{ mysql_user }}”
MYSQL_PASS=”{{ mysql_password }}”
MYSQL_HOST=”{{ mysql_host }}”
MYSQL_PORT=”{{ mysql_port }}”

# 验证备份文件
if [ ! -f “$BACKUP_FILE” ]; then
echo “错误: 备份文件 $BACKUP_FILE 不存在”
exit 1
fi

# 验证备份文件完整性
if ! gzip -t “$BACKUP_FILE”; then
echo “错误: 备份文件损坏”
exit 1
fi

# 确认恢复
read -p “确认恢复数据库?这将覆盖当前数据 (yes/no): ” CONFIRM
if [ “$CONFIRM” != “yes” ]; then
echo “恢复已取消”
exit 0
fi

# 执行恢复
echo “[$(date)] 开始恢复…”
gunzip -c “$BACKUP_FILE” | mysql -u$MYSQL_USER -p$MYSQL_PASS -h$MYSQL_HOST -P$MYSQL_PORT

# 验证恢复
if [ $? -eq 0 ]; then
echo “[$(date)] 恢复成功”
echo “[$(date)] 恢复文件: $BACKUP_FILE” >> /backup/mysql/restore.log
else
echo “[$(date)] 恢复失败”
exit 1
fi
五、高级策略与优化
5.1 使用Percona XtraBackup实现热备份
对于大型数据库(>100GB),
mysqldump
效率较低,建议使用Percona XtraBackup。
安装XtraBackup
:
# roles/mysql_backup/tasks/xtrabackup.yml
– name: 安装Percona XtraBackup
yum:
name: https://repo.percona.com/yum/percona-release-latest.noarch.rpm
state: present

– name: 安装XtraBackup 8.0
yum:
name: percona-xtrabackup-80
state: present
XtraBackup备份任务
:
– name: 执行XtraBackup全量备份
shell: |
xtrabackup –backup –user={{ mysql_user }} –password={{ mysql_password }} \
–target-dir={{ backup_base_dir }}/xtrabackup_{{ backup_date }} \
–compress –compress-threads=4
args:
executable: /bin/bash
5.2 备份加密
– name: 使用GPG加密备份
shell: |
gpg –cipher-algo AES256 –compress-algo 1 –symmetric –batch –passphrase-file /etc/backup.key \
–output {{ backup_file }}.gpg {{ backup_file }}
args:
executable: /bin/bash
5.3 监控与告警
创建
roles/mysql_backup/tasks/monitor.yml
:
– name: 检查备份文件大小
fail:
msg: “备份文件过小,可能备份失败”
when: backup_stat.stat.size < 102400 # 小于100KB - name: 发送告警邮件(示例) mail: subject: "MySQL备份失败: {{ inventory_hostname }}" body: "备份文件大小异常: {{ backup_stat.stat.size }} bytes" to: admin@example.com from: ansible@example.com host: smtp.example.com when: backup_stat.stat.size < 102400 六、完整项目结构与使用指南 6.1 项目目录结构 ansible-mysql-backup/ ├── inventory/ │ └── hosts ├── roles/ │ ├── mysql_backup/ │ │ ├── defaults/ │ │ │ └── main.yml │ │ ├── tasks/ │ │ │ ├── main.yml │ │ │ ├── backup.yml │ │ │ ├── cron.yml │ │ │ └── monitor.yml │ │ └── templates/ │ │ └── backup.sh.j2 │ └── mysql_restore/ │ ├── tasks/ │ │ └── main.yml │ └── templates/ │ └── restore.sh.j2 ├── playbooks/ │ ├── backup.yml │ �3── restore.yml └── README.md 6.2 执行备份 # 手动执行备份 ansible-playbook -i inventory/hosts playbooks/backup.yml # 批量执行 ansible-playbook -i inventory/hosts playbooks/backup.yml --limit db1 # 调试模式 ansible-playbook -i inventory/hosts playbooks/backup.yml --check --diff 6.3 执行恢复 # 交互式恢复 ansible-playbook -i inventory/hosts playbooks/restore.yml # 指定备份文件恢复 ansible-playbook -i inventory/hosts playbooks/restore.yml -e "backup_file=/backup/mysql/mysql_backup_2024-01-15.sql.gz" 6.4 定时任务管理 # 查看定时任务 ansible mysql_servers -i inventory/hosts -m shell -a "crontab -l" # 临时禁用备份 ansible mysql_servers -i inventory/hosts -m cron -a "name='MySQL自动备份' state=absent" 七、安全最佳实践 7.1 凭证管理 不要 在Playbook中明文存储密码,使用Ansible Vault: # 创建加密变量文件 ansible-vault create roles/mysql_backup/vars/vault.yml # 在vars/main.yml中引用 mysql_password: "{{ vault_mysql_password }}" 7.2 文件权限 - name: 设置备份目录权限 file: path: "{{ backup_base_dir }}" state: directory mode: '0700' owner: root group: root 7.3 网络传输安全 使用SSH密钥认证 备份传输使用VPN或专线 S3传输启用服务器端加密 八、故障排查与常见问题 8.1 备份失败常见原因 权限不足 :确保备份用户有 SELECT , SHOW VIEW , TRIGGER 权限 磁盘空间不足 :添加空间检查任务 MySQL连接失败 :检查网络、防火墙、MySQL配置 8.2 恢复失败常见原因 版本不兼容 :备份与恢复的MySQL版本需一致 表已存在 :恢复前需清空数据库 外键约束 :临时关闭外键检查 8.3 调试技巧 # 查看详细输出 - name: 调试备份命令 debug: msg: "执行命令: mysqldump -u{{ mysql_user }} -p***@{{ mysql_host }}:{{ mysql_port }}" # 注册变量调试 - name: 调试备份结果 debug: var: backup_result 九、总结 通过Ansible实现MySQL自动备份与恢复,我们获得了以下优势: 标准化 :所有服务器使用相同备份流程 可追溯 :完整日志记录与版本控制 自动化 :定时任务减少人工干预 可扩展 :轻松管理多实例、多环境 建议将此方案与CI/CD流程结合,定期进行恢复演练,确保备份有效性。对于生产环境,建议采用”3-2-1”备份策略:3份副本、2种介质、1份异地。 附录:常用命令速查 # 查看备份状态 ansible mysql_servers -m shell -a "ls -lh /backup/mysql/" # 手动清理旧备份 ansible mysql_servers -m shell -a "find /backup/mysql/ -name '*.sql.gz' -mtime +7 -delete" # 验证备份可恢复性(测试环境) ansible-playbook -i inventory/hosts playbooks/restore.yml -e "backup_file=/backup/mysql/mysql_backup_2024-01-15.sql.gz" --check