序章

多くの場合、データベースはインフラストラクチャに最も価値のある情報の一部を格納します。 このため、事故やハードウェア障害が発生した場合のデータ損失を防ぐために、信頼性の高いバックアップを用意することが重要です。

Percona XtraBackupバックアップツールは、システムの実行中にMySQLデータの「ホット」バックアップを実行する方法を提供します。 これを行うには、ファイルシステムレベルでデータファイルをコピーしてから、クラッシュリカバリを実行して、データセット内の一貫性を実現します。

このガイドでは、Ubuntu16.04サーバー上のMySQLデータのバックアップを自動化するシステムを作成します。 スクリプトのグループでcronツールとPerconaツールを使用して、問題が発生した場合の回復に使用できる定期的で安全なバックアップを作成します。

前提条件

このガイドを完了するには、ルート以外のsudoユーザーが管理タスク用に構成されたUbuntu16.04サーバーが必要です。 「Ubuntu16.04でのサーバーの初期設定」ガイドに従って、サーバーでこれらの権限を持つユーザーを設定できます。

sudoユーザーを利用できるようになったら、MySQLをインストールする必要があります。 使用するパッケージに応じて、これらのガイドのいずれかを使用できます。 最初のガイドは、公式のUbuntuリポジトリを使い続けたい場合に適していますが、2番目のガイドは、より最新の機能が必要な場合に適しています。

MySQLがインストールされたら、sudoユーザーとしてサーバーにログインして続行します。

PerconaXtrabackupツールのインストール

最初に行う必要があるのは、実際のPerconaバックアップユーティリティをインストールすることです。 プロジェクトは、パッケージにアクセスするためにMySQLサーバーに追加できる独自のリポジトリを維持しています。

まず、UbuntuPerconaリリースページに移動して、リポジトリをインストールするための最新の.debパッケージを見つけます。 コードネーム「XenialXerus」のUbuntu16.04を使用しているため、「xenial」パッケージを選択する必要があります。 対応するリンクを右クリックして、アドレスをコピーします。

注:次のように入力すると、いつでもサーバーのリリースコードネームを再確認できます。

  1. lsb_release -c
Output
Codename: xenial

リンクをコピーしたら、/tmpディレクトリに移動し、curlを含むリポジトリ構成パッケージをダウンロードします。

  1. cd /tmp
  2. curl -LO https://repo.percona.com/apt/percona-release_0.1-4.xenial_all.deb

次に、dpkgを使用して、ダウンロードしたパッケージをインストールします。これにより、システムにPerconaaptリポジトリが構成されます。

  1. sudo dpkg -i percona*

新しいリポジトリを構成したら、ローカルパッケージインデックスを更新して、新しく利用可能なパッケージに関する情報を取得します。 次に、リポジトリからXtraBackupツールとqpress圧縮ユーティリティをインストールします。

  1. sudo apt-get update
  2. sudo apt-get install percona-xtrabackup-24 qpress

バンドルされている他のユーティリティの中でも、xtrabackupxbstream、およびqpressコマンドが使用可能になります。 スクリプトは、これらのそれぞれを使用して、バックアップを実行し、データを復元します。

MySQLバックアップユーザーの構成とテストデータの追加

まず、MySQLrootユーザーとの対話型MySQLセッションを開始します。

  1. mysql -u root -p

MySQLのインストール中に選択した管理者パスワードの入力を求められます。 パスワードを入力すると、MySQLセッションに移動します。

適切な権限を持つMySQLユーザーを作成する

最初に行う必要があるのは、バックアップタスクを処理するように構成された新しいMySQLユーザーを作成することです。 このユーザーには、システムの実行中にデータを安全にコピーするために必要な権限のみが付与されます。

アカウントの目的を明確にするために、新しいユーザーをbackupと呼びます。 ユーザーの資格情報を安全なファイルに配置するので、複雑なパスワードを自由に選択してください。

  1. CREATE USER 'backup'@'localhost' IDENTIFIED BY 'password';

次に、新しいbackupユーザーに、データベースシステムですべてのバックアップアクションを実行するために必要なアクセス許可を付与する必要があります。 必要な権限を付与し、次のように入力して現在のセッションに適用します。

  1. GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT, CREATE TABLESPACE, PROCESS, SUPER, CREATE, INSERT, SELECT ON *.* TO 'backup'@'localhost';
  2. FLUSH PRIVILEGES;

MySQLバックアップユーザーが構成されており、必要なアクセス権があります。

バックアップのテストデータを作成する

次に、いくつかのテストデータを作成します。 次のコマンドを実行して、equipmentテーブルを含むplaygroundデータベースを作成します。 青いスライドを表す単一のレコードを挿入することから始めます。

  1. CREATE DATABASE playground;
  2. CREATE TABLE playground.equipment ( id INT NOT NULL AUTO_INCREMENT, type VARCHAR(50), quant INT, color VARCHAR(25), PRIMARY KEY(id));
  3. INSERT INTO playground.equipment (type, quant, color) VALUES ("slide", 2, "blue");

このガイドの後半では、このデータを使用および変更して、完全バックアップと増分バックアップを作成する機能をテストします。

MySQLセッションを終了する前に、datadir変数の値を確認します。 システムレベルのbackupユーザーがMySQLデータファイルにアクセスできるようにするには、この値を知る必要があります。

次のように入力して、datadir変数の値を表示します。

  1. SELECT @@datadir;
Output
+-----------------+ | @@datadir | +-----------------+ | /var/lib/mysql/ | +-----------------+ 1 row in set (0.01 sec)

あなたが見つけた場所をメモしてください。

現時点でMySQL内で行う必要があるのはこれだけです。 次のように入力して、シェルを終了します。

  1. exit

次に、いくつかのシステムレベルの構成を見てみましょう。

システムバックアップユーザーの構成とアクセス許可の割り当て

バックアップを実行するMySQLユーザーができたので、対応するLinuxユーザーが同様の制限された特権で存在することを確認します。

Ubuntu 16.04では、backupユーザーと対応するbackupグループがすでに利用可能です。 次のコマンドで/etc/passwdおよび/etc/groupファイルを確認して、これを確認します。

  1. grep backup /etc/passwd /etc/group
Output
/etc/passwd:backup:x:34:34:backup:/var/backups:/usr/sbin/nologin /etc/group:backup:x:34:

/etc/passwdファイルの1行目は、backupユーザーを示し、/etc/groupファイルの2行目は、backupグループを示しています。

MySQLデータが保持されている/var/lib/mysqlディレクトリは、mysqlユーザーとグループによって所有されています。 backupユーザーをmysqlグループに追加して、データベースファイルとディレクトリへのアクセスを安全に許可できます。 また、sudoユーザーをbackupグループに追加して、バックアップするファイルにアクセスできるようにする必要があります。

次のコマンドを入力して、backupユーザーをmysqlグループに追加し、sudoユーザーをbackupグループに追加します。

  1. sudo usermod -aG mysql backup
  2. sudo usermod -aG backup ${USER}

/etc/groupファイルを再度確認すると、現在のユーザーがbackupグループに追加され、backupユーザーがmysqlに追加されていることがわかります。 ] グループ:

  1. grep backup /etc/group
Output
backup:x:34:sammy mysql:x:116:backup

新しいグループは、現在のセッションでは自動的に利用できません。 sudoユーザーが利用できるグループを再評価するには、ログアウトして再度ログインするか、次のように入力します。

  1. exec su - ${USER}

続行するには、sudoユーザーのパスワードの入力を求められます。 ユーザーのグループをもう一度確認して、現在のセッションがbackupグループにアクセスできるようになったことを確認します。

  1. id -nG
Output
sammy sudo backup

sudoユーザーは、backupグループのメンバーシップを利用できるようになります。

次に、グループ実行権限を追加して、/var/lib/mysqlディレクトリとそのサブディレクトリにmysqlグループからアクセスできるようにする必要があります。 そうしないと、backupユーザーは、mysqlグループのメンバーであっても、これらのディレクトリに入ることができなくなります。

注:以前にMySQLの内部をチェックしたときに、datadirの値が/var/lib/mysqlでなかった場合は、次のコマンドで見つけたディレクトリに置き換えてください。

mysqlグループにMySQLデータディレクトリへのアクセスを許可するには、次のように入力します。

  1. sudo find /var/lib/mysql -type d -exec chmod 750 {} \;

これで、backupユーザーは、MySQLディレクトリに必要なアクセス権を取得できます。

バックアップ資産の作成

MySQLとシステムバックアップユーザーが利用できるようになったので、バックアップを正常に作成して保護するために必要な構成ファイル、暗号化キー、およびその他のアセットのセットアップを開始できます。

バックアップパラメータを使用してMySQL構成ファイルを作成します

バックアップスクリプトが使用する最小限のMySQL構成ファイルを作成することから始めます。 これには、MySQLユーザーのMySQLクレデンシャルが含まれます。

テキストエディタで/etc/mysql/backup.cnfにあるファイルを開きます。

  1. sudo nano /etc/mysql/backup.cnf

内部で、[client]セクションを開始し、MySQL内で定義したMySQLバックアップユーザーとパスワードユーザーを設定します。

/etc/mysql/backup.cnf
[client]
user=backup
password=password

終了したら、ファイルを保存して閉じます。

ファイルの所有権をbackupユーザーに付与してから、他のユーザーがファイルにアクセスできないようにアクセス許可を制限します。

  1. sudo chown backup /etc/mysql/backup.cnf
  2. sudo chmod 600 /etc/mysql/backup.cnf

バックアップユーザーはこのファイルにアクセスして適切な資格情報を取得できますが、他のユーザーは制限されます。

バックアップルートディレクトリを作成する

次に、バックアップコンテンツ用のディレクトリを作成します。 バックアップのベースディレクトリとして/backups/mysqlを使用します。

  1. sudo mkdir -p /backups/mysql

次に、/backups/mysqlディレクトリの所有権をbackupユーザーに割り当て、グループの所有権をmysqlグループに割り当てます。

  1. sudo chown backup:mysql /backups/mysql

backupユーザーは、この場所にバックアップデータを書き込めるようになります。

バックアップファイルを保護するための暗号化キーを作成する

バックアップにはデータベースシステム自体からのすべてのデータが含まれているため、それらを適切に保護することが重要です。 xtrabackupユーティリティには、バックアップおよびアーカイブ時に各ファイルを暗号化する機能があります。 暗号化キーを提供する必要があります。

opensslコマンドを使用して、バックアップルートディレクトリ内に暗号化キーを作成できます。

  1. printf '%s' "$(openssl rand -base64 24)" | sudo tee /backups/mysql/encryption_key && echo

このファイルへのアクセスも制限することが非常に重要です。 ここでも、backupユーザーに所有権を割り当て、他のすべてのユーザーへのアクセスを拒否します。

  1. sudo chown backup:backup /backups/mysql/encryption_key
  2. sudo chmod 600 /backups/mysql/encryption_key

このキーは、バックアッププロセス中、およびバックアップから復元する必要があるときに使用されます。

バックアップと復元のスクリプトの作成

これで、実行中のMySQLインスタンスの安全なバックアップを実行するために必要なものがすべて揃いました。

バックアップと復元の手順を繰り返し可能にするために、プロセス全体をスクリプト化します。 次のスクリプトを作成します。

  • backup-mysql.sh:このスクリプトは、MySQLデータベースをバックアップし、プロセスでファイルを暗号化および圧縮します。 完全バックアップと増分バックアップを作成し、コンテンツを日ごとに自動的に整理します。 デフォルトでは、スクリプトは3日分のバックアップを維持します。
  • extract-mysql.sh:このスクリプトは、バックアップファイルを解凍および復号化して、バックアップされたコンテンツを含むディレクトリを作成します。
  • prepare-mysql.sh:このスクリプトは、ファイルを処理してログを適用することにより、バックアップディレクトリを「準備」します。 増分バックアップはすべて、完全バックアップに適用されます。 準備スクリプトが終了すると、ファイルをデータディレクトリに戻す準備が整います。

このチュートリアルのスクリプトは、GitHubリポジトリでいつでも表示できます。 以下のコンテンツをコピーして貼り付けたくない場合は、次のように入力してGitHubから直接ダウンロードできます。

  1. cd /tmp
  2. curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/backup-mysql.sh
  3. curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/extract-mysql.sh
  4. curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/prepare-mysql.sh

ダウンロード後にスクリプトを調べて、スクリプトが正常に取得されたことと、スクリプトが実行するアクションを承認していることを確認してください。 満足のいく場合は、スクリプトを実行可能としてマークし、次のように入力して/usr/local/binディレクトリに移動します。

  1. chmod +x /tmp/{backup,extract,prepare}-mysql.sh
  2. sudo mv /tmp/{backup,extract,prepare}-mysql.sh /usr/local/bin

次に、これらの各スクリプトを設定し、詳細に説明します。

backup-mysql.shスクリプトを作成します

GitHubからbackup-mysql.shスクリプトをダウンロードしなかった場合は、/usr/local/binディレクトリにbackup-mysql.shという名前の新しいファイルを作成します。

  1. sudo nano /usr/local/bin/backup-mysql.sh

スクリプトの内容をコピーしてファイルに貼り付けます。

/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 [ "$(id --user --name)" != "$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 xtrabackup arguments
    xtrabackup_args=(
        "--defaults-file=${defaults_file}"
        "--backup"
        "--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"
    )
    
    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")
        xtrabackup_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
    xtrabackup "${xtrabackup_args[@]}" --target-dir="${todays_dir}" > "${todays_dir}/${backup_type}-${now}.xbstream.incomplete" 2> "${log_file}"
    
    mv "${todays_dir}/${backup_type}-${now}.xbstream.incomplete" "${todays_dir}/${backup_type}-${now}.xbstream"
}

sanity_check && set_options && rotate_old && take_backup

# Check success and print message
if tail -1 "${log_file}" | grep -q "completed OK"; then
    printf "Backup successful!\n"
    printf "Backup created at %s/%s-%s.xbstream\n" "${todays_dir}" "${backup_type}" "${now}"
else
    error "Backup failure! Check ${log_file} for more information"
fi

スクリプトには次の機能があります。

  • 毎日最初に実行されるときに、暗号化された圧縮された完全バックアップを作成します。
  • 同じ日に再度呼び出されたときに、毎日の完全バックアップに基づいて、暗号化された圧縮された増分バックアップを生成します。
  • 日ごとに整理されたバックアップを維持します。 デフォルトでは、3日間のバックアップが保持されます。 これは、スクリプト内のdays_of_backupsパラメーターを調整することで変更できます。

スクリプトが実行されると、個々のバックアップを表すタイムスタンプ付きファイルが書き込まれる毎日のディレクトリが作成されます。 最初のタイムスタンプ付きファイルは、full-というプレフィックスが付いた完全バックアップになります。 その日の後続のバックアップは、incremental-プレフィックスで示される増分バックアップであり、最後の完全バックアップまたは増分バックアップ以降の変更を表します。

バックアップは、最新のバックアップ操作からの出力を含むbackup-progress.logというファイルをdailyディレクトリに生成します。 最新のバックアップメタデータを含むxtrabackup_checkpointsというファイルもそこに作成されます。 このファイルは将来の増分バックアップを作成するために必要なので、削除しないことが重要です。 追加のメタデータを含むxtrabackup_infoというファイルも作成されますが、スクリプトはこのファイルを参照しません。

終了したら、ファイルを保存して閉じます。

次に、まだ実行していない場合は、次のように入力してスクリプトを実行可能にします。

  1. sudo chmod +x /usr/local/bin/backup-mysql.sh

これで、MySQLバックアップを開始する単一のコマンドを使用できるようになりました。

extract-mysql.shスクリプトを作成します

次に、extract-mysql.shスクリプトを作成します。 これは、個々のバックアップファイルからMySQLデータディレクトリ構造を抽出するために使用されます。

リポジトリからスクリプトをダウンロードしなかった場合は、/usr/local/binディレクトリにextract-mysql.shというファイルを作成して開きます。

  1. sudo nano /usr/local/bin/extract-mysql.sh

内部に、次のスクリプトを貼り付けます。

/usr/local/bin/extract-mysql.sh
#!/bin/bash

export LC_ALL=C

backup_owner="backup"
encryption_key_file="/backups/mysql/encryption_key"
log_file="extract-progress.log"
number_of_args="${#}"
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.  Try checking the \"${log_file}\" file for more information."' 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 qpress binary is installed
    if ! command -v qpress >/dev/null 2>&1; then
        error "Could not find the \"qpress\" command.  Please install it and try again."
    fi
    
    # Check whether any arguments were passed
    if [ "${number_of_args}" -lt 1 ]; then
        error "Script requires at least one \".xbstream\" file as an argument."
    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
}

do_extraction () {
    for file in "${@}"; do
        base_filename="$(basename "${file%.xbstream}")"
        restore_dir="./restore/${base_filename}"
    
        printf "\n\nExtracting file %s\n\n" "${file}"
    
        # Extract the directory structure from the backup file
        mkdir --verbose -p "${restore_dir}"
        xbstream -x -C "${restore_dir}" < "${file}"

        xtrabackup_args=(
            "--parallel=${processors}"
            "--decrypt=AES256"
            "--encrypt-key-file=${encryption_key_file}"
            "--decompress"
        )

        xtrabackup "${xtrabackup_args[@]}" --target-dir="${restore_dir}"
        find "${restore_dir}" -name "*.xbcrypt" -exec rm {} \;
        find "${restore_dir}" -name "*.qp" -exec rm {} \;
    
        printf "\n\nFinished work on %s\n\n" "${file}"
    
    done > "${log_file}" 2>&1
}

sanity_check && do_extraction "[email protected]"

ok_count="$(grep -c 'completed OK' "${log_file}")"

# Check the number of reported completions.  For each file, there is an
# informational "completed OK".  If the processing was successful, an
# additional "completed OK" is printed. Together, this means there should be 2
# notices per backup file if the process was successful.
if (( $ok_count !=  $# )); then
    error "It looks like something went wrong. Please check the \"${log_file}\" file for additional information"
else
    printf "Extraction complete! Backup directories have been extracted to the \"restore\" directory.\n"
fi

自動化されるように設計されたbackup-mysql.shスクリプトとは異なり、このスクリプトは、バックアップからの復元を計画しているときに意図的に使用されるように設計されています。 このため、スクリプトは、抽出する.xbstreamファイルを渡すことを想定しています。

スクリプトは、現在のディレクトリ内にrestoreディレクトリを作成し、引数として渡されたバックアップごとに個別のディレクトリを作成します。 アーカイブからディレクトリ構造を抽出し、その中の個々のファイルを復号化してから、復号化されたファイルを解凍することにより、提供された.xbstreamファイルを処理します。

このプロセスが完了すると、restoreディレクトリには、提供された各バックアップのディレクトリが含まれるはずです。 これにより、ディレクトリを検査し、バックアップの内容を調べて、準備および復元するバックアップを決定できます。

終了したら、ファイルを保存して閉じます。 その後、次のように入力して、スクリプトが実行可能であることを確認します。

  1. sudo chmod +x /usr/local/bin/extract-mysql.sh

このスクリプトを使用すると、個々のバックアップファイルを復元に必要なディレクトリ構造に拡張できます。

prepare-mysql.shスクリプトを作成します

最後に、/usr/local/binディレクトリ内にprepare-mysql.shスクリプトをダウンロードまたは作成します。 このスクリプトは、ログを各バックアップに適用して、一貫性のあるデータベーススナップショットを作成します。 増分バックアップを完全バックアップに適用して、後の変更を組み込みます。

以前にダウンロードしていない場合は、テキストエディタでスクリプトファイルを作成します。

  1. sudo nano /usr/local/bin/prepare-mysql.sh

中に、次の内容を貼り付けます。

/usr/local/bin/prepare-mysql.sh
#!/bin/bash

export LC_ALL=C

shopt -s nullglob
incremental_dirs=( ./incremental-*/ )
full_dirs=( ./full-*/ )
shopt -u nullglob

backup_owner="backup"
log_file="prepare-progress.log"
full_backup_dir="${full_dirs[0]}"

# 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.  Try checking the \"${log_file}\" file for more information."' 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 a single full backup directory are available
    if (( ${#full_dirs[@]} != 1 )); then
        error "Exactly one full backup directory is required."
    fi
}

do_backup () {
    # Apply the logs to each of the backups
    printf "Initial prep of full backup %s\n" "${full_backup_dir}"
    xtrabackup --prepare --apply-log-only --target-dir="${full_backup_dir}"
    
    for increment in "${incremental_dirs[@]}"; do
        printf "Applying incremental backup %s to %s\n" "${increment}" "${full_backup_dir}"
        xtrabackup --prepare --apply-log-only --incremental-dir="${increment}" --target-dir="${full_backup_dir}"
    done
    
    printf "Applying final logs to full backup %s\n" "${full_backup_dir}"
    xtrabackup --prepare --target-dir="${full_backup_dir}"
}

sanity_check && do_backup > "${log_file}" 2>&1

# Check the number of reported completions.  Each time a backup is processed,
# an informational "completed OK" and a real version is printed.  At the end of
# the process, a final full apply is performed, generating another 2 messages.
ok_count="$(grep -c 'completed OK' "${log_file}")"

if (( ${ok_count} == ${#full_dirs[@]} + ${#incremental_dirs[@]} + 1 )); then
    cat << EOF
Backup looks to be fully prepared.  Please check the "prepare-progress.log" file
to verify before continuing.

If everything looks correct, you can apply the restored files.

First, stop MySQL and move or remove the contents of the MySQL data directory:
    
        sudo systemctl stop mysql
        sudo mv /var/lib/mysql/ /tmp/
    
Then, recreate the data directory and  copy the backup files:
    
        sudo mkdir /var/lib/mysql
        sudo xtrabackup --copy-back --target-dir=${PWD}/$(basename "${full_backup_dir}")
    
Afterward the files are copied, adjust the permissions and restart the service:
    
        sudo chown -R mysql:mysql /var/lib/mysql
        sudo find /var/lib/mysql -type d -exec chmod 750 {} \\;
        sudo systemctl start mysql
EOF
else
    error "It looks like something went wrong.  Check the \"${log_file}\" file for more information."
fi

スクリプトは、現在のディレクトリでfull-またはincremental-で始まるディレクトリを探します。 MySQLログを使用して、コミットされたトランザクションを完全バックアップに適用します。 その後、増分バックアップを完全バックアップに適用して、データを最新の情報で更新し、コミットされたトランザクションを再度適用します。

すべてのバックアップが結合されると、コミットされていないトランザクションがロールバックされます。 この時点で、full-バックアップは、MySQLのデータディレクトリに移動できる一貫したデータセットを表します。

データ損失の可能性を最小限に抑えるために、スクリプトはファイルをデータディレクトリにコピーする前に停止します。 このようにして、ユーザーはこのプロセス中に作成されたバックアップの内容とログファイルを手動で確認し、MySQLデータディレクトリの現在の内容をどのように処理するかを決定できます。 コマンドを終了すると、ファイルを完全に復元するために必要なコマンドが表示されます。

終了したら、ファイルを保存して閉じます。 以前にそうしなかった場合は、次のように入力して、ファイルを実行可能としてマークします。

  1. sudo chmod +x /usr/local/bin/prepare-mysql.sh

このスクリプトは、バックアップファイルをMySQLのデータディレクトリに移動する前に実行する最後のスクリプトです。

MySQLバックアップおよび復元スクリプトのテスト

バックアップと復元のスクリプトがサーバー上にあるので、それらをテストする必要があります。

フルバックアップを実行する

backupユーザーでbackup-mysql.shスクリプトを呼び出すことから始めます。

  1. sudo -u backup backup-mysql.sh
Output
Backup successful! Backup created at /backups/mysql/Thu/full-04-20-2017_14-55-17.xbstream

すべてが計画どおりに進んだ場合、スクリプトは正しく実行され、成功を示し、新しいバックアップファイルの場所を出力します。 上記の出力が示すように、その日のバックアップを格納するために、日次ディレクトリ(この場合は「Thu」)が作成されています。 バックアップファイル自体はfull-で始まり、これが完全バックアップであることを示します。

毎日のバックアップディレクトリに移動して、内容を確認しましょう。

  1. cd /backups/mysql/"$(date +%a)"
  2. ls
Output
backup-progress.log full-04-20-2017_14-55-17.xbstream xtrabackup_checkpoints xtrabackup_info

ここでは、実際のバックアップファイル(この場合はfull-04-20-2017_14-55-17.xbstream)、バックアップイベントのログ(backup-progress.log)、バックアップに関するメタデータを含むxtrabackup_checkpointsファイルが表示されます。コンテンツ、および追加のメタデータを含むxtrabackup_infoファイル。

backup-progress.logを調整すると、バックアップが正常に完了したことを確認できます。

  1. tail backup-progress.log
Output
170420 14:55:19 All tables unlocked 170420 14:55:19 [00] Compressing, encrypting and streaming ib_buffer_pool to <STDOUT> 170420 14:55:19 [00] ...done 170420 14:55:19 Backup created in directory '/backups/mysql/Thu/' 170420 14:55:19 [00] Compressing, encrypting and streaming backup-my.cnf 170420 14:55:19 [00] ...done 170420 14:55:19 [00] Compressing, encrypting and streaming xtrabackup_info 170420 14:55:19 [00] ...done xtrabackup: Transaction log of lsn (2549956) to (2549965) was copied. 170420 14:55:19 completed OK!

xtrabackup_checkpointsファイルを見ると、バックアップに関する情報を見ることができます。 このファイルは、管理者に役立つ情報を提供しますが、主に後続のバックアップジョブで使用されるため、管理者はどのデータが既に処理されているかを知ることができます。

これは、各アーカイブに含まれているファイルのコピーです。 このコピーは最新の情報を表すためにバックアップごとに上書きされますが、各オリジナルはバックアップアーカイブ内で引き続き利用できます。

  1. cat xtrabackup_checkpoints
Output
backup_type = full-backuped from_lsn = 0 to_lsn = 2549956 last_lsn = 2549965 compact = 0 recover_binlog_info = 0

上記の例は、完全バックアップが作成され、バックアップがログシーケンス番号(LSN)0からログシーケンス番号2549956までをカバーしていることを示しています。 last_lsn番号は、バックアッププロセス中に一部の操作が発生したことを示します。

増分バックアップを実行する

これで完全バックアップができたので、追加の増分バックアップを取ることができます。 増分バックアップは、最後のバックアップが実行されてから行われた変更を記録します。 最初の増分バックアップは完全バックアップに基づいており、後続の増分バックアップは前の増分バックアップに基づいています。

どのバックアップが適用されたかを確認できるように、別のバックアップを作成する前にデータベースにデータを追加する必要があります。

playgroundデータベースのequipmentテーブルに、10個の黄色のスイングを表す別のレコードを挿入します。 このプロセス中に、MySQL管理者パスワードの入力を求められます。

  1. mysql -u root -p -e 'INSERT INTO playground.equipment (type, quant, color) VALUES ("swing", 10, "yellow");'

最新のバックアップよりも多くの最新データがあるので、増分バックアップを作成して変更をキャプチャできます。 backup-mysql.shスクリプトは、同じ日の完全バックアップが存在する場合、増分バックアップを取ります。

  1. sudo -u backup backup-mysql.sh
Output
Backup successful! Backup created at /backups/mysql/Thu/incremental-04-20-2017_17-15-03.xbstream

毎日のバックアップディレクトリをもう一度確認して、増分バックアップアーカイブを見つけます。

  1. cd /backups/mysql/"$(date +%a)"
  2. ls
Output
backup-progress.log incremental-04-20-2017_17-15-03.xbstream xtrabackup_info full-04-20-2017_14-55-17.xbstream xtrabackup_checkpoints

xtrabackup_checkpointsファイルの内容は、最新の増分バックアップを参照するようになりました。

  1. cat xtrabackup_checkpoints
Output
backup_type = incremental from_lsn = 2549956 to_lsn = 2550159 last_lsn = 2550168 compact = 0 recover_binlog_info = 0

バックアップの種類は「インクリメンタル」と表示され、完全バックアップのようにLSN 0から開始するのではなく、最後のバックアップが終了したLSNから開始します。

バックアップを抽出する

次に、バックアップファイルを抽出してバックアップディレクトリを作成しましょう。 スペースとセキュリティを考慮して、これは通常、データを復元する準備ができている場合にのみ実行する必要があります。

.xbstreamバックアップファイルをextract-mysql.shスクリプトに渡すことで、バックアップを抽出できます。 繰り返しますが、これはbackupユーザーが実行する必要があります。

  1. sudo -u backup extract-mysql.sh *.xbstream
Output
Extraction complete! Backup directories have been extracted to the "restore" directory.

上記の出力は、プロセスが正常に完了したことを示しています。 デイリーバックアップディレクトリの内容を再度確認すると、extract-progress.logファイルとrestoreディレクトリが作成されています。

抽出ログを調整すると、最新のバックアップが正常に抽出されたことを確認できます。 その他のバックアップ成功メッセージは、ファイルの前半に表示されます。

  1. tail extract-progress.log
Output
170420 17:23:32 [01] decrypting and decompressing ./performance_schema/socket_instances.frm.qp.xbcrypt 170420 17:23:32 [01] decrypting and decompressing ./performance_schema/events_waits_summary_by_user_by_event_name.frm.qp.xbcrypt 170420 17:23:32 [01] decrypting and decompressing ./performance_schema/status_by_user.frm.qp.xbcrypt 170420 17:23:32 [01] decrypting and decompressing ./performance_schema/replication_group_members.frm.qp.xbcrypt 170420 17:23:32 [01] decrypting and decompressing ./xtrabackup_logfile.qp.xbcrypt 170420 17:23:33 completed OK! Finished work on incremental-04-20-2017_17-15-03.xbstream

restoreディレクトリに移動すると、抽出したバックアップファイルに対応するディレクトリが利用できるようになります。

  1. cd restore
  2. ls -F
Output
full-04-20-2017_14-55-17/ incremental-04-20-2017_17-15-03/

バックアップディレクトリには生のバックアップファイルが含まれていますが、MySQLが使用できる状態にはまだなっていません。 これを修正するには、ファイルを準備する必要があります。

最終バックアップを準備する

次に、バックアップファイルを準備します。 これを行うには、full-およびincremental-バックアップを含むrestoreディレクトリにいる必要があります。 スクリプトは、incremental-ディレクトリからの変更をfull-バックアップディレクトリに適用します。 その後、ログを適用して、MySQLが使用できる一貫性のあるデータセットを作成します。

何らかの理由で変更の一部を復元したくない場合は、restoreディレクトリからそれらの増分バックアップディレクトリを削除する最後のチャンスです(増分バックアップファイルは引き続き親ディレクトリで利用できます) 。 現在のディレクトリ内に残っているincremental-ディレクトリは、full-バックアップディレクトリに適用されます。

準備ができたら、prepare-mysql.shスクリプトを呼び出します。 ここでも、個々のバックアップディレクトリが配置されているrestoreディレクトリにいることを確認してください。

  1. sudo -u backup prepare-mysql.sh
Output
Backup looks to be fully prepared. Please check the "prepare-progress.log" file to verify before continuing. If everything looks correct, you can apply the restored files. First, stop MySQL and move or remove the contents of the MySQL data directory: sudo systemctl stop mysql sudo mv /var/lib/mysql/ /tmp/ Then, recreate the data directory and copy the backup files: sudo mkdir /var/lib/mysql sudo xtrabackup --copy-back --target-dir=/backups/mysql/Thu/restore/full-04-20-2017_14-55-17 Afterward the files are copied, adjust the permissions and restart the service: sudo chown -R mysql:mysql /var/lib/mysql sudo find /var/lib/mysql -type d -exec chmod 750 {} \; sudo systemctl start mysql

上記の出力は、スクリプトがバックアップが完全に準備されていると見なし、full-バックアップが完全に整合性のあるデータセットを表していることを示しています。 出力に示されているように、prepare-progress.logファイルをチェックして、プロセス中にエラーが報告されていないことを確認する必要があります。

スクリプトは、ファイルをMySQLのデータディレクトリに実際にコピーする前に停止するため、すべてが正しく表示されていることを確認できます。

バックアップデータをMySQLデータディレクトリに復元する

ログを確認した後、すべてが正常であることに満足している場合は、prepare-mysql.shの出力に概説されている手順に従うことができます。

まず、実行中のMySQLプロセスを停止します。

  1. sudo systemctl stop mysql

バックアップデータはMySQLデータディレクトリの現在の内容と競合する可能性があるため、/var/lib/mysqlディレクトリを削除または移動する必要があります。 ファイルシステムに空き容量がある場合は、問題が発生した場合に備えて、現在のコンテンツを/tmpディレクトリまたは他の場所に移動するのが最善の方法です。

  1. sudo mv /var/lib/mysql/ /tmp

空の/var/lib/mysqlディレクトリを再作成します。 すぐに権限を修正する必要があるので、まだ心配する必要はありません。

  1. sudo mkdir /var/lib/mysql

これで、xtrabackupユーティリティを使用して完全バックアップをMySQLデータディレクトリにコピーできます。 以下のコマンドで、準備した完全バックアップへのパスを置き換えます。

  1. sudo xtrabackup --copy-back --target-dir=/backups/mysql/Thu/restore/full-04-20-2017_14-55-17

コピーされているファイルの実行ログは、プロセス全体で表示されます。 ファイルを配置したら、MySQLユーザーとグループが復元された構造を所有してアクセスできるように、所有権とアクセス許可を再度修正する必要があります。

  1. sudo chown -R mysql:mysql /var/lib/mysql
  2. sudo find /var/lib/mysql -type d -exec chmod 750 {} \;

復元されたファイルは、MySQLデータディレクトリにあります。

MySQLを再度起動して、プロセスを完了します。

  1. sudo systemctl start mysql

playground.equipmentテーブルの内容を表示して、データが復元されているかどうかを確認してください。 ここでも、続行するためにMySQLrootパスワードの入力を求められます。

  1. mysql -u root -p -e 'SELECT * FROM playground.equipment;'
Output
+----+-------+-------+--------+ | id | type | quant | color | +----+-------+-------+--------+ | 1 | slide | 2 | blue | | 2 | swing | 10 | yellow | +----+-------+-------+--------+ 2 rows in set (0.02 sec)

データは正常に復元されました。

データを復元した後、戻ってrestoreディレクトリを削除することが重要です。 将来の増分バックアップは、準備ができたら完全バックアップに適用できないため、削除する必要があります。 さらに、セキュリティ上の理由から、バックアップディレクトリをディスク上で暗号化せずに放置しないでください。

  1. cd ~
  2. sudo rm -rf /backups/mysql/"$(date +%a)"/restore

次にバックアップディレクトリのクリーンコピーが必要になったときに、バックアップファイルからそれらを再度抽出できます。

バックアップを1時間ごとに実行するためのcronジョブの作成

バックアップと復元のプロセスがスムーズに機能していることを確認したので、cronジョブを設定して定期的なバックアップを自動的に作成する必要があります。

/etc/cron.hourlyディレクトリ内に小さなスクリプトを作成して、バックアップスクリプトを自動的に実行し、結果をログに記録します。 cronプロセスは、これを1時間ごとに自動的に実行します。

  1. sudo nano /etc/cron.hourly/backup-mysql

内部では、systemd-catユーティリティを使用してバックアップスクリプトを呼び出し、出力がジャーナルで利用できるようにします。 ログを簡単にフィルタリングできるように、backup-mysql識別子でマークを付けます。

/etc/cron.hourly/backup-mysql
#!/bin/bash
sudo -u backup systemd-cat --identifier=backup-mysql /usr/local/bin/backup-mysql.sh

終了したら、ファイルを保存して閉じます。 次のように入力して、スクリプトを実行可能にします。

  1. sudo chmod +x /etc/cron.hourly/backup-mysql

バックアップスクリプトは1時間ごとに実行されます。 スクリプト自体が、3日前より古いバックアップのクリーンアップを処理します。

cronスクリプトを手動で実行すると、次のようにテストできます。

  1. sudo /etc/cron.hourly/backup-mysql

完了したら、次のように入力して、ジャーナルのログメッセージを確認します。

  1. sudo journalctl -t backup-mysql
Output
-- Logs begin at Wed 2017-04-19 18:59:23 UTC, end at Thu 2017-04-20 18:54:49 UTC. -- Apr 20 18:35:07 myserver backup-mysql[2302]: Backup successful! Apr 20 18:35:07 myserver backup-mysql[2302]: Backup created at /backups/mysql/Thu/incremental-04-20-2017_18-35-05.xbstream

数時間後にもう一度チェックして、追加のバックアップが作成されていることを確認してください。

結論

このガイドでは、MySQLデータのライブスナップショットを定期的に作成するのに役立つPerconaXtrabackupツールをインストールしました。 MySQLとシステムのバックアップユーザーを構成し、バックアップファイルを保護するための暗号化キーを設定してから、バックアップと復元の手順の一部を自動化するスクリプトを設定しました。

バックアップスクリプトは、毎日の開始時に完全バックアップを生成し、その後は1時間ごとに増分バックアップを生成し、いつでも3日間のバックアップを保持します。 暗号化されたファイルと暗号化キーを他のバックアップテクノロジーと組み合わせて使用して、データをオフサイトに転送して保管することができます。 抽出および準備スクリプトを使用すると、その日のバックアップを、システムの復元に使用できる一貫したデータセットにまとめることができます。