www.RiffZone.net
code.RiffZone.net
login.RiffZone.net
Retour au Sommaire

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 le suivant: Exporter chaque nuit à heure fixe l'intégralité des bases du serveur vers un répertoire défini. Nos sites sont dans des sous-répertoires de '/var/www', mettons donc en place l'arborescence suivante:

/var/www/ domaine1/ domaine2/ domaine3/ sqlbackup/ backup.php data/

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. Voici le code du script en question:

<?php define('DB_HOST', 'localhost'); define('DB_USER', 'sqlbackup'); define('DB_PASS', '*********'); define('MAIL_TO', 'admin@domain.tld'); define('MAIL_FROM', 'admin@domain.tld'); define('MAIL_SUBJECT', 'SQLDumps report'); $GLOBALS['log'] = ""; function get_bases_list () { $bases = array(); if ($sqlconnexion = mysqli_connect(DB_HOST, DB_USER, DB_PASS, 'information_schema')) { $sqlquery = "SELECT SCHEMA_NAME FROM SCHEMATA"; if ($sqlresult = mysqli_query($sqlconnexion, $sqlquery)) { while ($sqlrow = mysqli_fetch_array($sqlresult, MYSQLI_ASSOC)) { $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 dirname(realpath(__FILE__)).'/data/'.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' .' -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).")\r\n"; }; function send_log_mail () { $headers = "From: ".MAIL_FROM."\r\n" ."Reply-To: ".MAIL_FROM."\r\n" ."X-Mailer: PHP/".phpversion(); echo mail(MAIL_TO, MAIL_SUBJECT, $GLOBALS['log'], $headers) ? "[OK] Mail sent.\n" : "[ERR] Mail NOT sent.\n"; }; check_backup_dir(); $bases = get_bases_list(); foreach ($bases as $base) { backup_base($base); }; send_log_mail();

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

Purger les anciennes sauvegardes

On peut vouloir purger automatiquement les sauvegardes un peu anciennes, le code suivant est à rajouter à la fin du précédent et permet de supprimer les exports antérieurs de plus de 30 jours, excepté le premier export de chaque mois:

$backupdirnames = scandir(dirname(realpath(__FILE__)).'/data'); 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 $backupfilepath\n"; @unlink($backupfilepath); }; echo "Remove $backupdirpath\n"; @rmdir($backupdirpath); };

Ce code est suffisamment lisible pour être aisément modifiable, le cas échéant.