前言
CentOS 7 下 MySQL 5.7 配置 Percona Xtrabackup ,记录一下大致的安装和配置过程。
Percona XtraBackup 的备份工具支持热备份(即不必停止 MySQL 服务而进行备份)。热备份方式主要是通过文件系统级别的文件拷贝,当需要执行崩溃恢复时,可以实现数据集内的一致性。
参考 How To Configure MySQL Backups with Percona XtraBackup on Ubuntu 16.04
参考文档使用的操作系统为 Ubuntu 16.04,本例将操作系统改为 CentOS 7,命令基本一致,主要示例一下数据库的全备份,增量备份以及数据恢复。
环境说明
CentOS 7(Minimal Install)
$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)
MySQL 5.7
$ mysql --version
mysql Ver 14.14 Distrib 5.7.18, for Linux (x86_64) using EditLine wrapper
本系统初始有两个用户
超级管理员: root
管理组用户: admin
安装 Percona Xtrabackup 工具
参考 Installing Percona XtraBackup on Red Hat Enterprise Linux and CentOS
安装 yum 源
$ sudo yum install http://www.percona.com/downloads/percona-release/redhat/0.1-4/percona-release-0.1-4.noarch.rpm
查询一下安装包
$ sudo yum list | grep xtrabackup
percona-xtrabackup.x86_64 2.3.8-1.el7 percona-release-x86_64
percona-xtrabackup-22.x86_64 2.2.13-1.el7 percona-release-x86_64
percona-xtrabackup-22-debuginfo.x86_64 2.2.13-1.el7 percona-release-x86_64
percona-xtrabackup-24.x86_64 2.4.7-1.el7 percona-release-x86_64
percona-xtrabackup-24-debuginfo.x86_64 2.4.7-1.el7 percona-release-x86_64
percona-xtrabackup-debuginfo.x86_64 2.3.8-1.el7 percona-release-x86_64
percona-xtrabackup-test.x86_64 2.3.8-1.el7 percona-release-x86_64
percona-xtrabackup-test-22.x86_64 2.2.13-1.el7 percona-release-x86_64
percona-xtrabackup-test-24.x86_64 2.4.7-1.el7 percona-release-x86_64
安装 Xtrabackup
和 qpress
压缩工具。
$ sudo yum update
$ sudo yum install percona-xtrabackup-24 qpress
安装之后,innobackupex
, xtrabackup
, xbstream
, 和 qpress
命令将可以使用,本例的脚本会使用这些命令进行数据的备份和恢复。
配置一个 MySQL 备份用户并且添加测试数据
首先使用 MySQL 的 root
用户登录。
$ mysql -u root -p
创建一个 MySQL 用户并且授权
在 MySQL 中创建一个用户名为 backup
的用户,并且分配备份的相关权限给它。
mysql> CREATE USER 'backup'@'localhost' IDENTIFIED BY 'password';
授予备份的相关权限
mysql> GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT, CREATE TABLESPACE, PROCESS, SUPER, CREATE, INSERT, SELECT ON *.* TO 'backup'@'localhost';
mysql> FLUSH PRIVILEGES;
创建测试数据,创建一个 playground 的数据库,创建一个 equipment 的表,并且添加一条记录到这个表里。
mysql> CREATE DATABASE playground;
mysql> CREATE TABLE playground.equipment ( id INT NOT NULL AUTO_INCREMENT, type VARCHAR(50), quant INT, color VARCHAR(25), PRIMARY KEY(id));
mysql> INSERT INTO playground.equipment (type, quant, color) VALUES ("slide", 2, "blue");
此后我们将用这个数据库查看测试备份和恢复的效果。
mysql> SELECT * FROM playground.equipment;
+----+-------+-------+-------+
| id | type | quant | color |
+----+-------+-------+-------+
| 1 | slide | 2 | blue |
+----+-------+-------+-------+
1 row in set (0.00 sec)
在我们退出 MySQL 会话前,我们先检查一下 datadir
变量。因为我们还要创建一个操作系统的 backup
用户,而且这个需要有权限访问这个目录。
mysql> SELECT @@datadir;
+-----------------+
| @@datadir |
+-----------------+
| /var/lib/mysql/ |
+-----------------+
1 row in set (0.00 sec)
记住这个目录,现在可以退出 MySQL 了。
mysql> exit
Bye
配置操作系统的备份用户并授权
创建 backup 用户,不需要登录系统,没有 home 目录
$ sudo useradd -M -s /sbin/nologin backup
确认一下 backup 用户和组
$ grep backup /etc/passwd /etc/group
/etc/passwd:backup:x:1001:1001::/home/backup:/sbin/nologin
/etc/group:backup:x:1001:
MySQL 的数据目录 /var/lib/mysql
的所有者和所有组是 mysql
。
- 我们需要将 backup 加入到 mysql 组里,这样 backup 就可以访问 mysql 组的目录和文件。
- 我们需要将
sudo
加入到 backup 组里,这样我们就可以访问 backup 用户和组权限的目录和文件。
命令执行如下
$ sudo usermod -aG mysql backup
$ sudo usermod -aG backup ${USER}
此时我们再检查一下 backup 的组
$ grep backup /etc/group
mysql:x:27:backup
backup:x:1001:admin
新加入的组不会立即生效,需要执行如下命令
$ exec su - ${USER}
执行之后,可以使用如下命令确认
$ id -nG
admin wheel backup
注意:
admin
是 admin 的组,wheel
是 CentOS 默认的 sudo 组,backup
是新增的 mysql 备份的组。
创建备份相关的资源
现在我们已经有了 Mysql 用户 backup
, 系统用户 backup
,我们需要创建配置文件,密钥文件和其他脚本,这样我们就可以创建并保护备份的安全。
创建 MySQL 备份的配置文件
我们将 MySQL 备份用户 backup
的用户名和密码放到配置中。
$ sudo mkdir /etc/mysql
$ sudo vi /etc/mysql/backup.cnf
加入如下内容:
[client]
user=backup
password=password
保存并退出 :wq
,这样 MySQL 的配置文件就创建完毕,以后备份用户就会使用这个文件的配置登录 MySQL ,注意 password=password
这个密码是 MySQL 里面的用户 backup
的密码。
创建备份的根目录
本例使用 /backups/mysql
为备份文件的根目录,使用如下命令创建:
$ sudo mkdir -p /backups/mysql
对这个目录的所有者和所有组进行分配, 所有者为 backup,所有组为 mysql
$ sudo chown backup:mysql /backups/mysql
这样 backup 用户就可以进入并操作这个目录了
创建密钥保护备份文件安全
因为数据库文件是非常重要的文件,所以安全非常重要。 innobackupex
工具提供了加密和解密的功能。为此,我们只需要提供一个密钥即可。
我们使用 openssl
命令来创建一个密钥
$ printf '%s' "$(openssl rand -base64 24)" | sudo tee /backups/mysql/encryption_key && echo
修改这个文件的权限,保证这个文件只能 backup
用户可以使用。
$ sudo chown backup:backup /backups/mysql/encryption_key
$ sudo chmod 600 /backups/mysql/encryption_key
创建备份和恢复的脚本
目前安全方面的准备已经完成,我们需要创建 3 个脚本,来执行备份(加密备份),释放(解密)和恢复准备的工作。
backup-mysql.sh
: 这个脚本完成备份数据库,对备份的文件进行加密和压缩,他会根据日期创建全量和增量的备份,默认保存 3 天的备份。extract-mysql.sh
: 这个脚本会解压并解密备份文件,并将文件放到指定的文件夹中。prepare-mysql.sh
: 这个脚本为恢复做最后的准备,将增量文件合并到全备份中。并且记录执行的日志,如果这个脚本执行完,那么剩下的就是将恢复的数据库替换即可。
创建 backup-mysql.sh 脚本
$ sudo vi /usr/local/bin/backup-mysql.sh
脚本内容如下:
#!/bin/bash
export LC_ALL=C
days_of_backups=3 # Must be less than 7
backup_owner="backup"
parent_dir="/backups/mysql"
defaults_file="/etc/mysql/backup.cnf"
todays_dir="${parent_dir}/$(date +%a)"
log_file="${todays_dir}/backup-progress.log"
encryption_key_file="${parent_dir}/encryption_key"
now="$(date +%m-%d-%Y_%H-%M-%S)"
processors="$(nproc --all)"
# Use this to echo to standard error
error () {
printf "%s: %s
" "$(basename "${BASH_SOURCE}")" "${1}" >&2
exit 1
}
trap 'error "An unexpected error occurred."' ERR
sanity_check () {
# Check user running the script
if [ "$USER" != "$backup_owner" ]; then
error "Script can only be run as the "$backup_owner" user"
fi
# Check whether the encryption key file is available
if [ ! -r "${encryption_key_file}" ]; then
error "Cannot read encryption key at ${encryption_key_file}"
fi
}
set_options () {
# List the innobackupex arguments
#declare -ga innobackupex_args=(
innobackupex_args=(
"--defaults-file=${defaults_file}"
"--extra-lsndir=${todays_dir}"
"--compress"
"--stream=xbstream"
"--encrypt=AES256"
"--encrypt-key-file=${encryption_key_file}"
"--parallel=${processors}"
"--compress-threads=${processors}"
"--encrypt-threads=${processors}"
"--slave-info"
"--incremental"
)
backup_type="full"
# Add option to read LSN (log sequence number) if a full backup has been
# taken today.
if grep -q -s "to_lsn" "${todays_dir}/xtrabackup_checkpoints"; then
backup_type="incremental"
lsn=$(awk '/to_lsn/ {print $3;}' "${todays_dir}/xtrabackup_checkpoints")
innobackupex_args+=( "--incremental-lsn=${lsn}" )
fi
}
rotate_old () {
# Remove the oldest backup in rotation
day_dir_to_remove="${parent_dir}/$(date --date="${days_of_backups} days ago" +%a)"
if [ -d "${day_dir_to_remove}" ]; then
rm -rf "${day_dir_to_remove}"
fi
}
take_backup () {
# Make sure today's backup directory is available and take the actual backup
mkdir -p "${todays_dir}"
find "${todays_dir}" -type f -name "*.incomplete" -delete
innobackupex "${