Comment récupérer un 'rm -rf /var/lib/mysql/*'
Il y a un dicton que j’aime bien :
Dans la vie, il y a deux types d’administrateurs système :
- Celui qui a déjà fait une grosse connerie en production
- Et celui que ne va pas tarder
Et bien je viens de passer de la deuxième à la première catégorie en une ligne de commande :
Et bien entendu sur le serveur de production, et pas sur la future prod.
Bref, la GROSSE boulette. Sur une BDD de 80Go qui prend plusieurs heures à restaurer.
Et bien gros spoiler, nous avons réussi à tout restaurer.
Avant de lire la suite, je ne garantis pas que cela va fonctionner chez vous. Et décline toute responsabilité si vous perdez vos données.
Mais vous serez peut-être content de pouvoir récupéré un maximum de vos bases/tables.
Déjà, le premier truc à faire, c’est couper toute activité qu’il pourrait y avoir sur cette instance :
- Couper l’application
- Mettre une règle iptable
Peut importe, mais surtout réduire les accès à cette instance.
Le deuxième truc à avoir en tête, NE PAS ARRÊTER Mysql. C’est ce qui va nous sauver.
J’ai depuis écrit un script Python qui va permettre de sauvegarder un maximum de chose.
Ensuite, je vais rapidement expliquer pourquoi tout cela peut fonctionner.
Mais d’abord, voici comment reproduire ça chez vous (dans un Vagant). N’essayez pas ça sur un vrai serveur.
Mise en place de l’environnement de test
On va utiliser Vagrant
On va démarrer la restauration :
Et maintenant, laissons opérer la magie
Le script en mode --help
Récupération des fichiers supprimés
Extraction des données aux formats CSV pour plus de sécurité
Bon, si jamais la BDD est corrompue, on sera bien content d’avoir les données au format CSV. Par contre, dans mes tests, j’ai eu beaucoup de crash du moteur Mysql, il faut donc être très prudent dans les commandes. Mais aussi se dire que ça risque de planter et que l’on va tout perdre.
Et voilà !
Remettre les données en place
Diverses instructions
- Ne jamais arrêter Mysql, jamais, jamais, jamais, jamais, jamais, jamais
- Ne pas essayer de mysqldump pendant que l’on utilise cet outil, jamais (encore une fois)
- On identifie le process mysql avec la commande
ps ax | grep [m]ysqld
- On ne doit avoir qu’un seul process. Le noter pour plus tard
- On va vérifier les fichiers que l’on va pouvoir récupérer
sudo lsof -p $MYSQL_PID
- On peut voir tous les fichiers ‘(deleted)’
- Et tant que l’on a Mysql qui est lancé, on va pouvoir récupérer ces fichiers
Pourquoi ça peut fonctionner
Déjà, il faut comprendre comment fonctionne Linux en ext* au moment de la suppression d’un fichier.
On va revenir à la base. Quand un process ouvre un fichier, il ouvre un “file descriptor” FD dans lsof
.
Un fichier, c’est défini dans le système de fichier ext* comme un chemin qui pointe vers un inode.
Quand on supprime un fichier, on supprime ce lien dans la table d’allocation des fichiers.
Et ext* libère la place quand un inode n’a plus de FD ouvert sur l’inode.
C’est pour ça que des fois, on supprime un gros fichier, mais que la place n’est pas libérée.
Ou encore quand on fait un mv
d’un fichier de log, un touch du nouveau fichier et que le process continue à écrire dans le fichier qui a été renommé. Le FD est toujours ouvert sur l’ancien inode. Ça fait un peu mal à la tête hein ?
Et donc comme Mysql garde les fichiers ouverts, on peut faire un cat /proc/$PID/df/9
Voilà pour la partie récupération des fichiers supprimés.
Maintenant, les histoires de touch
.
C’est la partie que je maîtrise moins, mais j’imagine le comportement de Mysql.
Quand on fait un show databases;
Mysql doit lire la liste des répertoires dans /var/lib/mysql
. C’est pour ça que l’on peut se retrouver avec une base de donnés lost+found
.
Ensuite quand on fait un show tables;
Mysql doit lire la liste des fichiers *.MYI
ou *.MYD
(ou les deux, ou quand l’un manque ça doit planter). Donc un touch des fichiers fait apparaître les tables. Mais comme les fichiers sont déjà ouverts, il va pouvoir accéder aux contenu des anciens fichiers supprimés.
Par contre un desc table
ou un select
ne va pas fonctionner. Pour cela Mysql a besoin des fichiers *.frm
qui contiennent la structure des tables. Et là, il faut que les fichiers frm/MYD/MYI
soient bien alignés.
Voilà, il ne nous reste que le Mysqldump qui ne fonctionne pas. Je pense qu’il essaye de lire directement dans le contenu des fichiers et que la bidouille avec les touch
le perturbe.
J’en ai fini pour cet article. J’espère vous avoir appris des trucs. Que vous n’aurez jamais besoin de mettre tout ça en pratique sur de la prod.
Et si j’ai des conseils à donner :
- Faire des backup
- Tester les restaurations
- Noter le temps de ces opérations pour avoir conscience du temps que cela va prendre en cas de crash
- Si vous faites des mysqldump, pensez à aussi faire des sauvegardes des .frm régulièrement