Licence CC BY

Les sauvegardes de Zeste de Savoir

ZFS ftw

J’ai dans mon appart un petit serveur tournant sous FreeBSD, me servant notamment de routeur et de NAS. J’ai 8 To de stockage dessus en RAID1, et me sert depuis peu de ce serveur comme espace de sauvegarde pour la production de Zeste de Savoir.

Les données de Zeste de Savoir se trouvent à deux endroits en production. D’une part dans le dossier /opt/zds/data, contenant les dépôts git des contenus, les médias uploadés, et autres PDFs générés. D’autre part dans une base de donnée MySQL.


  1. J’ai en réalité ~16 To de disques durs répartis sur 4 disques, qui sont en miroirs deux par deux. 

Sauvegarde de la base via Xtrabackup

Auparavant, pour sauvegarder la base, mysqldump était utilisé pour générer un dump complet de la base sous forme de fichier sql. Ces dumps pèsent un peu moins de 500 Mo non-compressés, et 100 Mo gzippés. Aussi, chaque dump contient l’ensemble de la base de données, alors qu’en général entre deux sauvegardes, seul une partie de la base a changé.

Aussi, mysqldump a tendance à vouloir verrouiller les tables pendant le dump, et ainsi ralentir le site.

Mon objectif était d’effectuer des backups plus régulièrement, pour éviter des retours dans le passés trop importants en cas d’incident majeur. Les dumps étaient effectués une fois tous les jours, et je ne pouvais pas me permettre d’en faire un toutes les heures.

Xtrabackup est un outil développé par Percona permettant de faire des sauvegardes incrémentales et sans LOCK d’une base MySQL. Il se base sur le fait qu’InnoDB1 maintient sur le disque un historique des transactions lui permettant de récupérer la base en cas d’incident majeur, tant que le disque est intacte. Il utilise donc directement les fichiers InnoDB sur le disque, plutôt que de récupérer toute la base via une connexion mysql au serveur de BDD.

Il offre donc

  • des sauvegardes sans lock des tables2
  • des sauvegardes incrémentales, puisqu’il lui suffit de sauvegarder les nouvelles entrées du journal de transactions d’InnoDB
  • une restauration beaucoup plus rapide, puisqu’il s’agit d’une simple copie de fichier, et non un énorme script SQL à exécuter

J’ai donc écrit un petit script permettant de faire des backups complètes et incrémentales. Les backups complètes sont effectuées une fois par jours, et les backups incrémentales toutes les heures.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/bin/sh

# Bonne pratique: arrêter le script si une commande retourne un code d'erreur autre que 0, 
# et si une variable non-existante est utilisée
set -eu

# L'endroit où sont stockées les sauvegardes
WD=/var/backups/mysql
# `latest` est un lien symbolique vers la dernière sauvegarde
LATEST=$WD/latest

# On récupère le nom complet de l'ancienne backup
PREVIOUS=`readlink -f $LATEST`
# La nouvelle backup aura comme nom AAMMJJ-HHSS
NEXT=$WD/`date '+%Y%m%d-%H%M'`

if [ -d "$NEXT" ]; then
    echo "\`$NEXT' already exists."
    exit 1
fi

if [ "$#" -ge 1 ] && [ "$1" = "full" ]; then
    # Les backups "full" ont en plus `-full` dans le nom
    NEXT=$NEXT-full
    xtrabackup --backup --target-dir=$NEXT 2> $NEXT.log
else
    if ! [ -L "$LATEST" ]; then
        echo "\`$LATEST' does not exists. Consider doing a full backup first."
        exit 1
    fi

    xtrabackup --backup --target-dir=$NEXT --incremental-basedir=$PREVIOUS 2> $NEXT.log
fi

# On supprime les anciens liens symboliques
rm -f $LATEST $LATEST.log

# Et on les recréé avec la sauvegarde fraîche
ln -s $NEXT $LATEST
ln -s $NEXT.log $LATEST.log

Plus qu’à exécuter ce script régulièrement le script via cron:

1
2
3
4
root@zdsprod1:~# crontab -l
# min hour dom month dow command
0 * * * * /var/backups/mysql/backup.sh
15 3 * * * /var/backups/mysql/backup.sh full

Et voilà le résultat !

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
root@zdsprod1:~# ls -l /var/backups/mysql
…
drwxr-x---  5 root root 4.0K Mar 10 02:00 20180310-0200
-rw-r--r--  1 root root  47K Mar 10 02:00 20180310-0200.log
drwxr-x---  5 root root 4.0K Mar 10 03:00 20180310-0300
-rw-r--r--  1 root root  47K Mar 10 03:00 20180310-0300.log
drwxr-x---  5 root root 4.0K Mar 10 03:15 20180310-0315-full
-rw-r--r--  1 root root  46K Mar 10 03:15 20180310-0315-full.log
drwxr-x---  5 root root 4.0K Mar 10 04:00 20180310-0400
-rw-r--r--  1 root root  46K Mar 10 04:00 20180310-0400.log
drwxr-x---  5 root root 4.0K Mar 10 05:00 20180310-0500
-rw-r--r--  1 root root  46K Mar 10 05:00 20180310-0500.log
…

À titre indicatif, une sauvegarde complète pèse 1.2 Go non-compressée, 200 Mo compressé, et une incrémentale pèse en moyenne 15 Mo.


Ces sauvegardes sont ensuite récupérées sur mon serveur toutes les heures via rsync:

1
rsync -avr zdsprod1:/var/backups/mysql/ /tupperware/backups/zestedesavoir/db

Histoire de pas exploser le stockage de la prod, j’ai écrit un petit script pour garder que les deux dernières backups complètes (+ les incrémentales entre). Les anciennes sauvegardes restent bien sûr sur mon serveur.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/sh

set -eu

WD=/var/backups/mysql

BACKUPS="`echo $WD/*-*/ | tr ' ' '\n' | sort -nr`"

TO_DELETE="`
    echo "$BACKUPS" | awk '
        BEGIN { full=0 }
        { if (full > 1) { print $0 } }
        /full/ { full++ }
    '
`"

[ -z "$TO_DELETE" ] || rm -r "$TO_DELETE"

  1. Le moteur de stockage par défaut sur MySQL 

  2. Je sais, mysqldump peut ne pas locker les tables, mais il lock quand même les transactions pendant la backup, ce qui "met en pause" toute opération pendant le dump. 

rsync et snapshots ZFS

J’ai donc mentionné plus tôt que j’utilise ZFS sur mon serveur comme système de fichier.

ZFS a été développé à l’origine par Oracle pour Solaris. Il a été ensuite porté notamment sur FreeBSD, et plus récemment sur Linux (via ZFSOnLinux).

ZFS a la particularité de faire à la fois gestionnaire de volumes logiques (comme LVM) & RAID (mdadm sur linux) et du système de fichier. Il permet de gérer un ensemble de disques durs comme un seul ensemble (éventuellement avec de la redondance pour être tolérant aux pannes), que l’on peut subdiviser en multiples sous-volumes (datasets, dans le jargon ZFS) avec différentes propriétés. On pourra donc attribuer différents droits aux utilisateurs en fonction du volume, limiter la taille maximum de chaque volume, et activer certaines fonctionnalités type compression et deduplication à la volée par volume.

ZFS permet également de faire des snapshots instantanément et sans coût d’un volume. Une snapshot ne va prendre comme place sur le disque que la différence entre la snapshot et le volume actif. Idéal donc, pour des backups.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
spaetzle/root ~ > zfs list -t snap
NAME                                                  USED  AVAIL  REFER  MOUNTPOINT
…
tupperware/backups/zestedesavoir/data@20180310-1602   412K      -  5.56G  -
tupperware/backups/zestedesavoir/data@20180310-1702   884K      -  5.56G  -
tupperware/backups/zestedesavoir/data@20180310-1802   876K      -  5.56G  -
tupperware/backups/zestedesavoir/data@20180310-1902   844K      -  5.57G  -
tupperware/backups/zestedesavoir/data@20180310-2303      0      -  5.57G  -
tupperware/backups/zestedesavoir/data@20180311-0002      0      -  5.57G  -
tupperware/backups/zestedesavoir/data@20180311-0102      0      -  5.57G  -
…

Les snapshots sont donc très légères, avec en général moins d’1 Mo par snapshot.


Le script pour synchroniser le dossier /opt/zds/data et les backups de la base, et faire la snapshot est relativement simple:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/sh

BASE=tupperware/backups/zestedesavoir

echo "Syncing data"
rsync -azvr --delete zdsprod1.zestedesavoir.com:/opt/zds/data/ /$BASE/data
echo "Creating snapshot"
zfs snapshot $BASE/data@`date '+%Y%m%d-%H%M'`
echo "Syncing database"
rsync -azvr zdsprod1.zestedesavoir.com:/var/backups/mysql/ /$BASE/db

Ce script est lancé 1 fois par heure, 2 min après la backup incrémentale de la base.

La cerise sur le gâteau: compression à la volée des volumes

ZFS permet de compresser des volumes entiers de manière transparente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
spaetzle/root ~ > zfs get used,compressratio,compression,logicalused tupperware/backups/zestedesavoir/{db,data}
NAME                                   PROPERTY       VALUE     SOURCE
tupperware/backups/zestedesavoir/data  used           5.57G     -
tupperware/backups/zestedesavoir/data  compressratio  1.17x     -
tupperware/backups/zestedesavoir/data  compression    lz4       local
tupperware/backups/zestedesavoir/data  logicalused    5.89G     -
tupperware/backups/zestedesavoir/db    used           666M      -
tupperware/backups/zestedesavoir/db    compressratio  3.11x     -
tupperware/backups/zestedesavoir/db    compression    lz4       local
tupperware/backups/zestedesavoir/db    logicalused    1.88G     -

Ainsi, si les backups de la base semblent peser près de 2 Go, sur le disque ils ne prennent que 700 Mo.

Ça fait partie des fonctionnalités qui rendent ZFS awesome. Sachez que je n’ai couvert ici qu’une petite partie des fonctionnalités de ZFS.


Conclusion, j’aime ZFS.

À plus ou moins long terme j’aimerais mettre en place un autre serveur à un autre endroit, pour notamment avoir de la redondance à deux endroits physiques distincts sur mes backups. Oh, est-ce que j’ai mentionné que ZFS permet d’envoyer et recevoir des datasets entiers ou incrémentalement via de simples pipes ?

Et les backups de ZdS sont à nouveau un peu plus sérieuse, et on devrait pouvoir réagir plus rapidement en cas d’incident.

15 commentaires

Par contre il manque des quotes autour de tes chemins pour etre correct ! Sinon tu auras des soucis dès qu’il y aura un espace !

unidan

En théorie y’a pas de raison que j’ai d’espace dans les noms de dossiers. :-°

Mais bien vu, je vais changer ça.

+0 -0

Merci pour ce billet très intéressant, je ne connaissais pas le logiciel Xtrabackup, mais je vais me pencher dessus, du coup. :)

Je faisais de mon côté déjà des sauvegardes incrémentales à l’aide de mysqldump et de l’activation des binary logs (la procédure est détaillée ici), mais Xtrabackup à l’air plus simple d’emploi aussi bien pour sauvegarder que pour charger les données. ^^

+0 -0

une restauration beaucoup plus rapide, puisqu’il s’agit d’une simple copie de fichier, et non un énorme script SQL à exécuter

Ça m’étonne un peu, MySQL/InnoDB a la réputation ne ne pas supporter la sauvegarde/restauration par copie de fichiers. C’est maintenant le cas ?

Par curiosité, tu as essayé de restaurer une sauvegarde (genre, la dernière et une vieille) ? Il y a des points d’attention sur cette procédure ?

une restauration beaucoup plus rapide, puisqu’il s’agit d’une simple copie de fichier, et non un énorme script SQL à exécuter

Ça m’étonne un peu, MySQL/InnoDB a la réputation ne ne pas supporter la sauvegarde/restauration par copie de fichiers. C’est maintenant le cas ?

Effectivement, c’est pas tout à fait clair. En fait, il se base sur le mécanisme de récupération d’InnoDB. En cas de crash, MariaDB est capable de récupérer l’état de la base grâce aux fichiers de logs binaires présents sur le disque.

XtraBackup va demander le verrouillage de ces logs (LOCK BINLOG FOR BACKUP), copier les fichiers, et lancer ce mécanisme de récupération d’InnoDB.

cf. https://www.percona.com/doc/percona-xtrabackup/LATEST/how_xtrabackup_works.html

Par curiosité, tu as essayé de restaurer une sauvegarde (genre, la dernière et une vieille) ? Il y a des points d’attention sur cette procédure ?

SpaceFox

Sur une backup full en local1, ouais. J’ai prévu d’écrire un script pour gérer les backups incrémentales, puisqu’il faut appliquer unes par unes les backups incrémentales par dessus la backup full (ce qui demande un peu d’automatisation si on veut pas se taper potentiellement 24 backups incrémentales à appliquer à la main).

J’aimerais aussi utiliser ces backups pour synchroniser la beta, aussi.


  1. sur le serveur de sauvegarde 

+2 -0

Juste un petit truc qui m’intrigue, tu es le seul à faire des backup ? En cas de problème sur ZDS, qu’arriverait t’il si tu n’es pas joignable/problème de sauvegarde/etc. ?

Le système ne pourrait t’il pas être utilisée avec le Cloud storage archives de OVH (qui est relativement peu chère) ?

Juste un petit truc qui m’intrigue, tu es le seul à faire des backup ? En cas de problème sur ZDS, qu’arriverait t’il si tu n’es pas joignable/problème de sauvegarde/etc. ?

Le système ne pourrait t’il pas être utilisée avec le Cloud storage archives de OVH (qui est relativement peu chère) ?

WinXaito

Si vraiment, on a des snapshots Gandi du disque régulièrement1, qui sont elles accessibles à un certain nombre de personnes dans l’équipe technique.

Combiné au fait qu’on garde 2j de backups incrémentales de la DB sur le disque du serveur, je pense qu’on est pas trop mal. :)


  1. 3 snapshots par jour, fait toutes les 6 heures (6h, 12h,et 18h). 6 snapshots des 6 derniers jours, fait toutes les semaines (24h, 48h, 72h, 96h, 120h, et 144h - 6 jours). 2 snapshots des deux derniers mois, fait toutes les 28 jours. 

+0 -0

:magicien: 1, 2, 3… So… Sauvegarde !

Je ne comprends pas trop la différence/interaction entre MySQL et innoDB.

Tout ce qui est de l’interprétation du langage SQL c’est le SGBDR donc MySQL puis ce qui gère des données c’est le moteur innoDB?

+0 -0

Tout ce qui est de l’interprétation du langage SQL c’est le SGBDR donc MySQL puis ce qui gère des données c’est le moteur innoDB?

A-312

De ce que je sais, ouais. MySQL peut fonctionner avec différents moteurs de stockages, qui vont du coup s’occuper de stocker sur le disque les données. Pendant très longtemps celui par défaut c’était MyISAM, et depuis la 5.6 par défaut c’est InnoDB. Mais il en existe d’autres, cf. https://dev.mysql.com/doc/en/storage-engines.html

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte