Скрипт PowerShell для системного администратора для регулярной загрузки резервных копий с сервера на компьютер

Недавно один из моих друзей столкнулся с проблемой: провайдер VPS внезапно закрылся без предупреждения. К сожалению, ради экономии она выбрала нишевого провайдера, который даже не предоставлял возможность скачивания данных. Хуже того, у неё не было привычки регулярно скачивать резервные копии. За одну ночь её сайт исчез вместе со всеми данными на нём.

Шокированная этим, я написала скрипт на PowerShell, который автоматически скачивает резервные копии с вашего сервера на локальный компьютер на регулярной основе, без необходимости покупать дополнительные услуги объектного хранилища или тратить деньги на что-либо ещё. Всё, что вам нужно, — это компьютер под управлением Windows, который вы регулярно используете (и на котором достаточно места на жёстком диске) и который подключён к Интернету.

Скрипт автоматически удаляет резервные копии старше 5 дней. Вы можете настроить интервал автоматического резервного копирования и срок хранения самых старых резервных копий на локальном компьютере в соответствии со своими потребностями.


$ssh_port = 22
$ssh_address = "username@your.site"


Write-Output "Запуск задачи по скачиванию резервных копий Discourse..."
Write-Output '------------------------'
Write-Output "Получение последнего файла резервной копии..."
Write-Output ''

while ($true) {

    $filename = ''

    while ($true) {
        try {
            Write-Output "> ssh -p $ssh_port $ssh_address 'cd /var/discourse/shared/standalone/backups/default/ && ls -t | head -n 1'"
            Write-Output ''
            $filename = ssh -p $ssh_port "$ssh_address" 'cd /var/discourse/shared/standalone/backups/default/ && ls -t | head -n 1'
            break
        }
        catch {
            $filename = ''
    
            Write-Output "Не удалось получить... Вот лог:"
            Write-Output '-------------'
            Write-Output $_
            
            $answer = Read-Host "Хотите попробовать снова? (y/N)"
            if ($answer -ne 'y') {
                break
            }
            Write-Output ''
        }
    }
    
    
    if ([String]::IsNullOrEmpty($filename)) {
        Write-Output "Ошибка: Не удалось получить имя файла $filename"
        Write-Output ''
        $answer = Read-Host 'Повторить? (y/N)'
  
        if ($answer -eq 'y') {
  
        }
        else {
      
            exit 1
        }
  
    }
    else {

        Write-Output "Последняя резервная копия: $filename"
        Write-Output ''
        
        $need_download = $true
        if (Test-Path ".\backups\$filename") {
            $answer = Read-Host ".\backups\$filename уже существует. Хотите скачать его снова? (y/N)"
            Write-Output ''
            if ($answer -ne 'y') {
                $need_download = $false
            }
        }
        if ($need_download) {
            Write-Output "Начинаем скачивание..."
            Write-Output ''
            
            while ($true) {
                try {
                    Write-Output "scp -p $ssh_port ${ssh_address}:/var/discourse/shared/standalone/backups/default/$filename .\backups\"
                    Write-Output ''

                    scp -p $ssh_port "${ssh_address}:/var/discourse/shared/standalone/backups/default/$filename" .\backups\
                    
                    Write-Output "Скачивание завершено"
                    Write-Output ''
                    
                    break
                }
                catch {

                    Write-Output "Скачивание не удалось >_<... Вот лог:"
                    Write-Output ''

                    Write-Output $_
                    
                    $answer = Read-Host "Попробовать скачать снова? (y/N)"
                    Write-Output ''
                    if ($answer -ne 'y') {
                        break
                    }
                }
            }

        }
  
        Write-Output "Начинаем очистку старых файлов резервных копий..."
        Write-Output ''

        $count = 0
        $backupfiles = Get-ChildItem -Path .\backups\
  
        foreach ($file in $backupfiles) {
            if ($file.CreationTime -le (Get-Date).AddDays(-5)) {
                try {
                    Write-Output "Удаление старого файла резервной копии $file ..."
                    Write-Output ''
                    $file.Delete()
                    $count = $count + 1
                } catch {
                    Write-Output "Произошла ошибка при удалении старого файла резервной копии $file >_<"
                    Write-Output '-------------------'
                    Write-Output $_
                    Write-Output '-------------------'
                }
            }
        }

        if ($count -ge 0) {
            Write-Output "Удалено старых файлов резервных копий: $count"
            Write-Output ''
        }
        else {
            Write-Output 'Нет старых файлов резервных копий для удаления'
            Write-Output ''
        }

        Pause
  
        exit 0
  
    }
  
  
}


Сохраните приведённый выше скрипт как scriptname.ps1 в каталоге, куда вы хотите скачивать резервные копии. Попробуйте запустить его через “Запуск с помощью PowerShell”. Если всё прошло успешно, переходите к следующему шагу.

Для планирования задачи

  1. Найдите “Планировщик заданий”.
  2. Дважды щёлкните “Создать задачу”. Откроется мастер создания запланированной задачи.
  3. Нажмите “Далее”, затем “Обзор”. Откроется диалоговое окно “Выбор программы для планирования”.
  4. Перейдите к созданному вами скрипту, выберите его и нажмите “Открыть”. Вы вернётесь в мастер создания запланированной задачи.
  5. Укажите имя задачи или оставьте имя по умолчанию (это имя файла), укажите, как часто нужно запускать скрипт, затем нажмите “Далее”.
  6. Укажите время и дату начала (если вы выбрали Ежедневно, Еженедельно, Ежемесячно и т. д.) и периодичность, затем нажмите “Далее”. Этот параметр должен соответствовать циклу автоматического резервного копирования вашего Discourse.
  7. Введите имя пользователя и пароль учётной записи, от имени которой будет выполняться скрипт, затем нажмите “Далее”.
  8. Если вы хотите настроить дополнительные свойства, установите соответствующий флажок, затем нажмите “Готово”.
15 лайков

Хорошее напоминание о том, насколько важно иметь удалённые резервные копии. Несколько месяцев назад с аналогичной проблемой столкнулся ещё один пользователь Meta.

Один из моих экземпляров использует встроенную функцию резервного копирования в S3, остальные используют Rclone и задачу CRON для отправки резервных копий в Google Drive. Есть руководство по этому вопросу: Use rclone to sync backups to Dropbox or Google Drive

Спасибо, что поделились своим скриптом, Линка :+1:

3 лайка

Действительно, очень хорошее напоминание. Я предлагал (здесь), чтобы функция обновления проверяла наличие резервных копий и выдавала предупреждение, если время последнего доступа к файлам резервных копий было слишком давно. Мы обновляемся раз в два-три месяца, поэтому такая проверка не будет выполняться слишком часто, а момент обновления — отличное время, чтобы убедиться в наличии безопасной резервной копии. Любое действие по копированию файла резервной копии в другое место должно обновлять метку времени последнего доступа.

3 лайка

Хорошая идея, спасибо за скрипт. Делюсь версией rsync :slight_smile:.

В примере ниже SQL и вложения синхронизируются отдельно. Нет необходимости включать вложения в файл резервной копии. Также не нужно удалять старые резервные копии.

Я использую Chocolatey для установки cwrsync. После установки нужно лишь добавить несколько строк в конец cmd-файла и создать расписание:

C:\ProgramData\chocolatey\lib\rsync\tools\cwrsync.cmd

Например:

rsync -rvm --delete --ignore-errors --ignore-existing --size-only --chmod=ugo=rwX -e "ssh -i /cygdrive/c/Users/user1/.ssh/id_rsa" login@host:/var/discourse/shared/standalone/backups/default/ /cygdrive/d/backup/forum/db/

rsync -rvm --delete --ignore-errors --ignore-existing --size-only --chmod=ugo=rwX -e "ssh -i /cygdrive/c/Users/user1/.ssh/id_rsa" login@host:/var/discourse/shared/standalone/uploads/ /cygdrive/d/backup/forum/uploads/

Примечание 1: Используйте /cygdrive/c/ вместо C:, обратитесь к cmd-файлу для справки по синтаксису.

Примечание 2: Вы можете изменить команду для использования пароля вместо SSH-ключа (формат PEM).

Примечание 3: Если вы не добавите слэш после папки dump/, rsync скопирует саму папку; если добавите — скопирует только содержимое папки.

Примечание 4: Если Chocolatey обновит cwrsync, он создаст резервную копию предыдущего cmd-файла в той же папке. Вам нужно вручную перенести ваши команды в новый cmd-файл, чтобы продолжить резервное копирование.

Важно: Установите то же правило в Планировщике, как написал автор оригинального поста (OP). Строка для Планировщика выглядит так:

C:\ProgramData\chocolatey\lib\rsync\tools\cwrsync.cmd >> d:\backup\cwrsync.txt 2>&1

Это добавит вывод в файл cwrsync.txt. Вот и всё.

3 лайка

У меня есть привычка настраивать rclone для автоматического копирования резервных копий на удалённый сервер (самостоятельно размещённый NAS дома) из-за паранойи по поводу именно таких внезапных ситуаций. Этот пост служит отличным напоминанием о том, что не стоит класть все яйца в одну корзину.

5 лайков

Пока вы занимаетесь этим, вам также стоит скопировать всё содержимое из /var/discourse/containers.

4 лайка

Отличная идея! Запустим обновление

3 лайка

Где искать «Запланированные задачи»? (Новичок, прошу помощи, спасибо)

“Планировщик заданий” — это планировщик задач. В Windows 10 и выше достаточно нажать клавишу Win и ввести название для поиска.

2 лайка

Привет. Почему у меня возникает эта ошибка?

Для всех, кто это читает, вот более точные и актуальные инструкции, так как я сам немного запутался в указаниях из оригинального поста:

  1. Создайте или используйте папку, где будет храниться этот скрипт. Затем создайте внутри этой папки папку с именем backup.

  2. Скопируйте и вставьте скрипт в Блокнот. ПРИМЕЧАНИЕ: Если вы используете VPS, для строки $ssh_address = "username@your.site" укажите имя пользователя вашего VPS и его публичный IP-адрес. Сохраните файл под именем anyname.ps1 и не забудьте выбрать в выпадающем списке «Все файлы», как показано ниже.
    hhhh

  3. Нажмите клавишу Windows ⊞, введите «Задачи» и откройте «Планировщик заданий».

  4. Справа нажмите «Создать простую задачу».

  5. Укажите любое имя и описание.

  6. Выберите периодичность: ежедневно, еженедельно, ежегодно и т. д. Рекомендую «Ежедневно».

  7. Установите дату и время выполнения. Оставьте значение «Повторять» равным 1. Обязательно зайдите в настройки администратора вашего сайта Discourse > Резервные копии. Установите галочку напротив пункта automatic backups enabled (автоматические резервные копии включены).
    Затем установите backup frequency (частота резервного копирования) на нужное вам значение. Рекомендую 1, но ключевой момент здесь — чтобы это значение совпадало с настройками Планировщика заданий. Даты резервного копирования должны совпадать. Пример: если вы настроили Планировщик заданий на ежедневное резервное копирование, установите в настройках администратора параметр backup frequency равным 1.

  8. Оставьте значение по умолчанию «Запуск программы» и нажмите «Далее».

  9. Нажмите «Обзор», найдите сохранённый скрипт и нажмите «Далее».

  10. Нажмите «Готово».
    Если вы хотите протестировать скрипт, щёлкните по нему правой кнопкой мыши и выберите «Запустить с помощью PowerShell».

Примечание: Если вы получите сообщение «Системе не удаётся найти указанный файл», введите пароль так, как показано в PowerShell, и система всё равно найдёт файл.

1 лайк

Когда я сохранил скрипт PS (просто изменил своё имя пользователя и IP-адрес во второй строке) и нажал правой кнопкой мыши > Запустить с помощью PowerShell, ничего не произошло.
Произошло краткое мигание. Если у меня уже был открыт терминал в Windows 11, то этот терминал был выведен на передний план. Ничего больше. Просто мигание.

Меня интересовало: хотя я указал своё имя пользователя и IP-адрес в скрипте, как он будет использовать мой SSH-ключ для входа и загрузки резервной копии?

Спасибо!

Я попробовал оба решения: от автора темы (OP) и от пользователя 45thj5ej, но ни одно из них не сработало. При запуске скрипта в PowerShell он не находит папку «/backups/» и её содержимое. Также я не могу понять, где именно нужно ввести своё имя пользователя и пароль. Я запускаю Discourse на VPS.