technique:adminsys:backup:db:sauvegarde_unique

Système de sauvegarde unique

La mise en place d’un système de sauvegarde pouvant gérer plusieurs bases de données (PostgreSQL et MySQL) s’est fait dans un objectif de simplification de l’infrastructure et de scalabilité.

Nous disposions avant le changement de 2 images Docker (mysql-backup et postgres-backup) sur le registry de Picasoft. Chacune des images Docker contenait un script (nommé run.sh), qui effectuait la sauvegarde d’une seule base de donnée, dont les paramètres étaient précisés au lancement du conteneur par variables d’environnement. Pour chaque base de donnée, il était donc nécessaire de lancer une image Docker différente.

A terme, une telle solution semble peu adaptée, ainsi une image de sauvegarde unique semble être une meilleure solution pour plusieurs raisons :

  • Meilleur dimensionnement : Bien que la consommation de ressource (CPU, RAM) ne soit pas aujourd’hui un problème, il ne semble pas nécessaire de fournir un environnement d’exécution isolé (comprendre un conteneur Docker) pour chaque script de sauvegarde.
  • Gestion centralisée : Une image unique à chaque machine permettrait de simplifier l’accès au système de sauvegarde (un seul point d’accès), ainsi que sa maintenance (ex: mise à jour des scripts de sauvegarde).

L’objectif de création d’un système de sauvegarde unique revient donc à créer une image Docker, utilisant les scripts issus des images mysql-backup et postgres-backup, afin de sauvegarder plusieurs bases de donnée.

Les scripts de l’image Docker de sauvegarde unique sont :

mongodb_run.sh : Effectue une sauvegarde d’une seule base de donnée Mongo. Ses paramètres sont les suivants :

  • MONGO_SERVICE_NAME :
  • MONGO_HOST : nom de l’hôte (résolvable en tant qu’IP)
  • MONGO_PORT : port du service MONGO (défaut: 27017)
  • MONGO_PASS : mot de passe de l’utilisateur
  • MONGO_DB : nom de la base de donnée MONGO sauvegardée
  • INIT_BACKUP :
    1. 1 = sauvegarde au démarrage du script
    2. 0 = pas de sauvegarde au démarrage

    - CRON_TIME : règle cron définissant la fréquence d’appel du script

  • BACKUP_FOLDER : dossier de sauvegarde sur la machine hôte
  • mysql\_run.sh : Effectue une sauvegarde d’une seule base de donnée MySQL. Ses paramètres sont les suivants :
  • MYSQL_SERVICE_NAME :
  • MYSQL_HOST : nom de l’hôte (résolvable en tant qu’IP)
  • MYSQL_PORT : port du service MySQL (défaut: 3306)
  • MYSQL_USER : nom de l’utilisateur MySQL accédant à la base de donnée pour la sauvegarde
  • MYSQL_PASS : mot de passe de l’utilisateur
  • MYSQL_DB : nom de la base de donnée MySQL sauvegardée
  • INIT_BACKUP :
    1. 1 = sauvegarde au démarrage du script
    2. 0 = pas de sauvegarde au démarrage

    - CRON_TIME : règle cron définissant la fréquence d’appel du script

  • EXTRA_OPTS : options de sauvegarde supplémentaire (ex: --all-databases,--single-transaction)
  • BACKUP_FOLDER : dossier de sauvegarde sur la machine hôte

postgres_run.sh : Effectue une sauvegarde d’une seule base de donnée PostgreSQL. Ses paramètres sont les suivants :

  • POSTGRES_SERVICE_NAME :
  • POSTGRES_HOST : nom de l’hôte (résolvable en tant qu’IP)
  • POSTGRES_PORT : port du service PostgresSQL (défaut: 5432)
  • POSTGRES_USER : nom de l’utilisateur PostgresSQL accédant à la base de donnée pour la sauvegarde
  • POSTGRES_PASS : mot de passe de l’utilisateur
  • POSTGRES_DB : nom de la base de donnée PostgresSQL sauvegardée
  • INIT_BACKUP :
    1. 1 = sauvegarde au démarrage du script
    2. 0 = pas de sauvegarde au démarrage

    - CRON_TIME : règle cron définissant la fréquence d’appel du script

  • EXTRA_OPTS : options de sauvegarde supplémentaire
  • BACKUP_FOLDER : dossier de sauvegarde sur la machine hôte

Trois questions se posent alors :

  1. Comment transmettre plusieurs paramètres (décrivant plusieurs bases de données) à l’image Docker ?
    • Jusqu’alors, les paramètres étaient transmis par variable d’environnement, mais pour une unique base de donnée
    • Les variable Bash de type tableau ne sont pas gérés par Docker.
  2. Comment utiliser les scripts issus des images Docker existantes ?
    • Peut-ont les utiliser tel quel, et faire en sorte de les importer des images existantes (du moins de leur source)
    • Ou est-il nécessaire d’adapter les scripts ?
  3. Comment monter un nombre variable de volumes, représentant les différents dossiers de sauvegardes sur la machine hôte ?

A ces différentes questions les réponses suivantes ont été apportées :

  1. La solution choisie est de transmettre les paramètres à l’image Docker sous forme de liste, dans des variables d’environnement :
    • Le paramètre MYSQL_HOST utile au script mysql_run.sh sera obtenu grâce à la variable d’environnementMYSQL_HOST_LIST.
    • Pour mieux comprendre voici l’algorithme (implémenté dans le script run.sh de l’image Docker de sauvegarde unique):
          Pour i allant de 1 au nombre de sauvegarde MySQL
            Définir chaque variable utile au script mysql\_run.sh en prenant le i-ème élément de la liste correspondante
            Lancer mysql\_run.sh avec ces variables
          Pour i allant de 1 au nombre de sauvegarde PostgresSQL
            Définir chaque variable utile au script postgres\_run.sh en prenant le i-ème élément de la liste correspondante
            Lancer postgres\_run.sh avec ces variables
  2. Il est nécéssaire de modifier les scripts issus des images mysql-backup et postgres-backup pour plusieurs raisons :
    • Chaque script sauvegardait directement dans le dossier /backup/ , d’où l’ajout de la variable BACKUP_FOLDER, permettant de préciser un sous-dossier (l’arborescence est donc /backup/$BACKUP_FOLDER/ au sein du conteneur).
    • Chaque script crée un autre script pour effectuer la sauvegarde, qui est appelé au démarrage (si INIT_BACKUP=1) et par cron. Chaque script créé doit donc avoir un nom unique, d’où l’ajout de la variable MYSQL_SERVICE_NAME et POSTGRES_SERVICE_NAME pour préciser ce nom

L’image Docker de sauvegarde unifiée dispose donc de 4 scripts situés dans le dossier /scripts :

  • mongodb_run.sh, mysql_run.sh et postgres_run.sh
  • run.sh qui réalise l’algorithme ci-dessus.

Pour être plus précis sur le fonctionnement du script run.sh, celui-ci transforme les listes passés en tant que variables d’environnement en tableaux bash, plus facile à manipuler. Cela est rendu possible grâce à la fonction read à laquelle on précise le séparateur via la variable IFS (dans notre cas IFS=,)

Pour chaque service défini dans la variable MYSQL_HOST du fichier docker-compose.yml, les scripts mysql_run.sh et postgresrun.sh sont lancés. Ces deux scripts s’occupent de créer les fichiers <service>-backup.sh et <nomservice>-restore.sh, qui sont ensuite placés à la racine du conteneur. Le fichier <service>-backup.sh est rajouté à la crontab du serveur avec une fréquence d’exécution égale à celle définie dans la variable MYSQL_CRON_TIME_LIST du fichier docker-compose.yml. Le lancement du script effectue un dump de la base de données dans l’archive /backup/<nomservice>/<DATE>.tar.gz .

Voici un exemple d’entrée docker-compose permettant de lancer l’image :

  pica-backup:
    image: pica-backup
    container_name: pica-backup
    links:
      - <nom de l'image de base de donnée liée n°1>
      - <nom de l'image de base de donnée liée n°2>
      - ...
      - <nom de l'image de base de donnée liée n°k>
    environment:
      - "MYSQL_SERVICE_NAME_LIST=<nom service n°1>,<nom service n°2>,...,<nom service n°k>"
      - "MYSQL_HOST_LIST=<nom hôte service n°1>,<nom hôte service n°2>,...,<nom hôte service n°k>"
      - "MYSQL_PORT_LIST=<numéro port service n°1>,<numéro port service n°2>,...,<numéro port service n°k>"
      - "MYSQL_USER_LIST=<utilisateur service n°1>,<utilisateur service n°2>,...,<utilisateur service n°k>"
      - "MYSQL_PASS_LIST=<mot de passe service n°1>,<mot de passe service n°2>,...,<mot de passe service n°k>"
      - "MYSQL_DB_LIST=<base de donnée service n°1>,<base de donnée service n°2>,...,<base de donnée service n°k>"
      - "MYSQL_INIT_BACKUP_LIST=<sauvegarde au démarrage service n°1>,<sauvegarde au démarrage service n°2>,...,<sauvegarde au démarrage service n°k>"
      - "MYSQL_CRON_TIME_LIST=<temps cron service n°1>,<temps cron service n°2>,...,<temps cron service n°k>"
      - "MYSQL_EXTRA_OPTS_LIST=<args suppl. service n°1>,<args suppl. service n°2>,...,<args suppl. service n°k>"
      - "MYSQL_BACKUP_FOLDER_LIST=etherpad-test1,etherpad-test2"
      - "POSTGRES_SERVICE_NAME_LIST="
      - "POSTGRES_HOST_LIST="
      - "POSTGRES_PORT_LIST="
      - "POSTGRES_USER_LIST="
      - "POSTGRES_PASS_LIST="
      - "POSTGRES_DB_LIST="
      - "POSTGRES_INIT_BACKUP_LIST="
      - "POSTGRES_CRON_TIME_LIST="
      - "POSTGRES_BACKUP_FOLDER_LIST="
 
    volumes:
      - "/DATA/BACKUP/unified_backup_test/:/backup/"

NB: La structure des variables d’environnement pour PostgreSQL est la même que celles pour MySQL.

Voic un exemple d’entrée docker-compose dans le cas ou nous disposons de 4 services :

  • Deux utilisent une base de donnée MySQL : databaseA et databaseB.
    • Les sauvegardes sont respectivement stockées dans les dossiers /DATA/BACKUP/{dbA-save,dbB-save}
    • Les noms d’hôtes sont databaseA et databaseB, ils sont par conséquent résolvables en l’IP de leur conteneur Docker.
    • Nous sauvegardons toute la base de données (--all-databases) en une seule transaction (--single-transaction) toutes les heures (0 * * * *).
    • Une sauvegarde et effectuée au démarrage pour les deux services (MYSQL_INIT_BACKUP_LIST à 1,1)
  • Deux autres utilisant une base de donnée PostgreSQL : databaseC et databaseD
    • Les sauvegardes sont respectivement stockées dans les dossiers /DATA/BACKUP/{dbC-save,dbD-save}
    • Les noms d’hôtes sont databaseC et databaseD, ils sont par conséquent résolvables en l’IP de leur conteneur Docker.
    • Nous sauvegardons les bases de données dbC et dbD avec les utilisateurs postgreSQL user_dbC et user_dbD toutes les heures (0 * * * *)
    • Une sauvegarde et effectuée au démarrage pour les deux services (POSTGRES_INIT_BACKUP_LIST à 1,1)
pica-backup:
    image: registry.picasoft.net:5000/pica-backup:1.1
    container_name: pica-backup
    links:
      - databaseA
      - databaseB
      - databaseC
      - databaseD
    environment:
      - "MYSQL_SERVICE_NAME_LIST=serviceA,serviceB"
      - "MYSQL_HOST_LIST=databaseA,databaseB"
      - "MYSQL_PORT_LIST=3306,3306"
      - "MYSQL_USER_LIST=root,root"
      - "MYSQL_PASS_LIST=...,..."
      - "MYSQL_DB_LIST=--all-databases,--all-databases"
      - "MYSQL_INIT_BACKUP_LIST=1,1"
      - "MYSQL_CRON_TIME_LIST=0 * * * *,0 * * * *"
      - "MYSQL_EXTRA_OPTS_LIST=--single-transaction,--single-transaction"
      - "MYSQL_BACKUP_FOLDER_LIST=dbA-save,dbB-save"
      - "POSTGRES_SERVICE_NAME_LIST=serviceC,serviceD"
      - "POSTGRES_HOST_LIST=databaseC,databaseD"
      - "POSTGRES_PORT_LIST=5432,5432"
      - "POSTGRES_USER_LIST=user_dbC,user_dbD"
      - "POSTGRES_PASS_LIST=...,..."
      - "POSTGRES_DB_LIST=dbC,dbD"
      - "POSTGRES_INIT_BACKUP_LIST=1,1"
      - "POSTGRES_CRON_TIME_LIST=0 * * * *,0 * * * *"
      - "POSTGRES_BACKUP_FOLDER_LIST=dbC-save,dbD-save"
    volumes:
      - /DATA/BACKUP/:/backup/
      - /etc/localtime:/etc/localtime:ro

Afin de restaurer une sauvegarde SQL dans l’une des bases de donnée, des scripts on été générés à l’intérieur du conteneur de sauvegarde unique.

Dans le cas ou ce conteneur s’appelle pica-backup, il suffit le lancer les commandes suivantes pour effectuer une restauration :

# Lancer un shell interactif dans le conteneur
root@machine-hote:~$ docker exec -it pica-backup bash
# Éxecuter le script de restauration dans le conteneur
root@imagedocker:/$ /<service>-restore.sh /backup/<dossier de sauvegarde du service>/<fichier de sauvegarde à restaurer>.tar.gz

Afin de s’assurer du bon fonctionnement du conteneur de sauvegarde, plusieurs vérifications peuvent être faites :

  • Regarder le nom des sauvegardes dans le dossier de sauvegarde de la machine hôte (/DATA/BACKUP/<nom du service>/) avec la commande ls -lrh. Ci-dessous, nous pouvons voir que les sauvegardes se font toutes les 6 heures :
    -rw-r--r-- 1 root root 172M janv.  5 12:00 2018.01.05.120001.sql
    -rw-r--r-- 1 root root 172M janv.  5 06:00 2018.01.05.060001.sql
    -rw-r--r-- 1 root root 172M janv.  5 00:00 2018.01.05.000001.sql
    -rw-r--r-- 1 root root 172M janv.  4 18:00 2018.01.04.180001.sql
    -rw-r--r-- 1 root root 172M janv.  4 12:00 2018.01.04.120001.sql
    -rw-r--r-- 1 root root 172M janv.  4 06:00 2018.01.04.060001.sql
    -rw-r--r-- 1 root root 172M janv.  4 00:00 2018.01.04.000001.sql
    -rw-r--r-- 1 root root 172M janv.  3 18:00 2018.01.03.180001.sql
    -rw-r--r-- 1 root root 172M janv.  3 12:00 2018.01.03.120001.sql
    -rw-r--r-- 1 root root 172M janv.  3 06:00 2018.01.03.060001.sql
    -rw-r--r-- 1 root root 172M janv.  3 00:00 2018.01.03.000001.sql
    -rw-r--r-- 1 root root 172M janv.  2 18:00 2018.01.02.180001.sql
    -rw-r--r-- 1 root root 171M janv.  2 12:00 2018.01.02.120001.sql
    -rw-r--r-- 1 root root 171M janv.  2 06:00 2018.01.02.060001.sql
    -rw-r--r-- 1 root root 171M janv.  2 00:00 2018.01.02.000001.sql
    -rw-r--r-- 1 root root 171M janv.  1 18:00 2018.01.01.180001.sql
    -rw-r--r-- 1 root root 171M janv.  1 12:00 2018.01.01.120001.sql
    -rw-r--r-- 1 root root 171M janv.  1 06:00 2018.01.01.060001.sql
  • L’augmentation de la taille des sauvegardes au cours du temps peut-être aussi un bon indicateur
  • Au redémarrage du conteneur, les services pour lesquels l’option MYSQL_INIT_BACKUP/ POSTGRES_INIT_BACKUP est à 1 produisent une sauvegarde, ce qui indique que les paramètres fournis permettent l’accès à la base de données. Pour compléter cette information, la commande docker-compose logs pica-backup permettra de vérifier le bon déroulement des sauvegardes :
    pica-backup             | => mattermost: Backup started: 2018.01.05.110001.sql
    pica-backup             | mattermost:  Backup succeeded
    pica-backup             | => mattermost: Backup started: 2018.01.05.120001.sql
    pica-backup             | mattermost:  Backup succeeded
    pica-backup             | => etherpad: Backup started: 2018.01.05.120001.sql
    pica-backup             | etherpad: Backup succeeded
    pica-backup             | => mattermost: Backup started: 2018.01.05.130001.sql
    pica-backup             | mattermost:  Backup succeeded
    pica-backup             | => mattermost: Backup started: 2018.01.05.140001.sql
    pica-backup             | mattermost:  Backup succeeded
  • technique/adminsys/backup/db/sauvegarde_unique.1604871547.txt.gz
  • de qduchemi