Utiliser Git comme outil de sauvegarde pour WordPress
Attention, pure expérimentation de ma part. J’ai déjà pu lire des développeurs utiliser Git avec WordPress, mais uniquement pour le déploiement de leurs créations maisons, de thèmes notamment. Pour la sauvegarde du blog, je me suis pour l’instant concentré sur mon script maison. Mais il a un sacré désavantage : la taille des sauvegardes, et donc leur temps de transfert sur ma connexion moisie. J’ai donc tenté de détourner Git de son utilisation première pour en faire un outil de sauvegarde « incrémentielle », à la consommation plus mesurée.
Je suis conscient que ce n’est pas forcément la méthode la plus fiable. Je ne compte d’ailleurs pas laisser tomber mon script pour autant (j’ai « juste » besoin de lui ajouter la gestion de la suppression des vieilles sauvegardes). Mais il sauvegarde une fois par semaine, et ces temps-ci, il n’est pas rare que j’intervienne tous les jours, sans parler de vous sur les commentaires, il me faut donc un moyen de faire un « snapshot » plus léger, plus fréquent, idéalement une fois par jour.
En effet, git ne sauvegarde que les différences entre chaque commit, donc en théorie, on devrait n’avoir que les différences de sauvegardées tous les jours, et surtout transférées. Parce que 200+Mo avec un ADSL 2Mbps, ça pique un peu, et c’est très prompt aux « plantages ». Sans parler du problème de la taille du stockage.
Pour l’expérience, j’ai travaillé sur une copie du blog à la maison, en utilisant justement une des sauvegardes que j’ai à disposition. Le dépôt distant était en fait sur la même machine, dans un autre dossier avec un autre utilisateur, le but étant avant tout de vérifier la taille consommée à chaque commit.
NB : je ne gère que la problématique du blog WordPress pour l’instant, là où mon script actuel permet de faire un package avec Piwik. L’adaptation ne sera pas trop difficile cependant, voire même pour votre propre site quelque soit sa configuration.
La problématique des droits utilisateurs
En effet, pour être efficace, git doit pouvoir idéalement être propriétaire des fichiers dont il a la charge dans un dépôt. Si l’on en a la possibilité (parce qu’on utilise PHP-FPM, par exemple), on utilisera donc le compte du propriétaire, différent de celui du serveur web pour gérer tout ça. Sinon, il faudra utiliser le compte www-data pour toutes les opérations qui suivront.
Initialiser le dépôt Git, et le configurer
Quand on a réglé ces soucis de droits utilisateurs, on peut s’attaquer à la gestion du dépôt. On se rend dans le dossier du site, et on attaque l’initialisation :
1 2 |
cd /home/monuser/www/ && git init Initialized empty Git repository in /home/monuser/www/.git/ |
Un git status nous permet de vérifier qu’il est bien en service :
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 |
$ git status # On branch master # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .htaccess # favicon.ico # index.php # license.txt # wp-activate.php # wp-admin/ # wp-blog-header.php # wp-comments-post.php # wp-config-sample.php # wp-config.php # wp-content/ # wp-cron.php # wp-includes/ # wp-links-opml.php # wp-load.php # wp-login.php # wp-mail.php # wp-settings.php # wp-signup.php # wp-trackback.php # xmlrpc.php nothing added to commit but untracked files present (use "git add" to track) |
Avant d’attaquer le commit de base, on va modifier quelques paramètres du dépôt. Je vous renvoie à mon introduction à Git, qui commence à dater, mais qui s’avère toujours d’actualité pour les besoins qu’on a ici.
Première sauvegarde, en mode manuel
On est donc dans le dossier qui contient tous les fichiers, reste à ajouter la base de données. Si on repense à mon script de sauvegarde, on fait donc appel à ce bon vieux mysqldump :
1 |
mysqldump -u monuser -pmonpassword monuser >monuser.sql |
On peut dès lors préparer le premier commit :
1 2 |
git add * fatal: Not a git repository: wp-content/plugins/crayon-syntax-highlighter/js/jquery-colorpicker/../../.git/modules/js/jquery-colorpicker |
Problème : le développeur de Crayon Syntax Highlighter utilise lui aussi Git, et a laissé pas mal de fichiers .git* dans les dossiers du plugin distribué sur le dépôt WordPress. En particulier, un fichier .git dans le dossier wp-content/plugins/crayon-syntax-highlighter/js/jquery-colorpicker qui indique un chemin qui n’existe pas. Après avoir tenté sans succès de l’exclure au moyen de .gitignore, j’ai finalement décidé de supprimer le fichier :
1 |
rm -f wp-content/plugins/crayon-syntax-highlighter/js/jquery-colorpicker/.git |
Une deuxième erreur plus tard avec le fichier wp-content/plugins/crayon-syntax-highlighter/langs/vbnet/.git, le git add * prend du temps, mais se termine sans encombre.
Si on refait un git status, on se rend compte d’un petit truc :
1 2 3 4 5 6 7 8 9 10 |
(...) # new file: wp-settings.php # new file: wp-signup.php # new file: wp-trackback.php # new file: xmlrpc.php # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .htaccess |
Eh oui, le joker * ne permet pas d’ajouter les fichiers cachés, et donc on doit rajouter notre fichier à la main :
1 |
git add .htaccess |
Rappel : on utilise git rm pour supprimer un fichier de la liste des changements à enregistrer.
On peut dès lors passer au commit :
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 |
$ git commit -a -m "Sauvegarde Initiale" [master (root-commit) 657d734] Sauvegarde Initiale Committer: MonUser <monuser@test.(none)> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly: git config --global user.name "Your Name" git config --global user.email you@example.com After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 5564 files changed, 685879 insertions(+) create mode 100644 .htaccess create mode 100644 favicon.ico create mode 100644 index.php create mode 100644 license.txt create mode 100644 seboss666.sql create mode 100644 wp-activate.php create mode 100644 wp-admin/about.php create mode 100644 wp-admin/admin-ajax.php create mode 100644 wp-admin/admin-footer.php create mode 100644 wp-admin/admin-functions.php (...) |
Et voilà, si on veut vérifier que le commit est bien pris en compte :
1 2 3 4 5 6 |
$ git log commit 657d73409cf48c443e317f096b63cdfa875a6708 Author: MonUser <monuser@test.(none)> Date: Fri Apr 24 23:25:23 2015 +0200 Sauvegarde Initiale |
Parfait. Reste maintenant à passer à la deuxième étape.
Le dépôt distant
Comme il n’est pas question de créer le dépôt sur GitHub, il va falloir se retrousser un peu les manches. Dans mon cas, il ne sera accessible que par SSH avec la clé de l’utilisateur, parce que je n’ai besoin de rien d’autre (le dépôt « de sauvegarde » ne sera pas à vocation publique).
Sur la machine qui va recevoir le backup, j’ai choisi de reprendre l’utilisateur qui sert déjà au backup actuel (avec su monuser), mais je crée un nouveau dossier :
1 2 |
cd /home/monuser mkdir -p gitbackup.git |
Ensuite, je rentre dedans, et j’initialise un dépôt vide, presque comme n’importe quel dépôt. Toujours avec l’utilisateur en question :
1 2 |
cd gitbackup.git git init --bare |
De retour sur le serveur où se trouve WordPress, on retourne avec le bon utilisateur dans le bon dossier (notre dépôt), et on ajoute le distant, qu’on appellera backup (quelle originalité) :
1 |
git remote add backup monuser@test.seboss666.info:22:/home/monuser/gitbackup.git |
Une petite dernière chose avant de pousser. Contrairement à scp où l’on peut indiquer à la main quelle clé SSH utiliser, il faudra ici l’ajouter aux identités reconnues par SSH. Pour ça, on vérifie les chemins autorisés :
1 2 3 4 |
$ cat /etc/ssh/ssh_config |grep IdentityFile IdentityFile ~/.ssh/identity # IdentityFile ~/.ssh/id_rsa # IdentityFile ~/.ssh/id_dsa |
Donc dans ma config actuelle, lors d’un appel à ssh avec l’utilisateur en cours, il cherchera la clé dans le dossier ~/.ssh/identity. On va donc copier ma clé actuelle à cet endroit (créer le dossier .ssh si besoin) avec le bon nom :
1 |
cp /home/monuser/monuser.pub /home/monuser/.ssh/identity |
Normalement, on peut dès lors tenter d’envoyer notre premier commit sur le serveur :
1 |
git push --all backup |
Planifions les futures sauvegardes
Il va nous falloir un petit script et une incontournable tache cron pour faire notre petite affaire. En l’occurrence, le script devra faire plusieurs choses :
- Sauvegarder la base de données
- Ajouter les fichiers à commit (y compris les .htaccess)
- Commit avec la date en guise de message; on peut aussi utiliser les tags
- Pousser sur le dépôt distant
Ai-je vraiment besoin de vous remettre ce script ici ? Bon allez, je suis de bonne humeur aujourd’hui :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/bin/bash WDOSSIER="/home/monuser/" SITE1="www" DBUSER="monuser" DBPASSWORD="monpassword" DBNAME1="monuser" CURRENTDATE=`date +%y%m%d` cd ${WDOSSIER}/${SITE1} mysqldump -u $DBUSER -p$DBPASSWORD $DBNAME1 >$DBNAME1.sql git add * git add .* #git tag $DATE1 #si on veut utiliser les tags git commit -a -m $DATE1 git push backup |
On lui donnera le petit nom de gitsave.sh. Voilà la tache cron permettant de l’exécuter tous les matins à 3h30 (en me basant sur les tranches horaires d’activité de Piwik) :
1 |
30 3 * * * /home/monuser/backup/gitsave.sh |
Pourquoi 3h30 ? Le lundi je lance déjà la grosse sauvegarde à 3h, donc j’évite d’éventuelles collisions (à ce moment-là, il se concentre sur le téléchargement).
Comment restaurer ?
Si l’on a besoin de restaurer à une date antérieure, il suffira de faire un git log pour chercher le hash du commit à cette date, et de saisir cette commande :
1 |
git checkout hash_du_commit |
Ou plus simplement, si vous utilisez les tags (en respectant le format de date utilisé par le script) :
1 |
git checkout tags/<date> |
Ensuite, vous devez vider la base de données actuelle pour restaurer celle qui est intégrée dans le commit fraîchement restauré :
1 2 |
mysql -u monuser -omonpassword monuser < "DROP TABLE LIKE '%wp_%';" mysql -u monuser -omonpassword monuser < monuser.sql |
Et voilà, normalement le blog a changé de « date ». Il ne faudra pas oublier de refaire éventuellement les mises à jour de vos extensions et de vos thèmes.
Si l’on démarre d’une machine fraiche, on configure l’utilisateur (clé SSH surtout), on se rend dans le dossier parent qui va accueillir le site, et on clone le dépôt distant, enfin on importe la base de données avec la commande mysql juste au dessus (créer l’utilisateur/la base au besoin) :
1 2 |
git clone monuser@test.seboss666.info:22:/home/monuser/gitbackup.git mysql -u monuser -omonpassword monuser < monuser.sql |
Ne pas oublier les sauvegardes complètes (et les tester)
En effet, il n’est pas impossible que le dépôt Git local ou distant devienne inconsistant, pour une raison ou une autre. C’est la raison pour laquelle je conserve mon script de sauvegarde complète hebdomadaire, parce que ça permet de limiter la casse, quitte à perdre au maximum une semaine de travail.
Aussi, vérifier ses sauvegardes régulièrement est primordial. Il n’y a rien de pire que de se rendre compte que les sauvegardes fonctionnent mal depuis un moment et sont inexploitables (notamment parce que le transfert s’est mal passé, coupé en cours de route par une connexion merdique).
Un poil de sécurité
En effet, tout technicien aguerri aura deviné : avec une telle méthode, la sauvegarde de la base de données se trouve dans le dossier courant du site. Il serait donc possible, en testant toutes les combinaisons possibles, de trouver le lien qui permettrait d’afficher le fichier SQL dans un navigateur, ou de le télécharger, et donc d’en découvrir tous les secrets (enfin presque, mais vous avez compris). Pour empêcher ça, vous pouvez dire à votre serveur Web d’interdire l’accès à tous les fichier .sql de la boutique (c’est de toute façon une bonne pratique). Il suffira donc d’ajouter quelques lignes à la configuration du vhost :
1 2 3 4 5 6 7 8 9 10 11 |
#Apache <Files ~"\.(sql)$"> Order Allow,Deny Allow from none Deny from all </files> #Nginx location ~\.(sql)$ { return 403; } |
Bien sûr, un attaquant découvrira alors que le fichier existe bien. On pourrait plutôt retourner une erreur de type 404 pour brouiller les pistes. Soyez créatifs.
Profiter de la décentralisation de Git
On pourrait aussi se servir du dépôt Git pour pouvoir se faciliter la vie en cas de migration du site d’une machine à une autre. Si ça s’accompagne d’un changement de nom de domaine, il ne faudra pas oublier de modifier ce nom dans les liens, et ça, j’en ai déjà parlé il y a quelques mois 🙂
Juste une info au lieu de faire « git add * » fait plutôt « git add . », cela t’éviteras d’ajouter les fichier .htaccess (et autres) à la main.
Salut. Tu sauvegardes sur quel serveur ? Car l’idée est bonne , mais j’ai Un soucis de serveur distant, comme tu l’as dit GitHub Public est à exclure, mais pourquoi pas un Private ? C’est payant, voilà le « soucis ». Tu conseillles quoi ?
Sur la doc de Gitlab, il y a un système d’access token que tu peux paramétrer sur le compte qui doit servir au push : https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
Il suffit ensuite de paramétrer le stockage de l’access token sur le serveur (idéalement hors du docroot hein :D) pour que le push « privé' » fonctionne sans problème. C’est la solution que l’on a du utiliser sur notre Gitlab au boulot après avoir basculé l’authentification sur le SSO, la combinaison user:pass (auth basic) ne fonctionne plus; et on ne peut pas utiliser SSH car notre gitlab est déployé sur un cluster Openshift.
Bon du coup, je suis passé sur un repo privé gitlab.com, avec clé SSH , 7 Gb le site, les mises à jours sont rapides et je backup carrément tout le site entier 😛
C’est top ! Plus besoin de se prendre la tête avec un script bash, je lance ma sauvegarde sql avec un commit du site tous les jours à 1h du mat’ et voilà !