Небольшой скрипт для создания и ротации бэкапов, который забирает файлы с удаленного сервер (php / linux).
Система для создания бэкапов
07.04.2024
Выполняем каждую среду:
0 05 * * 3 /usr/bin/php -f /path/to/backupper.php >> /path/to/main.log
Необходимо доставить библиотеку для работы с БД. В composer.json:
{ "require": { "oddler/pdo": "^1.8" } }
В консоли
composer install
Осноыной скрипт backupper.php:
<?php error_reporting(E_ALL); ini_set('display_errors', 'On'); define('SO_ROOT', __DIR__); define('SO_PATH_ARCHIVE', '/backup/files/'); define('SO_PATH_REMOTE_BASE', '/home/bitrix/backup/'); //define('SO_PATH_REMOTE_WORK', SO_PATH_REMOTE_BASE.'test/'); define('SO_PATH_REMOTE_WORK', SO_PATH_REMOTE_BASE.'archive/'); define('SO_REMOTE_HOST', 'bitrix@XXX.YYY.ZZZ.1'); define('SO_INFO_FILE', 'info.json'); class soStatus { const newFile = 0; const forDownload = 1; const skipped = 2; const downloaded = 3; const error = 99; } require_once('vendor/autoload.php'); use Oddler\Pdo\DBCore; class arBackup extends Oddler\Pdo\Table { public $id = ''; public $file_name = ''; public $date_start = ''; public $date_end = ''; public $deleted = ''; public $downloaded = ''; public $message = ''; public $crc = ''; public function __construct(&$oDB = NULL) { $this->construct('backups', 'id', $oDB); } } class soMain { protected $_db = null; protected $_aRemoteFiles = []; protected $_aLastRemoteFile = []; public function __construct() { $DBCore = DBCore::getInstance(); $this->_db = $DBCore->connect( array( 'host' => 'localhost', 'user' => 'backupper', 'password' => 'PASS', 'database' => 'db_backupper', 'charset' => 'utf8', 'database_type' => 'mysql' )); } protected function updateInfo() { // Запускаем удаленный скрипт, для получения информации exec("ssh ".SO_REMOTE_HOST." 'php ".SO_PATH_REMOTE_BASE."/get_info.php'"); // Скачиваем инфу exec('scp -r '.SO_REMOTE_HOST.':'.SO_PATH_REMOTE_BASE.SO_INFO_FILE.' '.SO_ROOT.'/'.SO_INFO_FILE); // Получить инфу $this->_aRemoteFiles = json_decode(file_get_contents(SO_ROOT.'/'.SO_INFO_FILE)); } public function pullOutLastFile() { $lastDate = '2000-01-01'; foreach($this->_aRemoteFiles as $sName => $crc) { $date = $this->extractDate($sName); if ($lastDate < $date) { $lastDate = $date; $this->_aLastRemoteFile = [$sName => $crc]; } } } protected function query($sSQL) { $this->_db->setQuery($sSQL); return $this->_db->query(); } protected function queryAll($sSQL) { $this->_db->setQuery($sSQL); return $this->_db->loadObjectsList(); } protected function queryOne($sSQL) { $this->_db->setQuery($sSQL); return $this->_db->loadObject(); } protected function say($sText) { echo date('Ymd').': '. $sText."\n"; } protected function extractDate($sText) { $sPattern = "|([0-9]{2}).([0-9]{2}).([0-9]{4})|si"; preg_match_all($sPattern, $sText, $aMatches, PREG_SET_ORDER); // echo '<pre>'; // print_r($aMatches); // echo '</pre>'; return $aMatches[0][3].'-'.$aMatches[0][2].'-'.$aMatches[0][1]; } protected function syncDB() { foreach($this->_aRemoteFiles as $sName => $crc) { $this->say($sName); $sSQl = 'SELECT count(*) AS c FROM backups WHERE `file_name` = "'.$sName .'" AND deleted = 0 AND downloaded > ' . soStatus::forDownload; $oRes = $this->queryOne($sSQl); // $this->say($sSQl); // $this->say($oRes->c); if (!$oRes->c) { $oBackup = new arBackup(); // $oBackup = new stdClass(); $oBackup->file_name = $sName; $oBackup->crc = $crc; $oBackup->date_start = $this->extractDate($sName); // MONTH WEEK $oBackup->date_end = date('Y-m-d', strtotime('+3 WEEK', strtotime($oBackup->date_start))); $oBackup->deleted = 0; if (key($this->_aLastRemoteFile) == $sName) { $oBackup->downloaded = soStatus::forDownload; } else { $oBackup->downloaded = soStatus::skipped; } $oBackup->message = ''; $oBackup->save(); } } } // Скачиваем файлы которых нет protected function downloadNew() { $aRes = $this->queryAll('SELECT id FROM backups WHERE deleted = 0 AND downloaded = ' . soStatus::forDownload); if (count($aRes)) { foreach($aRes as $oRow) { $oBackup = new arBackup(); $oBackup->load($oRow->id); $sFile = SO_PATH_ARCHIVE.'/'.$oBackup->file_name; exec('scp -r '.SO_REMOTE_HOST.':'.SO_PATH_REMOTE_WORK.$oBackup->file_name.' '.$sFile); // Проверяем md5 if(!md5_file($sFile) == $oBackup->crc) { $sTMP7 = 'md5_error'; file_get_contents('http://api.oddler.ru/tools/?task=telegram&message='.$sTMP7.'&token=123'); $oBackup->message = 'crcError'; $oBackup->downloaded = soStatus::error; } else { $oBackup->downloaded = soStatus::downloaded; } $oBackup->save(); } } else { $this->say('Nothing to download'); } } protected function deleteOld() { $aRes = $this->queryAll('SELECT id FROM backups WHERE downloaded = '.soStatus::downloaded.' AND deleted = 0 AND date_end <= "'.date('Y-m-d').'"'); foreach($aRes as $oRow) { $oBackup = new arBackup(); $oBackup->load($oRow->id); $sFile = SO_PATH_ARCHIVE.'/'.$oBackup->file_name; $this->say('Unlink: ' . $sFile); unlink($sFile); $oBackup->deleted = 1; $oBackup->save(); // $sTMP7 = 'time_2_remove'; // file_get_contents('http://api.oddler.ru/tools/?task=telegram&message='.$sTMP7.'&token=123'); } } public function run() { $bClear = false; // $bClear = true; if ($bClear) { $this->query('TRUNCATE backups'); } else { $this->updateInfo(); $this->pullOutLastFile(); //! $this->query('TRUNCATE backups'); $this->syncDB(); //// $aRes = $this->queryAll('SELECT * FROM backups'); //// print_r($aRes); $this->downloadNew(); // Удаляем старые файлы $this->deleteOld(); } } } $soMain = new soMain(); $soMain->run();
Скрипт для удаленного сервера get_info.php
<?php $bDebug = false; $aResult = []; $sBasePath = __DIR__; if ($bDebug) { $sPath = $sBasePath.'/test/'; } else { $sPath = $sBasePath.'/archive/'; } $results = glob($sPath.'*.tar.gz'); foreach ($results as $result) { $result = str_replace($sPath, '', $result); if ($bDebug) { $aResult[$result] = 'MD5'; } else { $aResult[$result] = md5_file($sPath.$result); } } if ($bDebug) { print_r($aResult); } //print_r($aResult); file_put_contents($sBasePath.'/info.json', json_encode($aResult));[