如何使用 DigitalOcean Spaces 自动备份

简介

备份重要数据是管理任何计算机基础设施的重要组成部分。虽然每个人对备份的需求不同,但在异地维护备份数据不失为一种好的做法。

将数据副本发送到异地的过程曾经是一项重大的后勤挑战。但随着Crashplan和Dropbox等云存储服务的出现,以及DigitalOcean Spaces等对象存储解决方案的发展,这项工作现在变得简单多了。尽管如此,记住备份文件和花时间上传文件仍然是一些人的障碍。

因此,人们选择使用各种工具对重要数据进行例行自动备份。在本教程中,我们将围绕 "s3cmd "命令行工具创建一个脚本,用于将数据快速上传到DigitalOcean空间。然后,我们将使用 "crontab "定期调用备份脚本,并将文件上传到我们的空间。

先决条件

本教程需要

熟悉 shell 脚本和 cron 作业调度程序也会有所帮助。如需一些指导和其他背景资料,可考虑阅读"Shell 脚本简介 "和"如何在 VPS 上使用 Cron 和 Anacron 调度日常任务"。

有了前提条件,我们就可以开始备份自动化流程了。

构建我们的备份脚本

有许多工具可用于定期自动将备份文件上传到对象存储服务。不过,这些工具可能很难配置,灵活性也不高。使用 shell 脚本可以更优雅、更直接地实现对象存储备份自动化。

在本教程中,我们将编写一个基本的 bash 脚本,使用 tar 创建文件或目录的备份。然后,脚本将使用 s3cmd 命令行实用程序把备份上传到 Spaces。

要开始使用,请登录 Droplet 并导航到您的主文件夹:

1cd ~

进入主文件夹后,我们将使用 nano 创建一个空文件,在其中写入我们的脚本:

1nano bkupscript.sh

现在我们可以开始在文本编辑器中编写备份脚本了。在编写脚本的过程中,我们将按顺序逐节讲解脚本的各个部分。

启动我们的脚本

此时,bkupscript.sh 只是一个空文本文件。为了让计算机以命令的形式调用我们的可执行文件,我们需要在脚本开头加上哈希邦(hashbang)。hashbang**是一种解释器指令,允许将脚本或数据文件作为命令运行。

在我们的例子中,哈希班显示如下:

1[label bkupscript.sh]
2#!/bin/bash

通过在脚本顶部加入这个命令,我们就告诉 shell 用 bash 运行文件的命令。

声明变量

接下来,我们要告诉脚本需要知道哪些变量才能正常运行。我们可以将这些变量直接添加到文本文件顶端的哈希邦(hashbang)下面:

1[label bkupscript.sh]
2...
3DATETIME=`date +%y%m%d-%H_%M_%S`
4SRC=$1
5DST=$2
6GIVENNAME=$3

让我们来了解一下这些变量的赋值情况:

  • DATETIME:该变量包含一个时间戳,用于附加到生成的文件名上,这样备份到 Space 的每个文件都有一个唯一的名称。这个时间戳是通过调用 date 命令创建的,并将输出格式化,以显示年的最后两位数 (%y)、月的两位数 (%m)、日的两位数 (%d)、小时 (%H)、分钟 (%M)、秒 (%S)。
  • SRC:这是我们要备份的文件或文件夹的 路径。其中的 $1 表示我们从传递给脚本的第一个参数中获取该值。
  • DST:该变量表示文件的目的地 。在我们的例子中,这是我们要上载备份的空间的名称。该名称来自脚本传递的第二个参数,由 $2 表示。
  • GIVENNAME:该变量包含用户选择的目标文件名。生成的文件名将以 GIVENNAME 开头,并与 DATETIME 连接。该名称来自传递给脚本的第三个参数 ($3)。

提供一些帮助

在编写脚本时,最好添加一些提示或一般性建议,以便在用户尝试使用脚本失败时帮助他们排除故障。

对于备份脚本,我们将在变量下方添加一个名为 "showhelp() "的函数。这将打印一系列信息,帮助用户在脚本失败时排除故障。在 bash 中添加函数时,语法如下

1[label bkupscript.sh]
2...
3showhelp(){
4
5}

该函数将通过在屏幕上回显一系列使用说明来提供帮助信息。每条指令都应以字符串的形式显示,并用双引号括起来。在下面的示例中,你会注意到一些字符串的开头或结尾写有 \t\n。这些字符是_escape 字符_,为字符串在脚本输出中的显示方式提供了具体的说明:

  • \t 表示制表符空格
  • \n 表示换行

请随意在大括号之间添加任何对您有帮助的使用细节(只需记住在任何字符串前加上 echo)。为便于演示,我们将添加以下内容:

 1[label bkupscript.sh]
 2echo "\n\n############################################"
 3echo "# bkupscript.sh                            #"
 4echo "############################################"
 5echo "\nThis script will backup files/folders into a single compressed file and will store it in the current folder."
 6echo "In order to work, this script needs the following three parameters in the listed order: "
 7echo "\t- The full path for the folder or file you want to backup."
 8echo "\t- The name of the Space where you want to store the backup at (not the url, just the name)."
 9echo "\t- The name for the backup file (timestamp will be added to the beginning of the filename)\n"
10echo "Example: sh bkupscript.sh ./testdir testSpace backupdata\n"

最终的 showhelp 函数应该是这样的:

 1[label bkupscript.sh]
 2...
 3showhelp(
 4        echo "\n\n############################################"
 5        echo "# bkupscript.sh                            #"
 6        echo "############################################"
 7        echo "\nThis script will backup files/folders into a single compressed file and will store it in the current folder."
 8        echo "In order to work, this script needs the following three parameters in the listed order: "
 9        echo "\t- The full path for the folder or file you want to backup."
10        echo "\t- The name of the Space where you want to store the backup at (not the url, just the name)."
11        echo "\t- The name for the backup file (timestamp will be added to the beginning of the filename)\n"
12        echo "Example: sh bkupscript.sh ./testdir testSpace backupdata\n"
13}

有了帮助文本,我们就可以继续收集要备份到 Space 中的文件了。

收集文件

在我们的脚本向 Space 传输任何内容之前,它首先需要收集正确的文件,并将它们合并成一个单一的文件包供我们上传。我们可以使用 tar 工具和条件语句来实现这一点。因为我们要使用 tar 创建一个归档文件(有时称为 "zip "文件),所以我们将调用这个函数 tarandzip()

让我们先声明函数,并添加另一条 echo 命令,让用户知道脚本已开始收集文件:

1[label bkupscript.sh]
2...
3tarandzip(){
4    echo "\n##### Gathering files #####\n"
5}

echo 命令下面,我们可以添加一条 tar 命令,它将负责把文件收集并压缩成一个输出文件。

1[label bkupscript.sh]
2tarandzip(){
3    echo "\n##### Gathering files #####\n"
4    tar -czvf $GIVENNAME-$DATETIME.tar.gz $SRC
5}

你会注意到,调用这个 tar 命令时有几个选项和变量:

  • c:这个标志会告诉 tar 压缩输出文件。
  • z:指示 tar 使用 gzip 压缩文件。
  • v:表示 verbose 选项,指示 tar 在输出中显示更多信息。
  • f:该标志指示 tar 保存文件,文件名如下所示。
  • $GIVENNAME-$DATETIME.tar.gz:脚本会调用我们在开头声明的这些变量来创建新文件名。它通过合并 $GIVENNAME$DATETIME 变量并在末尾添加 .tar.gz 扩展名来形成新文件名。
  • $SRC:这个变量代表我们指示 tar 备份的 source 文件或文件夹。

这个函数现在应该可以完成我们想要它做的事情了,但我们可以再添加一些 echo 调用,以便为用户提供一些关于脚本工作情况的额外信息。这可以通过添加一些条件语句来实现,例如

1[label bkupscript.sh]
2    if tar -czvf $GIVENNAME-$DATETIME.tar.gz $SRC; then
3        echo "\n##### Done gathering files #####\n"
4        return 0
5    else
6        echo "\n##### Failed to gather files #####\n"
7        return 1
8    fi

调用 if 子句时,将执行 tar 命令并等待结果。如果命令的结果是肯定的(意味着运行成功),则会执行 thenelse 之间的行。这些内容是

  • 回声提示脚本已成功完成 "tar "进程
  • 返回错误代码 "0",以便调用此函数的代码部分知道一切正常。

只有在执行 tar 命令时发现错误,才会执行该函数的 else 部分。在这种情况下,子句的 else 分支将:

  • 回传一条信息,表明 "tar "命令失败
  • 返回错误代码 "1",表示出错了

最后,我们用 "fi "结束 "if/then/else "子句,在 bash 语言中,"fi "表示 "if "子句结束。

完成后的 tarandzip() 函数将如下所示:

 1[label bkupscript.sh]
 2tarandzip(){
 3    echo "\n##### Gathering files #####\n"
 4    if tar -czvf $GIVENNAME-$DATETIME.tar.gz $SRC; then
 5        echo "\n##### Done gathering files #####\n"
 6        return 0
 7    else
 8        echo "\n##### Failed to gather files #####\n"
 9        return 1
10    fi
11}

有了 tarandzip() 函数,我们就可以设置脚本来移动备份了。

将文件传输到对象存储

此时,我们可以使用 s3cmd 命令让备份脚本将文件传输到 Space。与 tarandzip 命令一样,我们还可以 echo 一些字符串,并使用 if/then/else 语句让用户了解脚本运行时的情况。

首先,我们将声明我们的函数。为了简单起见,将其命名为 movetoSpace()

1[label bkupscript.sh]
2...
3movetoSpace(){
4
5}

现在,我们可以使用 s3cmd 和之前声明的变量来构建命令,将备份文件推送到 Space:

1[label bkupscript.sh]
2movetoSpace(){
3    s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST
4}

以下是该命令各部分的含义:

  • s3cmd:调用 s3cmd,这是一个用于管理对象存储桶的命令行工具
  • put:这是 s3cmd 用来将数据上传到存储桶的命令。
  • $GIVENNAME-$DATETIME.tar.gz:这是将要上传到 Space 的备份的名称。它由我们声明的第四个变量和第一个变量组成,后跟 .tar.gz,由之前的 tarandzip() 函数创建。
  • s3://$DST;`:这是我们要上传文件的位置。s3://"是一个类似 URI 的模式,专门用于描述在线对象存储位置,而"$DST; "则是我们之前声明的第三个变量。

我们现在有一个可以将存档文件上传到空间的功能。但是,它并没有通知用户其状态。让我们改变这种情况,在命令前回显一个字符串,让用户知道命令已经启动,并在函数完成后让我们知道命令是否成功。

首先,让我们通知用户进程已经开始:

1[label bkupscript.sh]
2movetoSpace(){
3    echo "\n##### MOVING TO SPACE #####\n"
4    s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST
5}

由于命令要么成功要么失败(也就是说,要么将文件上传到我们的空间,要么不上传),因此我们可以通过回声来让用户知道命令是否成功,回声包含在 "if/then/else "语句中的两个字符串之一,就像这样:

1[label bkupscript.sh]
2...
3if s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST; then
4    echo "\n##### Done moving files to s3://"$DST" #####\n"
5    return 0
6else
7    echo "\n##### Failed to move files to the Space #####\n"
8    return 1
9fi

这条条件语句告诉 bash:"如果我们的 s3cmd 命令正常运行,那么让用户知道脚本已完成将文件移到我们的空间。否则,让用户知道进程失败了"。

如果 s3cmd 进程成功完成,函数将在屏幕上打印一条信息(then 语句中的第一个 echo 字符串),并返回一个 0 值,通知调用函数操作已完成。如果处理失败,then 子句将打印错误信息(第二个 echo 字符串),并返回1值,以便脚本的其他部分知道发生了错误。

总的来说,"movetoSpace() "函数应该是这样的:

 1[label bkupscript.sh]
 2movetoSpace(){
 3    echo "\n##### MOVING TO SPACE #####\n"
 4    if s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST; then
 5        echo "\n##### Done moving files to s3://"$DST" #####\n"
 6        return 0
 7    else
 8        echo "\n##### Failed to move files to the Space #####\n"
 9        return 1
10    fi
11}

在编写了movetoSpace()函数后,我们就可以通过使用条件语句进行流程控制,确保脚本按预期顺序调用函数。

设置流量控制

虽然我们已经为脚本设置了函数,但还没有为脚本提供完成这些函数的顺序。此时,我们可以引入一个调用函数,告诉脚本的其他部分如何以及何时运行我们编写的其他函数。

假设一切配置正确,当我们运行脚本时,它应该读取输入命令,将其中的值赋值给每个变量,执行 tarandzip() 函数,然后执行 movetoSpace() 函数。如果脚本在上述任何一点之间失败,它应打印我们的 showhelp() 函数的输出,以帮助用户排除故障。我们可以在文件底部添加一系列if/then/else语句来安排顺序和捕捉错误:

 1[label bkupscript.sh]
 2...
 3if [ ! -z "$GIVENNAME" ]; then
 4    if tarandzip; then
 5        movetoSpace
 6    else
 7        showhelp
 8    fi
 9else
10    showhelp
11fi

上一节中的第一条 if 语句检查第三个变量是否为空。其方法如下:

  • [ ]:方括号表示方括号之间是一个_测试_。在这种情况下,测试的是特定变量是否为空。
  • !:在这种情况下,该符号表示 "不是"。
  • -z:该选项表示_空字符串_。因此,结合_!,我们要求_不是空字符串
  • $givenname:我们在这里表示我们不希望为空的字符串是分配给变量 $GIVENNAME 的值。我们选择这种方法的原因是,当从命令行调用脚本时,这个变量会被分配给第三个参数传递的值。如果我们传递给脚本的参数少于 3 个,代码将没有第三个参数来为 $GIVENNAME 赋值,因此它将赋值一个空字符串,这个测试将失败。

假设第一次测试成功,就会进入下一个 if 语句,依此类推。如果任何一条 if 语句返回错误,则 then 子句将调用 showhelp 函数,并在输出中显示帮助文本。从根本上说,这就是把我们之前写的所有函数粘合在一起,并为 bash 提供所需的信息,使其按照正确的顺序执行这些函数。

我们的脚本现在已经完成!您可以在下面的部分验证您的脚本是否与我们创建的完整脚本相似。

完整的脚本

我们创建的备份脚本完成后应该是这样的:

 1[label bkupscript.sh]
 2#!/bin/bash
 3DATETIME=`date +%y%m%d-%H_%M_%S`
 4SRC=$1
 5DST=$2
 6GIVENNAME=$3
 7showhelp(){
 8        echo "\n\n############################################"
 9        echo "# bkupscript.sh                            #"
10        echo "############################################"
11        echo "\nThis script will backup files/folders into a single compressed file and will store it in the current folder."
12        echo "In order to work, this script needs the following three parameters in the listed order: "
13        echo "\t- The full path for the folder or file you want to backup."
14        echo "\t- The name of the Space where you want to store the backup at (not the url, just the name)."
15        echo "\t- The name for the backup file (timestamp will be added to the beginning of the filename)\n"
16        echo "Example: sh bkupscript.sh ./testdir testSpace backupdata\n"
17}
18tarandzip(){
19    echo "\n##### Gathering files #####\n"
20    if tar -czvf $GIVENNAME-$DATETIME.tar.gz $SRC; then
21        echo "\n##### Done gathering files #####\n"
22        return 0
23    else
24        echo "\n##### Failed to gather files #####\n"
25        return 1
26    fi
27}
28movetoSpace(){
29    echo "\n##### MOVING TO SPACE #####\n"
30    if s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST; then
31        echo "\n##### Done moving files to s3://"$DST" #####\n"
32        return 0
33    else
34        echo "\n##### Failed to move files to the Space #####\n"
35        return 1
36    fi
37}
38if [ ! -z "$GIVENNAME" ]; then
39    if tarandzip; then
40        movetoSpace
41    else
42        showhelp
43    fi
44else
45    showhelp
46fi

验证脚本后,请务必保存并关闭文件(CTRL-xy,然后ENTER),然后退出 nano。

测试脚本

现在我们已经完成了脚本的编写,可以开始测试了。这不仅能告诉我们脚本是否编写正确,还能让我们有机会练习使用脚本。

在测试类似脚本时,使用虚拟文件通常是个好主意。尽管我们知道它不能破坏或删除数据,但为了安全起见,还是用一些不重要的文件进行测试。我们首先使用 mkdir 命令创建一个目录:

1mkdir backupthis

接下来,我们将使用 touch 在该目录下创建两个空文件:

1sudo touch backupthis/file1.txt
2sudo touch backupthis/file2.txt

现在,我们可以通过将 backupthis 目录及其内容上传到 Space 来测试脚本。这是我们调用脚本时需要使用的格式:

1sh bkupscript.sh ./backupthis name_of_your_space testrun

<$>[注] 注意 * 因为 movetoSpace() 函数会自动将 s3:// 添加到目标变量(即空间名称)的前缀,所以该变量应该只是空间名称,而不是其完整的 URL。例如,如果您的空间的 URL 是 "https://example-space-name.nyc3.digitaloceanspaces.com",您可以这样编写测试命令:

1sh bkupscript.sh ./backupthis example-space-name testrun

<$>

上一条命令将启动脚本,并返回如下输出:

 1[secondary_label Output]
 2
 3##### Gathering files #####
 4
 5./backupthis/
 6./backupthis/file1.txt
 7./backupthis/file2.txt
 8
 9##### Done gathering files #####
10
11##### MOVING TO SPACE #####
12
13upload: 'testrun-210622-19_34_01.tar.gz' -> 's3://name_of_your_space /testrun-210622-19_34_01.tar.gz'  [1 of 1]
14 162 of 162 100% in 8s 19.81 B/s done
15
16##### Done moving files to s3://name_of_your_space #####

如果遇到任何错误,请检查您的脚本,确保与我们的示例相符。此外,请确保您安装的 s3cmd 已正确配置,并且使用的访问密钥和秘钥都正确无误。

使用 Crontab 自动备份

成功测试备份脚本后,我们可以设置一个 cron 作业,使用该脚本定期备份我们的空间。在本教程中,我们将设置它每分钟执行一次备份脚本。

首先,我们需要使脚本可执行:

1chmod +x bkupscript.sh

既然脚本可以作为命令执行,我们就可以编辑 crontab 文件,让脚本每分钟运行一次:

1crontab -e

首次运行 crontab -e 时,系统会要求您从列表中选择一个编辑器:

1no crontab for root - using an empty one
2Select an editor. To change later, run 'select-editor'.
3  1. /bin/ed
4  2. /bin/nano        <---- easiest
5  3. /usr/bin/vim.basic
6  4. /usr/bin/vim.tiny
7Choose 1-4 [2]:

您可以选择默认的 nano 或其他文本编辑器。

进入 crontab 后,我们将在已有值的底部添加以下一行:

1[label /tmp/crontab.example/crontab]
2* * * * * ~/bkupscript.sh ~/backupthis nameofyourspace cronupload

要保存更改,请按 CTRL-x,然后按 y,再按 ENTER 键。

一分钟左右后,一个新文件就会出现在 Space 的仪表板上。

如果不做任何更改而让 cron 工作一直运行,那么每隔一分钟就会有一个新文件复制到您的 Space。一旦确认 cron 运行成功,请随时重新配置 crontab,以按您所需的时间间隔备份文件。

现在,你有了一个脚本,可以定期压缩备份并将其发送到 DigitalOcean 空间!

结论

在本教程中,我们将介绍如何使用 bash 脚本、"crontab "和 DigitalOcean Spaces 为重要文件创建定期异地备份。虽然本教程中介绍的脚本仅用于演示目的,但它可用作构建生产就绪版本的基础,之后可与 JenkinsDroneTravis CI等 CI/CD 解决方案集成。

如果你想进一步了解 CI/CD 工具,可以阅读以下教程:

Published At
Categories with 技术
comments powered by Disqus