系统管理员定期从服务器下载备份到计算机的PowerShell脚本

最近,我的一个朋友遇到了 VPS 提供商突然倒闭而没有通知的问题。不幸的是,为了便宜,她选择了一个小众提供商——一个甚至不提供任何数据下载的提供商。更糟糕的是,她甚至没有定期下载备份的习惯。一夜之间,她的网站消失了,以及上面的所有数据。

对此感到震惊,我编写了一个 Powershell 脚本,它可以定期自动将备份从您的服务器下载到您的本地计算机,而无需购买任何额外的对象存储服务或花费一分钱。您只需要一台您经常使用(并且有大量硬盘空间)并且已连接到 Internet 的 Windows 计算机。

该脚本将自动清理 5 天前的备份。您可以根据需要设置自动备份间隔和本地保存的最旧备份时间。

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

Write-Output "Starting Discourse backup download task..."
Write-Output '------------------------'
Write-Output "Fetching the latest backup file..."
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 "Failed to fetch... Here is the log:"
            Write-Output '-------------'
            Write-Output $_

            $answer = Read-Host "Do you want to re-fetch? (y/N)"
            if ($answer -ne 'y') {
                break
            }
            Write-Output ''
        }
    }

    if ([String]::IsNullOrEmpty($filename)) {
        Write-Output "Error: Failed to fetch file name $filename"
        Write-Output ''
        $answer = Read-Host 'Retry?(y/N)'

        if ($answer -eq 'y') {

        }
        else {
            exit 1
        }
    }
    else {
        Write-Output "Latest backup: $filename"
        Write-Output ''

        $need_download = $true
        if (Test-Path ".\backups\$filename") {
            $answer = Read-Host ".\backups\$filename already exists. Do you want to download it again?(y/N)"
            Write-Output ''
            if ($answer -ne 'y') {
                $need_download = $false
            }
        }
        if ($need_download) {
            Write-Output "Start downloading..."
            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 "Download completed"
                    Write-Output ''

                    break
                }
                catch {
                    Write-Output "Download failed >_<... The following is the log:"
                    Write-Output ''

                    Write-Output $_

                    $answer = Read-Host "Download again? (y/N)"
                    Write-Output ''
                    if ($answer -ne 'y') {
                        break
                    }
                }
            }
        }

        Write-Output "Start trying to clean old backup files..."
        Write-Output ''

        $count = 0
        $backupfiles = Get-ChildItem -Path .\\backups\

        foreach ($file in $backupfiles) {
            if ($file.CreationTime -le (Get-Date).AddDays(-5)) {
                try {
                    Write-Output "Delete old backup file $file ..."
                    Write-Output ''
                    $file.Delete()
                    $count = $count + 1
                } catch {
                    Write-Output "An error occurred while deleting old backup file $file >_<"
                    Write-Output '-------------------'
                    Write-Output $_
                    Write-Output '-------------------'
                }
            }
        }

        if ($count -ge 0) {
            Write-Output "Cleaned $count old backup files"
            Write-Output ''
        }
        else {
            Write-Output 'No old backup files need to clean up'
            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

感谢分享你的脚本 Linca :+1:

3 个赞

确实是一个很好的提醒。我曾建议(在此)更新功能可以检查并警告备份文件的最后访问时间是否太久远。我们每两三个月更新一次,所以这个检查不会太频繁,而更新的时候是确保有安全备份的好时机。任何复制备份文件的操作都应该更新最后访问时间戳。

3 个赞

说得有理,感谢提供脚本。让我分享一下 rsync 版本 :slight_smile:

在下面的示例中,sql 和 attachments 是分开同步的。没有必要将 attachments 包含在备份文件中。也没有必要删除旧的备份。

我使用 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 个赞

Where to Search “Scheduled Tasks” ? (新手求教,谢谢)

“Scheduled Tasks” 就是任务计划程序,win10以上直接按win键输入就能搜索

image

2 个赞

你好。为什么会出现这个错误?

对于阅读此文的各位,这里提供一份更佳/最新的说明,因为我在遵循 OP 的说明时遇到了一些困难:

  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运行,什么也没发生。
闪了一下。如果我在win11中已经打开了一个终端,那么那个终端会被带到前面。仅此而已。只是闪了一下。

我想知道,尽管我将用户名和ip地址提供给了脚本,但它将如何使用我的ssh密钥登录并拉取备份?

谢谢!

我尝试了 OP 和 45thj5ej 的解决方案,但对我来说都不起作用。使用 Powershell 运行脚本时,它找不到“/backups/”及其子项。我也找不到 OP 所说的输入用户名和密码的地方。我在 VPS 上运行 Discourse。