Небольшой скрипт для создания и ротации бэкапов, который забирает файлы с удаленного сервер (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));[