前言
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\n" "$(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 "${innobackupex_args[@]}