Как настроить резервное копирование MySQL с помощью Percona XtraBackup в Ubuntu 16.04

Вступление

Базы данных часто хранят наиболее ценную информацию в вашей инфраструктуре. По этой причине важно иметь надежные резервные копии для защиты от потери данных в случае аварии или сбоя оборудования.

Инструменты резервного копирования Percona XtraBackup предоставляют метод «горячего» резервного копирования данных MySQL во время работы системы. Они делают это, копируя файлы данных на уровне файловой системы, а затем выполняя аварийное восстановление для достижения согласованности в наборе данных.

В этом руководстве мы создадим систему для автоматизации резервного копирования данных MySQL на сервере Ubuntu 16.04. Мы будем использовать инструменты cron и Percona в группе сценариев для создания регулярных, безопасных резервных копий, которые мы можем использовать для восстановления в случае проблем.

Предпосылки

Для завершения этого руководства вам понадобится сервер Ubuntu 16.04 с пользователем без полномочий root + sudo +, настроенным для административных задач. Вы можете следовать нашему InInialial Server Setup с Ubuntu 16.04 ” руководству, чтобы настроить пользователя с этими привилегиями на вашем сервере.

Если у вас есть пользователь + sudo +, вам нужно будет установить MySQL. Любое из этих руководств может быть использовано в зависимости от того, какой пакет вы хотели бы использовать. Первое руководство подходит, если вы хотите придерживаться официальных репозиториев Ubuntu, тогда как второе руководство лучше подходит, если вам нужны более современные функции:

После установки MySQL войдите на свой сервер как пользователь + sudo +, чтобы продолжить.

Установка инструментов Percona Xtrabackup

Первое, что нам нужно сделать, это установить настоящие утилиты резервного копирования Percona. Проект поддерживает свои собственные репозитории, которые мы можем добавить на наш сервер MySQL, чтобы получить доступ к пакетам.

Чтобы начать, перейдите на Percona версию для Ubuntu, чтобы найти последние пакеты + .deb + для установки репозитория. Поскольку мы находимся на Ubuntu 16.04, которая называется «Xenial Xerus», мы должны выбрать пакет «xenial». Щелкните правой кнопкой мыши соответствующую ссылку и скопируйте адрес.

После того, как вы скопировали ссылку, перейдите в каталог + / tmp + и затем загрузите пакет конфигурации репозитория с помощью + curl +:

cd /tmp
curl -LO https://repo.percona.com/apt/.deb

Затем используйте + dpkg + для установки загруженного пакета, который настроит репозиторий Percona + apt + в системе:

sudo dpkg -i percona*

С настроенным новым хранилищем мы обновим локальный индекс пакета, чтобы получить информацию о новых доступных пакетах. Затем мы установим инструменты XtraBackup и утилиту сжатия + qpress + из репозитория:

sudo apt-get update
sudo apt-get install percona-xtrabackup-24 qpress

Среди других связанных утилит теперь будут доступны команды + xtrabackup +, + xbstream + и + qpress +. Наши сценарии будут использовать каждый из них для резервного копирования и восстановления данных.

Настройка MySQL Backup User и добавление тестовых данных

Для начала запустите интерактивный сеанс MySQL с пользователем root MySQL:

mysql -u root -p

Вам будет предложено ввести пароль администратора, который вы выбрали при установке MySQL. После того, как вы ввели пароль, вы попадете в сеанс MySQL.

Создайте пользователя MySQL с соответствующими привилегиями

Первое, что нам нужно сделать, - это создать нового пользователя MySQL, настроенного на обработку задач резервного копирования. Мы дадим этому пользователю только те привилегии, которые ему необходимы для безопасного копирования данных во время работы системы.

Чтобы уточнить назначение учетной записи, мы назовем нового пользователя + backup +. Мы разместим учетные данные пользователя в защищенном файле, поэтому не стесняйтесь выбирать сложный пароль:

CREATE USER 'backup'@'localhost' IDENTIFIED BY '';

Затем нам нужно предоставить новому пользователю + backup + разрешения, необходимые для выполнения всех действий резервного копирования в системе базы данных. Предоставьте необходимые привилегии и примените их к текущей сессии, введя:

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

Наш резервный пользователь MySQL настроен и имеет доступ, который ему требуется.

Создать тестовые данные для резервных копий

Далее мы создадим некоторые тестовые данные. Выполните следующие команды, чтобы создать базу данных «+ площадка » с таблицей « оборудование +». Мы начнем с вставки одной записи, представляющей синий слайд:

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

Далее в этом руководстве мы будем использовать и изменять эти данные для проверки нашей способности создавать полные и инкрементные резервные копии.

Перед тем как завершить сеанс MySQL, мы проверим значение переменной + datadir +. Нам нужно знать это значение, чтобы наш пользователь + backup + системного уровня имел доступ к файлам данных MySQL.

Отобразите значение переменной + datadir +, набрав:

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

Запишите местоположение, которое вы найдете.

Это все, что нам нужно сделать в MySQL на данный момент. Выйдите в оболочку, набрав:

exit

Далее мы можем взглянуть на некоторую конфигурацию системного уровня.

Конфигурирование пользователя Backup System и назначение разрешений

Теперь, когда у нас есть пользователь MySQL для выполнения резервного копирования, мы гарантируем, что соответствующий пользователь Linux существует с аналогичными ограниченными привилегиями.

В Ubuntu 16.04 пользователь + backup + и соответствующая группа + backup + уже доступны. Подтвердите это, проверив файлы + / etc / passwd + и + / etc / group + следующей командой:

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 + описывает пользователя + backup +, а вторая строка файла + / etc / group + определяет группу + backup +.

Каталог + / var / lib / mysql, в котором хранятся данные MySQL, принадлежит пользователю и группе` + mysql`. Мы можем добавить пользователя + backup в группу` + mysql`, чтобы безопасно разрешить доступ к файлам и каталогам базы данных. Мы также должны добавить пользователя + sudo в группу` + backup`, чтобы мы могли получить доступ к файлам, для которых мы создадим резервную копию.

Введите следующие команды, чтобы добавить пользователя + backup + в группу + mysql и своего пользователя` + sudo` в группу + backup +:

sudo usermod -aG mysql backup
sudo usermod -aG backup ${USER}

Если мы снова проверим файлы + / etc / group +, вы увидите, что ваш текущий пользователь добавлен в группу + backup +, а пользователь + backup + добавлен в группу + mysql +:

grep backup /etc/group
Outputbackup:x:34:
mysql:x:116:backup

Новая группа не доступна в нашей текущей сессии автоматически. Чтобы повторно оценить группы, доступные нашему пользователю + sudo +, либо выйдите и снова войдите в систему, либо введите:

exec su - ${USER}

Вам будет предложено ввести пароль вашего пользователя + sudo + для продолжения. Убедитесь, что ваш текущий сеанс теперь имеет доступ к группе «+ backup +», еще раз проверив группы наших пользователей:

id -nG
Output sudo backup

Наш пользователь + sudo + теперь сможет воспользоваться своим членством в группе + backup +.

Затем нам нужно сделать каталог + / var / lib / mysql и его подкаталоги доступными для группы` + mysql`, добавив разрешения на выполнение группы. В противном случае пользователь + backup + не сможет войти в эти каталоги, даже если он является членом группы + mysql +.

Чтобы предоставить группе + mysql + доступ к каталогам данных MySQL, введите:

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

Наш пользователь + backup + теперь имеет доступ к каталогу MySQL.

Создание резервных активов

Теперь, когда пользователи MySQL и пользователи системы резервного копирования доступны, мы можем приступить к настройке файлов конфигурации, ключей шифрования и других ресурсов, необходимых для успешного создания и защиты наших резервных копий.

Создайте файл конфигурации MySQL с параметрами резервного копирования

Начните с создания минимального файла конфигурации MySQL, который будет использовать скрипт резервного копирования. Это будет содержать учетные данные MySQL для пользователя MySQL.

Откройте файл в + / etc / mysql / backup.cnf + в текстовом редакторе:

sudo nano /etc/mysql/backup.cnf

Внутри откройте раздел + [client] + и задайте пользователя резервного копирования MySQL и пользователя пароля, который вы определили в MySQL:

/etc/mysql/backup.cnf

[client]
user=backup
password=

Сохраните и закройте файл, когда вы закончите.

Передайте права собственности на файл пользователю + backup +, а затем ограничьте разрешения, чтобы другие пользователи не могли получить доступ к файлу:

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

Пользователь резервного копирования сможет получить доступ к этому файлу для получения надлежащих учетных данных, но другие пользователи будут ограничены.

Создать резервную корневую директорию

Затем создайте каталог для содержимого резервной копии. Мы будем использовать + / backups / mysql в качестве основного каталога для наших резервных копий:

sudo mkdir -p /backups/mysql

Далее, назначьте владение каталогом + / backups / mysql пользователю` + backup + и владение группой группе + mysql`:

sudo chown backup:mysql /backups/mysql

Пользователь + backup + теперь сможет записывать данные резервной копии в это место.

Создайте ключ шифрования для защиты файлов резервных копий

Поскольку резервные копии содержат все данные из самой системы базы данных, важно обеспечить их надлежащую защиту. Утилита + xtrabackup + имеет возможность шифровать каждый файл во время его резервного копирования и архивирования. Нам просто нужно предоставить ключ шифрования.

Мы можем создать ключ шифрования в резервном корневом каталоге с помощью команды + 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

Этот ключ будет использоваться в процессе резервного копирования и в любое время, когда вам нужно восстановить из резервной копии.

Создание сценариев резервного копирования и восстановления

Теперь у нас есть все необходимое для безопасного резервного копирования запущенного экземпляра MySQL.

Чтобы повторить шаги резервного копирования и восстановления, мы запишем весь процесс. Мы создадим следующие скрипты:

  • + backup-mysql.sh +: этот скрипт выполняет резервное копирование баз данных MySQL, шифруя и сжимая файлы в процессе. Он создает полные и инкрементные резервные копии и автоматически организует контент по дням. По умолчанию скрипт сохраняет резервные копии на 3 дня.

  • + extract-mysql.sh +: этот сценарий распаковывает и дешифрует файлы резервных копий для создания каталогов с резервным копированием содержимого.

  • + prepare-mysql.sh +: этот сценарий «подготавливает» резервные каталоги путем обработки файлов и применения журналов. Любые инкрементные резервные копии применяются к полной резервной копии. После завершения сценария подготовки файлы готовы для перемещения обратно в каталог данных.

Вы можете в любое время просмотреть скрипты в repository для этого руководства на GitHub. Если вы не хотите копировать и вставлять содержимое ниже, вы можете загрузить его прямо с GitHub, набрав:

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

Обязательно проверьте скрипты после загрузки, чтобы убедиться, что они были успешно получены и что вы одобряете действия, которые они будут выполнять. Если вы удовлетворены, пометьте сценарии как исполняемые и затем переместите их в каталог + / usr / local / bin +, набрав:

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

Далее мы настроим каждый из этих сценариев и обсудим их более подробно.

Создайте скрипт backup-mysql.sh

Если вы не загрузили скрипт + backup-mysql.sh + из GitHub, создайте новый файл в каталоге + / usr / local / bin + с именем + backup-mysql.sh +:

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

Скрипт имеет следующие функциональные возможности:

  • Создает зашифрованную, сжатую полную резервную копию при первом запуске каждый день.

  • Создает зашифрованные, сжатые инкрементные резервные копии на основе ежедневного полного резервного копирования при повторном вызове в тот же день.

  • Поддерживает резервные копии, организованные днем. По умолчанию резервные копии хранятся три дня. Это можно изменить, изменив параметр + days_of_backups + внутри скрипта.

При запуске сценария создается ежедневный каталог, в который будут записываться файлы с метками времени, представляющие отдельные резервные копии. Первый файл с временной меткой будет полной резервной копией с префиксом + full- +. Последующие резервные копии в течение дня будут инкрементными резервными копиями, обозначенными префиксом «+ incremental- +», представляющими изменения с момента последнего полного или инкрементного резервного копирования.

Резервные копии будут генерировать файл с именем + backup-progress.log + в ежедневном каталоге с выводом самой последней операции резервного копирования. Там же будет создан файл с именем + xtrabackup_checkpoints +, содержащий самые последние резервные метаданные. Этот файл необходим для создания будущих инкрементных резервных копий, поэтому важно не удалять его. Файл с именем + xtrabackup_info +, который содержит дополнительные метаданные, также создается, но скрипт не ссылается на этот файл.

Когда вы закончите, сохраните и закройте файл.

Затем, если вы еще этого не сделали, сделайте скрипт исполняемым, набрав:

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

Теперь у нас есть единственная команда, которая будет инициировать резервное копирование MySQL.

Создайте скрипт extract-mysql.sh

Далее мы создадим скрипт + extract-mysql.sh +. Это будет использоваться для извлечения структуры каталогов данных MySQL из отдельных файлов резервных копий.

Если вы не загрузили скрипт из репозитория, создайте и откройте файл с именем + extract-mysql.sh + в каталоге + / usr / local / bin +:

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 "$@"

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 + должен содержать каталоги для каждой из предоставленных резервных копий. Это позволяет вам проверять каталоги, проверять содержимое резервных копий и решать, какие резервные копии вы хотите подготовить и восстановить.

Сохраните и закройте файл, когда вы закончите. После этого убедитесь, что скрипт выполняется, набрав:

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

Этот скрипт позволит нам расширить отдельные файлы резервных копий в структуру каталогов, необходимую для восстановления.

Создайте скрипт prepare-mysql.sh

Наконец, загрузите или создайте скрипт + prepare-mysql.sh + в каталоге + / usr / local / bin +. Этот сценарий будет применять журналы к каждой резервной копии для создания согласованного снимка базы данных. Он будет применять любые инкрементные резервные копии к полной резервной копии, чтобы включить последующие изменения.

Создайте файл сценария в текстовом редакторе, если вы не загружали его ранее:

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. Команды, необходимые для полного восстановления файлов, отображаются при выходе из команды.

Сохраните и закройте файл, когда вы закончите. Если вы не сделали этого ранее, отметьте файл как исполняемый, введя:

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

Этот сценарий является последним сценарием, который мы запускаем перед перемещением файлов резервных копий в каталог данных MySQL.

Тестирование сценариев резервного копирования и восстановления MySQL

Теперь, когда сценарии резервного копирования и восстановления находятся на сервере, мы должны их протестировать.

Выполните полное резервное копирование

Начните с вызова скрипта + backup-mysql.sh + с пользователем + backup +:

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

Если все прошло как запланировано, сценарий будет выполнен правильно, укажет успех и выведет местоположение нового файла резервной копии. Как показывает вышеприведенный вывод, ежедневный каталог (в данном случае «Чт») был создан для размещения резервных копий дня. Сам файл резервной копии начинается с + full- +, чтобы указать, что это полная резервная копия.

Давайте перейдем в каталог ежедневного резервного копирования и просмотрим содержимое:

cd /backups/mysql/"$(date +%a)"
ls
Outputbackup-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 +, мы можем подтвердить, что резервное копирование завершено успешно.

tail backup-progress.log
Output170420 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

Если мы посмотрим на файл + xtrabackup_checkpoints, мы можем просмотреть информацию о резервной копии. Хотя этот файл содержит некоторую информацию, полезную для администраторов, он в основном используется последующими заданиями резервного копирования, чтобы они знали, какие данные уже были обработаны.

Это копия файла, который включен в каждый архив. Даже если эта копия перезаписывается каждой резервной копией для представления самой последней информации, каждый оригинал будет по-прежнему доступен в архиве резервной копии.

cat xtrabackup_checkpoints
Outputbackup_type = full-backuped
from_lsn = 0
to_lsn = 2549956
last_lsn = 2549965
compact = 0
recover_binlog_info = 0

Приведенный выше пример говорит нам о том, что было выполнено полное резервное копирование и что резервное копирование охватывает порядковый номер журнала (LSN) от 0 до порядкового номера журнала 2549956. Число + last_lsn + указывает на то, что в процессе резервного копирования произошли некоторые операции.

Выполнить инкрементное резервное копирование

Теперь, когда у нас есть полная резервная копия, мы можем делать дополнительные инкрементные резервные копии. Инкрементные резервные копии записывают изменения, которые были сделаны со времени последнего резервного копирования. Первая инкрементная резервная копия основана на полной резервной копии, а последующая инкрементная резервная копия основана на предыдущей инкрементной резервной копии.

Мы должны добавить некоторые данные в нашу базу данных, прежде чем делать другую резервную копию, чтобы мы могли сказать, какие резервные копии были применены.

Вставьте еще одну запись в таблицу + equipment + нашей базы данных + площадка +, представляющую 10 желтых колебаний. Во время этого процесса вам будет предложено ввести пароль администратора MySQL:

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

Теперь, когда существует больше текущих данных, чем в нашей последней резервной копии, мы можем сделать инкрементную резервную копию, чтобы зафиксировать изменения. Сценарий + backup-mysql.sh + будет делать инкрементную резервную копию, если существует полная резервная копия на тот же день:

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

Проверьте каталог ежедневного резервного копирования снова, чтобы найти архив инкрементной резервной копии:

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

Содержимое файла + xtrabackup_checkpoints + теперь ссылается на самую последнюю инкрементную резервную копию:

cat xtrabackup_checkpoints
Outputbackup_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 +:

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

Приведенный выше вывод указывает, что процесс был успешно завершен. Если мы снова проверим содержимое каталога ежедневного резервного копирования, будет создан файл + extract-progress.log + и каталог + restore +.

Если мы следим за журналом извлечения, мы можем подтвердить, что последняя резервная копия была успешно извлечена. Другие сообщения об успешном резервном копировании отображаются ранее в файле.

tail extract-progress.log
Output170420 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 +, каталоги, соответствующие файлам резервной копии, которые мы извлекли, теперь доступны:

cd restore
ls -F
Outputfull-04-20-2017_14-55-17/  incremental-04-20-2017_17-15-03/

Каталоги резервных копий содержат необработанные файлы резервных копий, но они еще не находятся в состоянии, которое может использовать MySQL. Чтобы это исправить, нам нужно подготовить файлы.

Подготовьте финальную резервную копию

Далее мы подготовим файлы резервных копий. Для этого вы должны находиться в каталоге + restore +, который содержит + full- + и любые + incremental- + резервные копии. Сценарий применит изменения из любых каталогов + incremental- + в каталог резервных копий + full- +. После этого он будет применять журналы для создания согласованного набора данных, который может использовать MySQL.

Если по какой-либо причине вы не хотите восстанавливать некоторые изменения, теперь у вас есть последний шанс удалить эти каталоги инкрементной резервной копии из каталога + restore + (файлы инкрементной резервной копии по-прежнему будут доступны в родительском каталоге). Все остальные каталоги + incremental- + в текущем каталоге будут применены к каталогу резервного копирования + full- +.

Когда вы будете готовы, вызовите скрипт + prepare-mysql.sh +. Опять же, убедитесь, что вы находитесь в каталоге + restore +, где находятся ваши отдельные каталоги резервных копий:

sudo -u backup prepare-mysql.sh
OutputBackup 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:

sudo systemctl stop mysql

Поскольку данные резервного копирования могут конфликтовать с текущим содержимым каталога данных MySQL, мы должны удалить или переместить каталог + / var / lib / mysql +. Если у вас есть место в вашей файловой системе, лучше всего переместить текущее содержимое в каталог + / tmp + или в другое место на случай, если что-то пойдет не так:

sudo mv /var/lib/mysql/ /tmp

Создайте пустой каталог + / var / lib / mysql. Нам нужно будет исправить разрешения в данный момент, поэтому нам не нужно беспокоиться об этом:

sudo mkdir /var/lib/mysql

Теперь мы можем скопировать полную резервную копию в каталог данных MySQL, используя утилиту + xtrabackup +. Укажите путь к подготовленной полной резервной копии в приведенной ниже команде:

sudo xtrabackup --copy-back --target-dir=/backups/mysql//restore/full-

Текущий журнал копируемых файлов будет отображаться на протяжении всего процесса. Как только файлы будут созданы, нам нужно снова исправить владельца и права доступа, чтобы пользователь и группа MySQL владели и могли получить доступ к восстановленной структуре:

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

Наши восстановленные файлы теперь находятся в каталоге данных MySQL.

Запустите MySQL снова, чтобы завершить процесс:

sudo systemctl start mysql

Проверьте, были ли восстановлены данные, просмотрев содержимое таблицы + plays.equipment. Снова вам будет предложено ввести пароль MySQL + root для продолжения:

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 +. Будущие инкрементные резервные копии не могут быть применены к полной резервной копии после ее подготовки, поэтому мы должны удалить ее. Кроме того, резервные каталоги не следует оставлять незашифрованными на диске по соображениям безопасности:

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

В следующий раз, когда нам понадобятся чистые копии каталогов резервных копий, мы снова сможем извлечь их из файлов резервных копий.

Создание задания Cron для ежечасного запуска резервных копий

Теперь, когда мы убедились, что процессы резервного копирования и восстановления работают без сбоев, нам нужно настроить задачу `+ cron + 'для автоматического создания регулярных резервных копий.

Мы создадим небольшой скрипт в каталоге + / etc / cron.hourly +, чтобы автоматически запустить наш скрипт резервного копирования и записать результаты. Процесс + cron + будет запускаться автоматически каждый час:

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

Сохраните и закройте файл, когда вы закончите. Сделайте скрипт исполняемым, набрав:

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

Сценарий резервного копирования теперь будет запускаться ежечасно. Сам сценарий позаботится об очистке резервных копий старше трех дней назад.

Мы можем проверить скрипт + cron +, запустив его вручную:

sudo /etc/cron.hourly/backup-mysql

После завершения проверьте журнал на наличие сообщений журнала, набрав:

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

Перепроверьте через несколько часов, чтобы убедиться, что дополнительные резервные копии принимаются.

Заключение

В этом руководстве мы установили инструменты Percona Xtrabackup, которые помогают регулярно создавать моментальные снимки наших данных MySQL. Мы настроили MySQL и пользователя системы резервного копирования, настроили ключ шифрования для защиты наших файлов резервных копий, а затем настроили сценарии для автоматизации частей процедур резервного копирования и восстановления.

Сценарий резервного копирования создает полную резервную копию в начале каждого дня, а затем каждый час создает дополнительные резервные копии, сохраняя резервные копии в течение трех дней в любое время. Зашифрованные файлы и ключ шифрования могут использоваться в сочетании с другими технологиями резервного копирования для передачи данных за пределы сайта для безопасного хранения. Сценарии извлечения и подготовки позволяют нам собирать резервные копии за день в единый набор данных, которые можно использовать для восстановления системы.

Related