自动化远程备份

作者:Michael J. Hammel

Linux 用户群体非常多样化,因为他们可以自由选择各种各样的工具。但是,无论他们选择 Ubuntu、Fedora 还是 Debian,或者 KDE、GNOME 还是 Xfce,他们都有一个共同点:大量的数据。因硬盘故障或简单地覆盖而丢失数据是所有用户在某个时候都必须面对的事情。然而,这些并不是进行备份的唯一原因。稍作计划,备份并不像看起来那么困难。

硬盘价格已经下降到 USB 存储可以轻松取代普通用户的离线磁带存储需求的程度。每晚将您的数据推送到本地或远程的外部 USB 设备,这是一个相当廉价且简单的过程,应该成为每个用户的个人系统管理的一部分。

在本文中,我描述了一个选择要备份的文件的过程,介绍了执行备份所需的工具,并提供了用于自定义和自动化该过程的简单脚本。我在家庭和工作中都使用了这些过程和脚本多年。不需要特殊的管理技能,尽管了解 SSH 会很有用。

为什么我们需要备份

在继续之前,您应该问问自己备份的目的是什么。执行备份有两个原因。第一个是为了在发生灾难性事件时恢复文件的最新副本。这种类型的恢复利用了完整备份,其中备份存档中仅维护每个文件的单个副本。复制到存档的每个文件都会替换存档中的先前版本。

如果您使用根分区来安装您选择的发行版(Fedora、Ubuntu 等)和用户分区来存放用户数据(/home),那么这种备份形式尤其有用。在这种配置下,发行版更新是通过重新安装而不是升级来完成的。安装主要的发行版已经变得相当容易且几乎无人值守。使用单独的根分区进行重新安装,您可以完全清除旧的安装,而不会触及用户数据。所有需要做的就是合并您的管理文件备份——使用像 meld(一个可视化差异工具)这样的工具可以使这个过程更容易。

执行备份的第二个原因是恢复文件的先前版本。这种类型的恢复需要备份存档维护初始完整备份和后续增量更改。恢复特定版本的文件需要知道执行完整备份的时间与所需文件版本日期之间的时间,以便在该时间点重建文件。图 1 以图形方式显示了完整/增量备份概念。

Automating Remote Backups

图 1. 完整备份替换存档内容。增量备份使用基于时间的文件更改扩展存档。

与完整备份相比,增量备份会更快地占用存档上的磁盘空间。大多数家庭用户更关心处理灾难性故障,而不是检索文件的先前版本。因此,家庭用户会更喜欢没有增量更新的完整备份,因此本文重点介绍仅处理完整备份。幸运的是,使用此处描述的工具的高级功能,向提供的脚本添加对增量备份的支持并不困难。

在任何情况下,商业环境通常将备份保存在三个位置:本地和两个远距离分隔的远程站点。这种做法避免了在灾难广泛发生时完全丢失数据的可能性。家庭用户可能不会做到如此程度,但在单独的系统上,即使在您家中,也强烈建议保留备份。

工具入门

在 Linux 系统上执行备份的主要工具是 rsync。此工具专门用于处理在两个系统之间复制大量文件。它最初被设计为 rcp 和 scp 的替代品,后者是 OpenSSH 提供的用于执行安全文件传输的文件复制工具。

作为 scp 的替代品,rsync 能够利用 OpenSSH 提供的功能来提供安全的文件传输。这意味着在使用 rsync 时,可以利用正确安装的 SSH 配置。实际上,默认情况下使用 SSH 传输,使用源文件或目标文件的标准 URI 格式(例如 user@host:/path)。或者,rsync 提供了一个独立的服务器,rsync 客户端可以连接到该服务器进行文件传输。要使用 rsync 服务器,请在 URI 中使用双冒号而不是单冒号。

SSH(安全外壳)是一个客户端/服务器系统,用于使用加密数据跨网络执行操作。这意味着您传输的内容不容易被识别。例如,SSH 用于安全地登录到远程 Linux 系统。它也可以用于打开安全通道,称为隧道,通过该隧道可以运行远程桌面应用程序并在本地系统上显示。

SSH 配置可能相当复杂,但幸运的是,不必如此。为了与 rsync 一起使用,配置本地和远程计算机,以便本地计算机无需密码即可登录到远程计算机。为此,在本地计算机上,切换到 $HOME/.ssh 并生成公钥文件

$ cd $HOME/.ssh
$ ssh-keygen -t dsa

ssh-keygen 将提示您输入各种信息。为了简单起见,按 Enter 键以接受每个提示的默认值。为了更高的安全性,请阅读 ssh-keygen 和 ssh 手册页,以了解这些提示的含义。

ssh-keygen 生成两个文件,id_dsa 和 id_dsa.pub。后一个文件必须复制到远程系统上的 $HOME/.ssh 下,并附加到文件 $HOME/.ssh/authorized_keys。在此代码中,remoteHost 是远程计算机的名称,localHost 是本地计算机的名称

$ scp id_dsa.pub \
      remoteHost:$HOME/.ssh/id_dsa.pub.localHost
$ ssh remoteHost
$ cd $HOME/.ssh
$ cat id_dsa.pub.localHost >> authorized_keys

在本文中,我假设进行了正确的 SSH 配置,无需密码即可执行基于 rsync 的备份。这些自动化备份脚本旨在从 cron 运行,并且需要正确的 SSH 配置。

备份 UI:Grsync

对于喜欢使用桌面工具而不是脚本来设置和执行备份的用户,可以使用 Grsync 工具。这是一个基于 GTK+ 的工具,为 rsync 提供了几乎完整的前端。它可以用于选择单个源和目标,并且很可能可以从 Linux 发行版存储库中获得。

Automating Remote Backups

图 2. Grsync 是一个用于计划备份的桌面工具。虽然通常很有用,但它缺少包含/排除选项和直接 cron 管理。

虽然以前的版本似乎具有集成的 cron 配置,但 Fedora 提供的当前版本没有。此外,Grsync 不允许选择多个源文件或目录,也不允许设置排除列表。rsync 命令行都支持这两者。Grsync 可以创建一个会话文件,可以从 cron 调用该文件,但它不包含有关如何通知用户备份结果的信息。

由于缺乏 cron 集成、缺少包含和排除选项以及没有用户通知集成,Grsync 不是理想的备份解决方案。此处描述的脚本,以及添加用于简化基于 SMTP 的通知的 ssmtp,是一个更好的解决方案。

文件选择

在设置好 SSH 并决定使用脚本备份而不是桌面应用程序之后,现在是考虑备份哪些文件的时候了。应考虑四组文件:系统配置文件、数据库文件、用户主目录和 Web 文件。

系统配置文件包括密码和组文件、hosts、exports 和 resolver 文件、MySQL 和 PHP 配置、SSH 服务器配置等文件。即使不希望在系统重新安装期间直接重用它们,备份各种系统配置文件也很重要。例如,密码和组文件不应逐字复制到 /etc/passwd 和 /etc/group,而应作为参考来重新创建与用户主目录和现有组匹配的用户登录名。可以备份整个 /etc 目录,尽管实际上,在发行版重新安装后,只需要重新安装或合并其中的几个文件。

一些从源代码构建的应用程序,例如 ssmtp(将在备份脚本中用于通知),可能会安装到 /usr/local 或 /opt。也可以备份这些目录,或者可以在发行版升级后重新构建应用程序。

MySQL 数据库文件可以逐字备份,但将数据库转储到文本文件,然后在升级后重新加载它们可能会更容易。这种方法应该允许数据库干净地处理版本更改。

用户主目录通常包含所有用户数据。通常,除了 /home/lost+found 目录之外,/home 下的所有文件都应备份。这假设所有用户登录名都保存在 /home 上。查看您的发行版文档以验证用户主目录的位置。

家庭用户可能不会在内部使用 Web 服务器,但没有理由不应该使用。Wiki、博客、媒体存档等易于设置,并为家庭提供各种易于使用的家庭内部通信系统。在 /home 下设置文档根目录(使用 Apache 配置文件)使备份这些文件与任何其他用户文件相同。

在执行备份时,还有一些文件和目录要避免。lost+found 目录应始终排除,同样应该排除 $HOME/.gvfs,这是 GNOME 用户登录后创建的。

脚本编写和通知

所有备份都可以通过单个脚本处理,但由于备份需求经常变化,我发现更容易遵循 UNIX 传统,并创建了一组四个小型脚本来管理不同的备份需求。

第一个脚本用于运行其他脚本,并通过电子邮件发送有关备份过程报告的通知。此脚本由 root 通过 cron 每晚运行

#!/bin/bash

HOST=`hostname`
date=`date`
mailfile="/tmp/$$.bulog"

# Mail Header
echo "To: userid@yourdomain.org"          > $mailfile
echo "From: userid@yourdomain.org"       >> $mailfile
echo "Subject: $HOST: Report for $date"  >> $mailfile
echo " "                                 >> $mailfile
echo "$HOST backup report:"              >> $mailfile
echo "-------------------------------"   >> $mailfile

# Run the backup.
$1 >> $mailfile 2>&1

# Send the report.
cat $mailfile | \
    /usr/local/ssmtp/sbin/ssmtp -t \
    -auuserid@yourdomain.org -apyourpassword \
    -amCRAM-MD5
rm $mailfile

脚本的第一个参数是要运行的备份脚本。增强版本将在尝试运行之前验证命令行选项。

此脚本使用外部程序 (ssmtp) 发送备份报告。如果您有其他从命令行发送电子邮件的工具,则可以使用该工具替换 ssmtp 的使用。或者,您可以完全跳过使用此前端,直接从 cron 和/或命令行运行备份脚本。

ssmtp

ssmtp 是 Sendmail 的替代品,配置和使用起来要简单得多。但是,它不用于检索邮件。它仅用于发送出站电子邮件。它有一个小而简单的配置文件,当用作 Sendmail 的替代品时,命令行程序(如 mail)将使用它来发送电子邮件。

Linux 发行版通常不提供 ssmtp,但可以通过在 Internet 上进行 Google 搜索找到源代码。按照软件包说明在 /usr/local 下构建和安装。然后,通过设置从 /usr/sbin/sendmail 到 ssmtp 安装位置的符号链接,将 sendmail 替换为 ssmtp。

$ mv /usr/sbin/sendmail /usr/sbin/sendmail.orig
$ ln -s /usr/local/sbin/ssmtp /usr/sbin/sendmail

如果您的发行版支持 alternatives 工具,您可能更喜欢使用它而不是符号链接,以让系统使用 ssmtp 而不是 Sendmail。请注意,作为奖励,当作者用 ssmtp 替换 Sendmail 时,LogWatch 突然开始通过电子邮件发送夜间报告,让我看到了以前从未见过的系统活动视图,并且许多 Linux 用户可能也从未见过。

系统配置文件备份

系统配置文件备份由 Perl 脚本处理,该脚本详细列出了要复制到 /home 分区上的位置的文件。该脚本由 root 通过 cron 每晚运行,以将配置文件复制到用户数据空间(/home 下)中的目录

#!/usr/bin/perl
$filelist = <<EOF;
/etc/passwd
/etc/group
...  # other config files to backup
EOF

@configfiles = split('\n', $filelist);
for (@configfiles)
{
    if (-e $_) { $files = join(" ", $files, $_); }
    elsif (index($_, "*") >= 0) {
        $files = join(" ", $files, $_);
    }
}
print "Creating archive...\n";
`tar Pczf $ARGV[0]/systemfiles.tar.gz $files`;

这种暴力方法包含要备份的文件列表,将它们连接到单个 tar 命令中,并在本地系统上构建这些文件的 tar 存档。通过修改文件和目录列表,可以轻松维护脚本。由于配置文件在本地复制到用户数据空间,并且用户数据空间是单独备份的,因此这里不需要 rsync 命令。相反,系统配置文件 tar 存档与用户数据一起保存,并在执行恢复或系统升级时易于引用。备份脚本充当完整备份,每次执行都会替换 tar 存档,除非将不同的目标指定为命令行参数。

这个脚本在 Perl 方面的优点不足,但它弥补了维护的简单性。请注意,此脚本的“零售”版本应包含额外的错误检查,以检查指定保存存档文件位置所需的命令行参数。

数据库备份

与系统配置文件类似,数据库也备份到用户数据目录,以便包含在用户数据备份中。数据库在日常使用中稍微重要一些,因此此脚本对数据库文件转储使用七天轮换周期。这允许从最多一周前的备份中恢复,而不会过度使用磁盘空间进行备份。但是,此方法不是增量的。它是一组七个每个数据库的完整备份。

与系统配置文件备份脚本类似,此脚本列出了要备份的项目。mysqldump 命令假定 root 用户访问数据库时没有密码。这是非常不安全的,但对于防火墙后的用户来说,这可能是处理数据库管理的最简单方法

#!/usr/bin/perl -w
use File::Path qw(make_path remove_tree);
my $BUDIR1="/home/httpd/db";
my ($sec,$min,$hour,$mday,$mon,$year,
    $wday,$yday,$isdst) = localtime time;
$year += 1900;
$mon += 1;
if ($mon < 10 )  { $mon  = "0".$mon; }
if ($mday < 10 ) { $mday = "0".$mday; }
$TODAY = $wday;

@dbname = (
    "mysql",
    "wordpress",
);

make_path ("$BUDIR1/$year");
foreach $db (@dbname) {
    $cmd = "mysqldump -B -u root $db " .
           "-r $BUDIR1/$year/$TODAY-$db.sql";
    system("$cmd");
}

print ("Database Backups for " .
       $year . "/" . $mon . "/" .
       $mday . "\n");
print ("-------------------------------\n");
open(PD, "ls -l $BUDIR1/$year/$TODAY-*.sql |" );
@lines = <PD>;
close(PD);
$output = join("\n", @lines);
print ($output);

与配置文件备份脚本不同,此脚本会打印出已创建的文件列表。这在通过电子邮件发送的报告中提供了快速、直观的反馈,表明备份产生了有意义的结果。

用户数据备份

系统配置备份脚本和数据库备份脚本首先运行,以生成用户数据空间的备份。完成后,所有数据都准备好使用基于 rsync 的脚本备份到远程系统

#!/bin/bash
function checkRC
{
    rc=$1
    name=$2
    if [ $rc != 0 ]
    then
        echo "== $name failed with rsync rc=$rc =="
    fi
}
LOGIN=root@feynman
BRAHE=$LOGIN:/media/BackupDrive/feynman
if [ "$1" != "" ]
then
    BRAHE=$1
fi

该脚本包含一个 shell 函数,用于测试 rsync 的返回代码,并在失败时打印错误消息。前端脚本将来自此脚本的输出重定向到一个文件,因此错误消息会显示在通过电子邮件发送的备份报告中。

备份的默认目标在脚本开始时配置。第一个命令行参数可用于覆盖默认值

DIR1="/home/httpd"
DIR2="/home/mjhammel"
EXCL2=--exclude-from=/home/mjhammel/.rsync/local

用户数据备份脚本侧重于目录。与其他备份脚本不同,要备份的项目列表在单独的变量中硬编码。同样,这是一种用于简单性的暴力方法,因为要备份的每个目录可能有一组或多组包含和排除参数。在更通用的脚本版本中,可以使用关联数组代替变量集。

请注意,此配置调用了 /home 下的各个目录,而不是备份整个 /home。从中提取此脚本的脚本用于一台机器,该机器在 /home 下有不需要备份的开发目录。指定 /home 并使用排除文件是执行相同操作的另一种方法

DATE=`date`
echo "== Backing up `uname -n` to $BRAHE."
echo "== Started @ $DATE "
echo "== Directory: $DIR1"
rsync -aq --safe-links $DIR1 $BRAHE
checkRC $? "$DIR1"

第一个目录备份到远程系统。-a 选项告诉 rsync 在存档模式下运行,其中 rsync 将执行以下操作

  • 递归遍历指定的目录树。

  • 将符号链接复制为符号链接,而不是它们指向的文件。

  • 保留所有者、组、权限和修改时间。

  • 保留特殊文件,例如设备文件。

safe-links 选项告诉 rsync 忽略指向当前目录树外部文件的符号链接。这样,从存档恢复将不包括可能指向不再存在的位置的符号链接。-q 选项告诉 rsync 以尽可能少的非错误消息运行

echo "== Directory: $DIR2"
rsync -aq --safe-links $EXCL2 $DIR2 $BRAHE
checkRC $? "$DIR2"
DATE=`date`
echo "Backups complete @ $DATE"

第二个目录树使用排除列表进行备份。此列表是一个文件,用于指定当前目录树中要被 rsync 忽略的文件和目录。此文件中以短划线为前缀的条目将从 rsync 将处理的文件和目录集中排除。三个星号匹配指定目录下的任何内容

- /mjhammel/.gvfs/***
- /mjhammel/Videos/***
- /mjhammel/brahe/***
- /mjhammel/iso/***

此示例显示 Videos 和 iso 目录下的任何文件都不会包含在备份中。备份存在于您的主目录中但也可以从 Internet 检索的文件是对磁盘空间的糟糕利用。

brahe 引用是远程系统上相同用户 ID 的主目录的挂载点。这允许通过简单地更改为远程系统的本地挂载点来访问另一个系统上登录名下的文件。但是,没有理由在本地系统上备份这些远程文件,因为该远程系统有自己的备份脚本配置。

此脚本的完整版本包括基于 SSH 的验证,以验证远程系统是否已挂载所需的外部 USB 驱动器并且可用。这允许脚本在浪费时间尝试运行无论如何都会失败的备份之前识别出远程系统行为异常。

通过 Cron 自动化

这些脚本的运行顺序很重要。系统配置文件备份脚本和数据库备份脚本可以并行运行,但必须在用户数据备份脚本运行之前完成

30 0 * * * /path/to/backup-db.pl
30 1 * * * /path/to/backup-configfiles.sh \
           /path/to/save/dir 2>&1 > /dev/null
30 2 * * * /path/to/backup-frontend.sh \
           /path/to/backup-data.sh

要将参数传递给 backup-data.sh,请将整个命令用双引号括起来

30 2 * * * /path/to/backup-frontend.sh \
          "/path/to/backup-data.sh root@copernicus:/backups"

每天早上,每台运行这些脚本的机器都可以获得备份报告,可以查看以确保备份成功完成。在实践中,最常见的问题与驱动器未挂载或无法正常工作,或者在备份过程之前或期间发生的网络中断有关。

总结

在准备个人备份策略时,重要的是确定备份的目的,建立一组准备文件进行备份并将备份执行到远程系统的流程。同样重要的是,这些流程的自动化提供反馈,以便用户至少可以了解备份失败的原因以及何时可能发生。

此处显示的方法有些简单,当然不是每个用户的理想选择。这些脚本可能并非没有错误,并且还有改进空间。它们仅旨在作为构建个人备份计划的起点。我欢迎您对这些脚本所做的任何改进提供反馈。

Michael J. Hammel 是科罗拉多州科罗拉多斯普林斯市 Colorado Engineering, Inc. (CEI) 的首席软件工程师,拥有超过 20 年的软件开发和管理经验。他为众多在线和印刷杂志撰写了 100 多篇文章,并且是关于 The GIMP(首屈一指的开源图形编辑软件包)的三本书的作者。

加载 Disqus 评论