Comment configurer les sauvegardes MySQL avec Percona XtraBackup sur Ubuntu 16.04

introduction

Les bases de données stockent souvent certaines des informations les plus précieuses de votre infrastructure. Pour cette raison, il est important de disposer de sauvegardes fiables pour se prémunir contre la perte de données en cas d’accident ou de défaillance matérielle.

Les outils de sauvegarde Percona XtraBackup fournissent une méthode permettant d’effectuer des sauvegardes «à chaud» des données MySQL pendant l’exécution du système. Pour ce faire, ils copient les fichiers de données au niveau du système de fichiers, puis effectuent une récupération sur incident pour assurer la cohérence du jeu de données.

Dans ce guide, nous allons créer un système pour automatiser les sauvegardes de données MySQL sur un serveur Ubuntu 16.04. Nous allons utiliser les outils cron et Percona dans un groupe de scripts pour créer des sauvegardes régulières et sécurisées que nous pouvons utiliser pour la récupération en cas de problème.

Conditions préalables

Pour compléter ce guide, vous aurez besoin d’un serveur Ubuntu 16.04 avec un utilisateur non root + sudo + configuré pour les tâches administratives. Vous pouvez suivre notre «Configuration initiale du serveur avec Ubuntu 16.04» pour configurer un utilisateur avec ces privilèges sur votre serveur.

Une fois que vous avez un utilisateur + sudo + disponible, vous devrez installer MySQL. Chacun de ces guides peut être utilisé, selon le package que vous souhaitez utiliser. Le premier guide est approprié si vous souhaitez vous en tenir aux référentiels officiels Ubuntu, tandis que le second guide est plus adapté si vous avez besoin de fonctionnalités plus récentes:

Une fois MySQL installé, connectez-vous à votre serveur en tant qu’utilisateur + sudo + pour continuer.

Installer les outils Percona Xtrabackup

La première chose à faire est d’installer les utilitaires de sauvegarde Percona. Le projet gère ses propres référentiels que nous pouvons ajouter à notre serveur MySQL pour accéder aux packages.

Pour commencer, allez à la page Percona pour Ubuntu afin de trouver les derniers packages + .deb + pour l’installation du référentiel. Puisque nous sommes sur Ubuntu 16.04, qui porte le nom de code “Xenial Xerus”, nous devrions choisir le paquet “xenial”. Cliquez avec le bouton droit sur le lien correspondant et copiez l’adresse.

Une fois que vous avez copié le lien, accédez au répertoire + / tmp + puis téléchargez le package de configuration du référentiel avec + curl +:

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

Ensuite, utilisez + dpkg + pour installer le paquet téléchargé, qui configurera le référentiel Percona + apt + sur le système:

sudo dpkg -i percona*

Avec le nouveau référentiel configuré, nous mettrons à jour l’index de paquetage local pour extraire des informations sur les paquetages nouvellement disponibles. Nous installerons ensuite les outils XtraBackup et l’utilitaire de compression + qpress + à partir du référentiel:

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

Parmi les autres utilitaires fournis, les commandes + xtrabackup +, + xbstream + et + qpress + seront désormais disponibles. Nos scripts utiliseront chacun de ceux-ci pour effectuer des sauvegardes et restaurer des données.

Configuration d’un utilisateur de sauvegarde MySQL et ajout de données de test

Pour commencer, démarrez une session interactive MySQL avec l’utilisateur racine MySQL:

mysql -u root -p

Le mot de passe administratif que vous avez sélectionné lors de l’installation de MySQL vous sera demandé. Une fois que vous avez entré le mot de passe, vous serez plongé dans une session MySQL.

Créer un utilisateur MySQL avec les privilèges appropriés

La première chose à faire est de créer un nouvel utilisateur MySQL configuré pour gérer les tâches de sauvegarde. Nous ne donnerons à cet utilisateur que les privilèges nécessaires pour copier les données en toute sécurité pendant le fonctionnement du système.

Pour être explicite sur le but du compte, nous appellerons le nouvel utilisateur + backup +. Nous allons placer les informations d’identité de l’utilisateur dans un fichier sécurisé. N’hésitez donc pas à choisir un mot de passe complexe:

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

Ensuite, nous devons accorder au nouvel utilisateur + backup + les autorisations nécessaires pour effectuer toutes les actions de sauvegarde sur le système de base de données. Accordez les privilèges requis et appliquez-les à la session en cours en tapant:

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

Notre utilisateur de sauvegarde MySQL est configuré et dispose des accès nécessaires.

Créer des données de test pour les sauvegardes

Ensuite, nous allons créer des données de test. Exécutez les commandes suivantes pour créer une base de données + playground + avec une table + equipment +. Nous allons commencer par insérer un seul enregistrement représentant une diapositive bleue:

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");

Plus loin dans ce guide, nous utiliserons et modifierons ces données pour tester notre capacité à créer des sauvegardes complètes et incrémentielles.

Avant de terminer notre session MySQL, nous allons vérifier la valeur de la variable + datadir +. Nous aurons besoin de connaître cette valeur pour nous assurer que notre utilisateur + backup + au niveau système a accès aux fichiers de données MySQL.

Affichez la valeur de la variable + datadir + en tapant:

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

Prenez note de l’emplacement que vous trouvez.

C’est tout ce que nous devons faire dans MySQL pour le moment. Quittez le shell en tapant:

exit

Ensuite, nous pouvons examiner une configuration au niveau du système.

Configuration d’un utilisateur de sauvegarde des systèmes et attribution d’autorisations

Maintenant que nous avons un utilisateur MySQL pour effectuer des sauvegardes, nous allons nous assurer qu’un utilisateur Linux correspondant existe avec des privilèges limités similaires.

Sur Ubuntu 16.04, un utilisateur + backup + et le groupe + backup + correspondant est déjà disponible. Confirmez cela en vérifiant les fichiers + / etc / passwd et` + / etc / group` à l’aide de la commande suivante:

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

La première ligne du fichier + / etc / passwd + décrit l’utilisateur + backup +, tandis que la deuxième ligne du fichier + / etc / group + définit le groupe + backup +.

Le répertoire + / var / lib / mysql où les données MySQL sont conservées est la propriété de l’utilisateur et du groupe` + mysql`. Nous pouvons ajouter l’utilisateur + backup au groupe` + mysql` pour permettre l’accès en toute sécurité aux fichiers et aux répertoires de la base de données. Nous devrions également ajouter un utilisateur + sudo au groupe` + backup` afin de pouvoir accéder aux fichiers que nous allons sauvegarder.

Tapez les commandes suivantes pour ajouter l’utilisateur + backup + au groupe + mysql et votre utilisateur` + sudo` au groupe + backup +:

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

Si nous vérifions à nouveau les fichiers + / etc / group +, vous verrez que votre utilisateur actuel est ajouté au groupe + backup + et que l’utilisateur + backup + est ajouté au groupe + mysql +:

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

Le nouveau groupe n’est pas disponible automatiquement dans notre session en cours. Pour réévaluer les groupes disponibles pour notre utilisateur + sudo +, déconnectez-vous et reconnectez-vous, ou tapez:

exec su - ${USER}

Le mot de passe de votre utilisateur + sudo + vous sera demandé pour continuer. Confirmez que votre session actuelle a maintenant accès au groupe + backup + en vérifiant à nouveau les groupes de nos utilisateurs:

id -nG
Output sudo backup

Notre utilisateur + sudo + sera désormais en mesure de tirer parti de son appartenance au groupe + backup +.

Ensuite, nous devons rendre le répertoire + / var / lib / mysql et ses sous-répertoires accessibles au groupe` + mysql` en ajoutant des autorisations d’exécution de groupe. Sinon, l’utilisateur + backup + ne pourra pas entrer dans ces répertoires, même s’il est membre du groupe + mysql +.

Pour donner au groupe + mysql + accès aux répertoires de données MySQL, tapez:

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

Notre utilisateur + backup + a maintenant l’accès nécessaire au répertoire MySQL.

Création des actifs de sauvegarde

Maintenant que MySQL et les utilisateurs de sauvegarde système sont disponibles, nous pouvons commencer à configurer les fichiers de configuration, les clés de cryptage et les autres ressources nécessaires pour créer et sécuriser efficacement nos sauvegardes.

Créer un fichier de configuration MySQL avec les paramètres de sauvegarde

Commencez par créer un fichier de configuration MySQL minimal utilisé par le script de sauvegarde. Cela contiendra les informations d’identification MySQL pour l’utilisateur MySQL.

Ouvrez un fichier dans + / etc / mysql / backup.cnf + dans votre éditeur de texte:

sudo nano /etc/mysql/backup.cnf

À l’intérieur, démarrez une section + [client] + et définissez l’utilisateur de sauvegarde MySQL et le mot de passe que vous avez définis dans MySQL:

/etc/mysql/backup.cnf

[client]
user=backup
password=

Enregistrez et fermez le fichier lorsque vous avez terminé.

Attribuez la propriété du fichier à l’utilisateur + backup + puis limitez les autorisations afin qu’aucun autre utilisateur ne puisse accéder au fichier:

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

L’utilisateur de sauvegarde pourra accéder à ce fichier pour obtenir les informations d’identification appropriées, mais les autres utilisateurs seront limités.

Créer un répertoire racine de sauvegarde

Ensuite, créez un répertoire pour le contenu de la sauvegarde. Nous allons utiliser + / backups / mysql comme répertoire de base pour nos sauvegardes:

sudo mkdir -p /backups/mysql

Ensuite, assignez la propriété du répertoire + / backups / mysql à la propriété de l’utilisateur` + backup + et au groupe + mysql`:

sudo chown backup:mysql /backups/mysql

L’utilisateur + backup + devrait maintenant être en mesure d’écrire des données de sauvegarde à cet emplacement.

Créer une clé de cryptage pour sécuriser les fichiers de sauvegarde

Étant donné que les sauvegardes contiennent toutes les données du système de base de données lui-même, il est important de les sécuriser correctement. L’utilitaire + xtrabackup + permet de chiffrer chaque fichier au fur et à mesure de sa sauvegarde et de son archivage. Nous avons juste besoin de lui fournir une clé de cryptage.

Nous pouvons créer une clé de chiffrement dans le répertoire racine de la sauvegarde avec la commande + openssl +:

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

Il est également très important de restreindre l’accès à ce fichier. Encore une fois, attribuez la propriété à l’utilisateur + backup + et refusez l’accès à tous les autres utilisateurs:

sudo chown backup:backup /backups/mysql/encryption_key
sudo chmod 600 /backups/mysql/encryption_key

Cette clé sera utilisée pendant le processus de sauvegarde et chaque fois que vous devez restaurer à partir d’une sauvegarde.

Création des scripts de sauvegarde et de restauration

Nous avons maintenant tout ce dont nous avons besoin pour effectuer des sauvegardes sécurisées de l’instance MySQL en cours d’exécution.

Afin de rendre nos étapes de sauvegarde et de restauration répétables, nous allons scripter l’ensemble du processus. Nous allons créer les scripts suivants:

  • + backup-mysql.sh +: Ce script sauvegarde les bases de données MySQL, chiffrant et compressant les fichiers au cours du processus. Il crée des sauvegardes complètes et incrémentielles et organise automatiquement le contenu par jour. Par défaut, le script conserve 3 jours de sauvegardes.

  • + extract-mysql.sh +: Ce script décompresse et déchiffre les fichiers de sauvegarde pour créer des répertoires avec le contenu sauvegardé.

  • + prepare-mysql.sh +: Ce script «prépare» les répertoires de sauvegarde en traitant les fichiers et en appliquant les journaux. Toutes les sauvegardes incrémentielles sont appliquées à la sauvegarde complète. Une fois le script de préparation terminé, les fichiers sont prêts à être déplacés dans le répertoire de données.

Vous pouvez afficher les scripts dans la repository pour ce tutoriel sur GitHub à tout moment. Si vous ne voulez pas copier et coller le contenu ci-dessous, vous pouvez le télécharger directement à partir de GitHub en tapant:

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

Assurez-vous d’inspecter les scripts après le téléchargement pour vous assurer qu’ils ont été récupérés avec succès et que vous approuvez les actions qu’ils vont effectuer. Si vous êtes satisfait, marquez les scripts comme exécutables, puis déplacez-les dans le répertoire + / usr / local / bin + en tapant:

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

Ensuite, nous allons configurer chacun de ces scripts et en discuter plus en détail.

Créez le script backup-mysql.sh

Si vous n’avez pas téléchargé le script + backup-mysql.sh + depuis GitHub, créez un nouveau fichier dans le répertoire + / usr / local / bin + appelé + backup-mysql.sh +:

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

Copiez et collez le contenu du script dans le fichier:

/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

Le script a les fonctionnalités suivantes:

  • Crée une sauvegarde complète chiffrée et compressée la première fois qu’elle est exécutée chaque jour.

  • Génère des sauvegardes incrémentielles compressées et chiffrées sur la base de la sauvegarde complète quotidienne, à nouveau appelée le même jour.

  • Maintient les sauvegardes organisées par jour. Par défaut, trois jours de sauvegarde sont conservés. Cela peut être changé en ajustant le paramètre + days_of_backups + dans le script.

Lorsque le script est exécuté, un répertoire quotidien est créé dans lequel les fichiers horodatés représentant des sauvegardes individuelles seront écrits. Le premier fichier horodaté sera une sauvegarde complète, précédée de + full- +. Les sauvegardes suivantes de la journée seront des sauvegardes incrémentielles, indiquées par un préfixe + incremental- +, représentant les modifications apportées depuis la dernière sauvegarde complète ou incrémentielle.

Les sauvegardes généreront un fichier appelé "+ backup-progress.log " dans le répertoire quotidien avec le résultat de la dernière opération de sauvegarde. Un fichier nommé ` xtrabackup_checkpoints ` contenant les métadonnées de sauvegarde les plus récentes y sera également créé. Ce fichier est nécessaire à la création de futures sauvegardes incrémentielles. Il est donc important de ne pas le supprimer. Un fichier nommé ` xtrabackup_info +`, qui contient des métadonnées supplémentaires, est également produit, mais le script ne fait pas référence à ce fichier.

Lorsque vous avez terminé, enregistrez et fermez le fichier.

Ensuite, si vous ne l’avez pas déjà fait, rendez le script exécutable en tapant:

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

Nous avons maintenant une seule commande disponible qui lancera les sauvegardes MySQL.

Créez le script extract-mysql.sh

Ensuite, nous allons créer le script + extract-mysql.sh +. Ceci sera utilisé pour extraire la structure de répertoires de données MySQL à partir de fichiers de sauvegarde individuels.

Si vous n’avez pas téléchargé le script depuis le référentiel, créez et ouvrez un fichier appelé + extract-mysql.sh + dans le répertoire + / usr / local / bin +:

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

À l’intérieur, collez le script suivant:

/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

Contrairement au script + backup-mysql.sh +, conçu pour être automatisé, ce script est conçu pour être utilisé intentionnellement lorsque vous prévoyez de restaurer à partir d’une sauvegarde. Pour cette raison, le script s’attend à ce que vous transmettiez les fichiers + .xbstream + que vous souhaitez extraire.

Le script crée un répertoire + restore + dans le répertoire actuel, puis crée des répertoires individuels dans chacune des sauvegardes transmises en tant qu’arguments. Il traitera les fichiers + .xbstream + fournis en extrayant la structure de répertoires de l’archive, en déchiffrant les fichiers individuels qu’il contient, puis en décompressant les fichiers déchiffrés.

Une fois ce processus terminé, le répertoire + restore + devrait contenir les répertoires de chacune des sauvegardes fournies. Cela vous permet d’inspecter les répertoires, d’examiner le contenu des sauvegardes et de choisir les sauvegardes que vous souhaitez préparer et restaurer.

Enregistrez et fermez le fichier lorsque vous avez terminé. Ensuite, assurez-vous que le script est exécutable en tapant:

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

Ce script nous permettra d’étendre les fichiers de sauvegarde individuels dans la structure de répertoires nécessaire à la restauration.

Créez le script prepare-mysql.sh

Enfin, téléchargez ou créez le script + prepare-mysql.sh + dans le répertoire + / usr / local / bin +. Ce script appliquera les journaux à chaque sauvegarde pour créer un instantané cohérent de la base de données. Il appliquera toutes les sauvegardes incrémentielles à la sauvegarde complète pour intégrer les modifications ultérieures.

Créez le fichier de script dans votre éditeur de texte si vous ne l’avez pas téléchargé plus tôt:

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

À l’intérieur, collez le contenu suivant:

/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

Le script recherche dans le répertoire en cours les répertoires commençant par + full- + ou + incremental- +. Il utilise les journaux MySQL pour appliquer les transactions validées à la sauvegarde complète. Ensuite, il applique les sauvegardes incrémentielles à la sauvegarde complète pour mettre à jour les données avec les informations les plus récentes, en appliquant à nouveau les transactions validées.

Une fois que toutes les sauvegardes ont été combinées, les transactions non validées sont annulées. À ce stade, la sauvegarde + complète– + représentera un ensemble cohérent de données pouvant être déplacées dans le répertoire de données de MySQL.

Afin de minimiser les risques de perte de données, le script s’arrête avant de copier les fichiers dans le répertoire de données. De cette manière, l’utilisateur peut vérifier manuellement le contenu de la sauvegarde et le fichier journal créé au cours de ce processus, et décider quoi utiliser avec le contenu actuel du répertoire de données MySQL. Les commandes nécessaires à la restauration complète des fichiers sont affichées à la fermeture de la commande.

Enregistrez et fermez le fichier lorsque vous avez terminé. Si vous ne l’avez pas fait plus tôt, marquez le fichier comme exécutable en tapant:

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

Ce script est le dernier script que nous avons exécuté avant de déplacer les fichiers de sauvegarde dans le répertoire de données de MySQL.

Test des scripts de sauvegarde et de restauration MySQL

Maintenant que les scripts de sauvegarde et de restauration sont sur le serveur, nous devrions les tester.

Effectuer une sauvegarde complète

Commencez par appeler le script + backup-mysql.sh + avec l’utilisateur + backup +:

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

Si tout se passe comme prévu, le script s’exécutera correctement, indiquera le succès et affichera l’emplacement du nouveau fichier de sauvegarde. Comme indiqué ci-dessus, un répertoire quotidien ("Jeu" dans ce cas) a été créé pour héberger les sauvegardes du jour. Le fichier de sauvegarde lui-même commence par + full- + pour indiquer qu’il s’agit d’une sauvegarde complète.

Passons au répertoire de sauvegarde quotidienne et visualisons le contenu:

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

Ici, nous voyons le fichier de sauvegarde réel (+ full-04-20-2017_14-55-17.xbstream + dans ce cas), le journal de l’événement de sauvegarde (+ backup-progress.log +), le + Le fichier xtrabackup_checkpoints + `, qui inclut les métadonnées relatives au contenu sauvegardé, ainsi que le fichier + xtrabackup_info + `, qui contient des métadonnées supplémentaires.

Si nous suivons le + backup-progress.log +, nous pourrons confirmer que la sauvegarde est terminée.

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

Si nous regardons le fichier + xtrabackup_checkpoints +, nous pouvons voir des informations sur la sauvegarde. Bien que ce fichier fournisse des informations utiles aux administrateurs, il est principalement utilisé par les travaux de sauvegarde ultérieurs pour leur permettre de savoir quelles données ont déjà été traitées.

Ceci est une copie d’un fichier inclus dans chaque archive. Même si cette copie est écrasée avec chaque sauvegarde pour représenter les informations les plus récentes, chaque original sera toujours disponible dans l’archive de sauvegarde.

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

L’exemple ci-dessus nous indique qu’une sauvegarde complète a été effectuée et qu’elle couvre le numéro de séquence du journal (LSN) 0 jusqu’au numéro de séquence du journal 2549956. Le nombre + last_lsn + indique que certaines opérations ont eu lieu pendant le processus de sauvegarde.

Effectuer une sauvegarde incrémentielle

Maintenant que nous avons une sauvegarde complète, nous pouvons effectuer des sauvegardes incrémentielles supplémentaires. Les sauvegardes incrémentielles enregistrent les modifications apportées depuis la dernière sauvegarde. La première sauvegarde incrémentielle est basée sur une sauvegarde complète et les sauvegardes incrémentielles suivantes sont basées sur la sauvegarde incrémentielle précédente.

Nous devrions ajouter des données à notre base de données avant de procéder à une nouvelle sauvegarde afin de pouvoir déterminer quelles sauvegardes ont été appliquées.

Insérez un autre enregistrement dans le tableau + equipment + de notre base de données + playground + représentant 10 balançoires jaunes. Le mot de passe administratif MySQL vous sera demandé au cours de cette procédure:

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

Maintenant qu’il y a plus de données actuelles que notre sauvegarde la plus récente, nous pouvons effectuer une sauvegarde incrémentielle pour capturer les modifications. Le script + backup-mysql.sh + effectuera une sauvegarde incrémentielle s’il existe une sauvegarde complète pour le même jour:

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

Consultez à nouveau le répertoire de sauvegarde quotidienne pour trouver l’archive de sauvegarde incrémentielle:

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

Le contenu du fichier + xtrabackup_checkpoints + fait maintenant référence à la sauvegarde incrémentielle la plus récente:

cat xtrabackup_checkpoints
Outputbackup_type = incremental
from_lsn = 2549956
to_lsn = 2550159
last_lsn = 2550168
compact = 0
recover_binlog_info = 0

Le type de sauvegarde est répertorié comme «incrémentiel» et au lieu de commencer à partir du LSN 0 comme notre sauvegarde complète, il commence au LSN où s’est terminée notre dernière sauvegarde.

Extraire les sauvegardes

Ensuite, extrayons les fichiers de sauvegarde pour créer des répertoires de sauvegarde. Pour des raisons d’espace et de sécurité, cette opération ne devrait normalement être effectuée que lorsque vous êtes prêt à restaurer les données.

Nous pouvons extraire les sauvegardes en transmettant les fichiers de sauvegarde + .xbstream + au script + extract-mysql.sh +. Encore une fois, cela doit être exécuté par l’utilisateur + backup +:

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

La sortie ci-dessus indique que le processus s’est terminé avec succès. Si nous vérifions à nouveau le contenu du répertoire de sauvegarde quotidien, un fichier + extract-progress.log + et un répertoire + restore + ont été créés.

Si nous suivons le journal d’extraction, nous pouvons confirmer que la dernière sauvegarde a été extraite avec succès. Les autres messages de réussite de la sauvegarde sont affichés plus tôt dans le fichier.

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

Si nous passons dans le répertoire + restore +, les répertoires correspondant aux fichiers de sauvegarde extraits sont maintenant disponibles:

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

Les répertoires de sauvegarde contiennent les fichiers de sauvegarde bruts, mais ils ne sont pas encore dans un état que MySQL peut utiliser. Pour résoudre ce problème, nous devons préparer les fichiers.

Préparer la sauvegarde finale

Ensuite, nous préparerons les fichiers de sauvegarde. Pour ce faire, vous devez être dans le répertoire + restore + qui contient les sauvegardes + full- + et toutes les sauvegardes + incremental- +. Le script appliquera les modifications de tous les répertoires + incremental- + au répertoire de sauvegarde + full- +. Ensuite, il utilisera les journaux pour créer un ensemble de données cohérent que MySQL peut utiliser.

Si, pour une raison quelconque, vous ne souhaitez pas restaurer certaines des modifications, c’est votre dernière chance de supprimer ces répertoires de sauvegarde incrémentielle du répertoire + restore + (les fichiers de sauvegarde incrémentielle seront toujours disponibles dans le répertoire parent). Tout répertoire + incremental- + restant dans le répertoire actuel sera appliqué au répertoire de sauvegarde + complet- +.

Lorsque vous êtes prêt, appelez le script + prepare-mysql.sh +. Encore une fois, assurez-vous que vous vous trouvez dans le répertoire + restore + où se trouvent vos répertoires de sauvegarde individuels:

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

La sortie ci-dessus indique que le script pense que la sauvegarde est entièrement préparée et que la sauvegarde + full- + représente désormais un ensemble de données entièrement cohérent. Au moment de la sortie, vérifiez le fichier + prepare-progress.log + pour vous assurer qu’aucune erreur n’a été signalée au cours du processus.

Le script s’arrête avant de copier les fichiers dans le répertoire de données de MySQL afin que vous puissiez vérifier que tout semble correct.

Restaurer les données de sauvegarde dans le répertoire de données MySQL

Si vous êtes convaincu que tout est en ordre après avoir examiné les journaux, vous pouvez suivre les instructions données dans la sortie + prepare-mysql.sh +.

Tout d’abord, arrêtez le processus MySQL en cours d’exécution:

sudo systemctl stop mysql

Comme les données de sauvegarde peuvent entrer en conflit avec le contenu actuel du répertoire de données MySQL, nous devrions supprimer ou déplacer le répertoire + / var / lib / mysql +. Si vous avez de la place sur votre système de fichiers, la meilleure option est de déplacer le contenu actuel dans le répertoire + / tmp + ou ailleurs, au cas où quelque chose se passerait mal:

sudo mv /var/lib/mysql/ /tmp

Créez un répertoire + / var / lib / mysql vide. Nous devrons corriger les autorisations dans un moment, nous n’avons donc pas à nous en préoccuper pour le moment:

sudo mkdir /var/lib/mysql

Maintenant, nous pouvons copier la sauvegarde complète dans le répertoire de données MySQL en utilisant l’utilitaire + xtrabackup +. Remplacez le chemin de votre sauvegarde complète préparée dans la commande ci-dessous:

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

Un journal en cours des fichiers en cours de copie sera affiché tout au long du processus. Une fois les fichiers en place, nous devons redéfinir la propriété et les autorisations afin que l’utilisateur et le groupe MySQL possèdent et puissent accéder à la structure restaurée:

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

Nos fichiers restaurés sont maintenant dans le répertoire de données MySQL.

Relancez MySQL pour terminer le processus:

sudo systemctl start mysql

Vérifiez si les données ont été restaurées en consultant le contenu de la table + playground.equipment. Encore une fois, vous serez invité à entrer le mot de passe MySQL + root pour continuer:

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)

Nos données ont été restaurées avec succès.

Après la restauration de vos données, il est important de revenir en arrière et de supprimer le répertoire + restore +. Les sauvegardes incrémentielles futures ne peuvent pas être appliquées à la sauvegarde complète une fois celle-ci préparée. Nous devons donc la supprimer. De plus, les répertoires de sauvegarde ne doivent pas rester non chiffrés sur le disque pour des raisons de sécurité:

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

La prochaine fois que nous aurons besoin d’une copie vierge des répertoires de sauvegarde, nous pourrons les extraire à nouveau des fichiers de sauvegarde.

Création d’un travail périodique pour exécuter les sauvegardes toutes les heures

Maintenant que nous avons vérifié que le processus de sauvegarde et de restauration fonctionne correctement, nous devons configurer un travail + cron + pour effectuer automatiquement des sauvegardes régulières.

Nous allons créer un petit script dans le répertoire + / etc / cron.hourly + pour exécuter automatiquement notre script de sauvegarde et consigner les résultats. Le processus + cron + s’exécutera automatiquement toutes les heures:

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

À l’intérieur, nous appellerons le script de sauvegarde avec l’utilitaire + systemd-cat + afin que le résultat soit disponible dans le journal. Nous allons les marquer avec un identifiant + backup-mysql afin de pouvoir facilement filtrer les journaux:

/etc/cron.hourly/backup-mysql

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

Enregistrez et fermez le fichier lorsque vous avez terminé. Rendre le script exécutable en tapant:

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

Le script de sauvegarde sera maintenant exécuté toutes les heures. Le script lui-même se chargera de nettoyer les sauvegardes datant de plus de trois jours.

Nous pouvons tester le script + cron + en l’exécutant manuellement:

sudo /etc/cron.hourly/backup-mysql

Une fois l’opération terminée, recherchez dans le journal les messages du journal en tapant:

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

Revenez quelques heures plus tard pour vous assurer que des sauvegardes supplémentaires sont effectuées.

Conclusion

Dans ce guide, nous avons installé les outils Percona Xtrabackup pour permettre de créer régulièrement des instantanés en direct de nos données MySQL. Nous avons configuré un utilisateur MySQL et un utilisateur du système de sauvegarde, une clé de chiffrement pour sécuriser nos fichiers de sauvegarde, puis des scripts pour automatiser des parties des procédures de sauvegarde et de restauration.

Le script de sauvegarde génère une sauvegarde complète au début de chaque jour et des sauvegardes incrémentielles toutes les heures après, en conservant trois jours de sauvegardes à tout moment. Les fichiers cryptés et la clé de cryptage peuvent être utilisés avec d’autres technologies de sauvegarde pour transférer les données hors site pour des raisons de sécurité. Les scripts d’extraction et de préparation nous permettent d’assembler les sauvegardes du jour dans un ensemble cohérent de données pouvant être utilisées pour restaurer le système.