Des sauvegardes locales MySQL automatisées
Les sauvegardes, c'est important. Je ne m'étendrai pas ici sur les différents systèmes existants, mais sur le cas particulier des sauvegardes de bases MySQL vers un répertoire local du serveur, lequel sera sauvegardé ainsi que les autres répertoires de fichiers plats.
Le cahier des charges est d'exporter chaque nuit à heure fixe l'intégralité des bases du serveur vers un répertoire défini. On peut créer dans /opt l'arborescence suivante :
Pour accéder à toutes les bases sans avoir à utiliser un couple identifiant/mot de passe à chaque fois, commencez par créer un nouvel utilisateur MyQSL, nommé 'sqlbackup' et qui aura les droits "Données / SELECT" sur toutes les bases.
Pensez également à régler les droits du répertoire data/ pour que le script backup.php puisse librement y créer dossiers et fichiers.
Enfin, on ne gardera que les sauvegardes des 30 derniers jours, pour ne pas saturer le serveur, à l'exception par sécurité de la sauvegarde du premier jour de chaque mois.
Voici le code du script en question:
<?php // crontab : docker exec aphp php /opt/mysql/backup/sqlbackup.php define('DB_HOST', 'localhost'); define('DB_USER', 'myBackupUser'); define('DB_PASS', 'myBackupPassword'); define('DST_DIRPATH', '/opt/sqlbackup/data'); $GLOBALS['log'] = ""; function get_bases_list () { $bases = array(); if ($sqlconnexion = mysqli_connect(DB_HOST, DB_USER, DB_PASS)) { if (mysqli_select_db($sqlconnexion, 'information_schema')) { $sqlquery = "SELECT SCHEMA_NAME FROM SCHEMATA"; if ($sqlresult = mysqli_query($sqlconnexion, $sqlquery)) { while ($sqlrow = mysqli_fetch_assoc($sqlresult)) { $base = $sqlrow['SCHEMA_NAME']; if ($base == 'information_schema') continue; if ($base == 'performance_schema') continue; if ($base == 'mysql') continue; if ($base == 'phpmyadmin') continue; if ($base == 'sys') continue; $bases[] = $base; }; mysqli_free_result($sqlresult); }; }; mysqli_close($sqlconnexion); }; return $bases; }; function exec_shell_command ($theCommand) { $output_text = ""; $output_array = array(); exec($theCommand, $output_array); if (count($output_array) > 0) $output_text = implode("n", $output_array); return $output_text; }; function build_backup_dirpath () { return DST_DIRPATH.'/'.date('Ymd'); }; function check_backup_dir () { $backup_dirpath = build_backup_dirpath(); if (!file_exists($backup_dirpath)) @mkdir($backup_dirpath); }; function backup_base ($theBaseName) { $backup_dirpath = build_backup_dirpath(); if (!file_exists($backup_dirpath)) return; $backup_filepath = $backup_dirpath.'/'.$theBaseName.'.sql'; if (file_exists($backup_filepath)) return; $command = '/usr/bin/mysqldump --opt --lock-tables=false --no-tablespaces' .' -h '.DB_HOST.' -u '.DB_USER .' --password='.DB_PASS.' '.$theBaseName.' > '.$backup_filepath; echo $command."n"; echo exec_shell_command($command)."n"; $GLOBALS['log'] .= $theBaseName." (".filesize($backup_filepath).")rn"; }; check_backup_dir(); $bases = get_bases_list(); print_r($bases); foreach ($bases as $base) { backup_base($base); }; $backupdirnames = scandir(DST_DIRPATH); foreach ($backupdirnames as $backupdirname) { if (substr($backupdirname, 0, 1) == '.') continue; if ($backupdirname > date('Ymd', strtotime('-30 day'))) continue; if (substr($backupdirname, -2) == '01') continue; $backupdirpath = dirname(realpath(__FILE__)).'/data/'.$backupdirname; $backupfilenames = scandir($backupdirpath); foreach ($backupfilenames as $backupfilename) { if (substr($backupfilename, 0, 1) == '.') continue; $backupfilepath = $backupdirpath.'/'.$backupfilename; echo "Remove $backupfilepathn"; @unlink($backupfilepath); }; echo "Remove $backupdirpathn"; @rmdir($backupdirpath); };
Les définitions de constantes en tête de script sont bien sûr à adapter en fonction de votre propre environnement.
Enfin, on peut automatiser l'exécution quotidienne (ici à 3h du matin) en rajoutant cette ligne dans '/etc/crontab':
0 3 * * * root /usr/bin/php /var/www/sqlbackup/backup.php
On obtiendra au final un résultat de ce type :
Ce code est suffisamment lisible pour être aisément modifiable, le cas échéant. Je l'utilise depuis des années sans trop d'évolutions, il fait clairement le job. Enjoy !