Pour des raisons évidentes de sécurité, j’ai cherché un script de backup automatique pour mon serveur. Je voulais un backup de plusieurs dossiers et des bases de données. Le backup devait être envoyé directement sur un serveur FTP. Jusque là pas trop de problème, j’ai trouvé 2-3 scripts qui faisait cela. Mais je voulais également indiquer des destinations différentes pour les backups. Je m’explique. Je me suis proposé pour heberger le wiki de Syncany, un Dropbox like mais où l’hébergement n’est pas assuré par une société mais par la personne elle même. Pour plus de sécurité je voulais faire des backups sur plusieurs serveurs FTP. Mais je voulais également séparer les backups de mes sites et de celui de Syncany.
J’ai donc écrit un petit script en PHP qui permet très simplement de configurer :
- Les serveurs FTP
- Les dossiers à sauvegarder
- Les bases de données à sauvegarder
- Le temps de vie des backups
Je pense que la configuration est assez clair. Sinon faites un commentaire et je préciserais les choses.
<?php set_time_limit(0); $deb = microtime(true); echo "start backup\n"; $servers = array( 1 => array('monftp.mondomaine.me', 'myFTPUser', 'myFTPPass', '/myBackupDir'), 2 => array('nestle.com', 'george', 'clonney', './nespresso'), ); // name => array(local dir, array(server1, server2, ...)) $backupDir = array( 'configSys' => array('/etc/', array(1)), 'syncany' => array('/var/www/syncany', array(1, 2)), 'www' => array('/var/www', array(1)) ); // bdd name => array(server1, server2, ...) // 'all' mean all databases $backupMysql = array( 'all' => array(1) 'syncany' => array(2) ); $TMP_DIR = '/var/tmp/'; // temp dir $EMAIL = 'myEmailAddress'; //email address $DAY = 30; //time to live of backup in days $sql_host = 'localhost'; //mysql host $sql_user = 'mySqlUser'; //mysql user $sql_pass = 'mySqlPass'; //mysql pass $errorMsg = "Errors list : \n\n"; $oneErr = false; function addError($msg){ global $errorMsg, $oneErr; $errorMsg .= "\t".$msg."\n"; echo "ERROR : ".$msg."\n"; $oneErr = true; } $infosMsg = "Infos list : \n\n"; function addMsg($msg){ global $infosMsg; $infosMsg .= "\t".$msg."\n"; echo $msg."\n"; } $now = date('Y-m-d'); //localdir backup foreach($backupDir as $name => $backup){ $dir = $backup[0]; addMsg("Backup of local dir : '$dir'"); $file = "file-$name-$now.tar.gz"; exec("tar -zcvf $TMP_DIR/$file $dir");//compression foreach($backup[1] as $srvID){ $srv = $servers[$srvID]; addMsg("\ttry to send to : ".$srv[0]); $conn = ftp_connect($srv[0], 21) or addError('Cannot connect to '.$srv[0]); ftp_login($conn, $srv[1], $srv[2]) or addError('Cannot login to '.$srv[0]); ftp_put($conn, $srv[3]."/".$file, "$TMP_DIR/$file", FTP_BINARY); ftp_close($conn); } exec("rm $TMP_DIR/$file"); } addMsg(""); //backup of database $listDB; exec("mysql -u $sql_user -h $sql_host -p$sql_pass -Bse 'show databases'", $listDB); foreach($backupMysql as $name=> $listSrv){ addMsg("backup of bdd : $name"); $list = $name == 'all' ? $listDB : array($name); $listFile = array(); foreach($list as $db){ if($db == 'information_schema') continue; $file = "mysql-$db-$now.gz"; $listFile[] = $file; exec("mysqldump -u $sql_user -h $sql_host -p$sql_pass $db | gzip -9 > $TMP_DIR/$file"); } foreach($listSrv as $srvID){ $srv = $servers[$srvID]; addMsg("\ttry to send to ".$srv[0]); $conn = ftp_connect($srv[0], 21) or addError('Cannot connect to '.$srv[0]); ftp_login($conn, $srv[1], $srv[2]) or addError('Cannot login to '.$srv[0]); foreach($listFile as $file){ ftp_put($conn, $srv[3]."/".$file, "$TMP_DIR/$file", FTP_BINARY); } ftp_close($conn); } foreach($listFile as $file) exec("rm $TMP_DIR/$file"); } //remove old backups foreach($servers as $srv){ addMsg("Delete of old file on : ".$srv[0]); $conn = ftp_connect($srv[0], 21) or addError('Cannot connect to '.$srv[0]); ftp_login($conn, $srv[1], $srv[2]) or addError('Cannot login to '.$srv[0]); ftp_chdir($conn, $srv[3]); foreach(ftp_nlist($conn, '') as $file){ $delta = (time()-ftp_mdtm($conn, $file))/(3600*24); if($delta>$DAY){ ftp_delete($conn, $file); addMsg("\t $file"); } } ftp_close($conn); } addMsg("\nBackup done in :".(round(microtime(true)-$deb))."sec"); $titre = $oneErr ? "[ERREUR] " : ""; $titre .= "Backup ".exec("hostname"); mail($EMAIL, $titre, $infosMsg."\n\n".$errorMsg); ?>
Exemple d’email reçu :
Liste des infos :
backup de ‘/etc/’
Envoie sur ftp.monserver.me
backup de ‘/var/www/syncany’
Envoie sur ftp.monserver.me
backup de ‘/var/www’
Envoie sur ftp.monserver.mebackup de la bdd all
Envoie sur ftp.monserver.me
Suppression des vieux fichiers sur ftp.monserver.me
mysql-graphsconcept-2011-07-31.gz
mysql-passvacs-2011-07-31.gz
mysql-syncany-2011-07-31.gz
file-configSys-2011-07-31.tar.gz
file-syncany-2011-07-31.tar.gz
file-www-2011-07-31.tar.gz
mysql-android-2011-07-31.gz
mysql-android_new-2011-07-31.gz
mysql-ddosproject-2011-07-31.gz
mysql-engiby-2011-07-31.gz
mysql-falconmedia-2011-07-31.gz
mysql-gaetancollaud-2011-07-31.gz
mysql-gagatemplate-2011-07-31.gz
mysql-mysql-2011-07-31.gz
mysql-proftpd-2011-07-31.gzBackup effectue en :715sec
Liste des erreurs :
Salut,
Cette fonctionnalité me semble intéressante pour notre NG-Server.
Est-ce qu’on pourrait l’intéger?
Elle pourrait être configurable par notre interface ‘Manager’.
Question à laquelle je n’ai jamais de réponse: Comment utiliser les backups pour faire des restore ciblés, par table et période?
Nicolas
Oui on pourrait facilement l’intégrer.
Ce script fait un backup complet de la base de données et d’un dossier à un temps donner. On peut ensuite facilement l’importer grâce à un outil MySql tel que PhpMyAdmin ou autre. Pour les dossiers, il suffit de décompresser et de copier ce qui nous intéresse.