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/
:CODE
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).")rn";
};
function send_log_mail ()
{
$headers = "From: ".MAIL_FROM."rn"
."Reply-To: ".MAIL_FROM."rn"
."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();
:CODE
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 $backupfilepathn";
@unlink($backupfilepath);
};
echo "Remove $backupdirpathn";
@rmdir($backupdirpath);
};
:CODE
Ce code est suffisamment lisible pour être aisément modifiable, le cas échéant.