介绍
备份重要数据是管理任何计算机基础设施的重要组成部分。 尽管在执行备份时每个人的需求都不同,但在异地位置维护备份数据是一种很好的做法。
将数据副本发送到异地位置的过程曾经是一项重大的后勤挑战。 但随着 Crashplan 和 Dropbox 等基于云的存储服务的出现,以及 DigitalOcean Spaces 等对象存储解决方案的开发,它现在已成为一项简单得多的任务。 尽管如此,记住备份文件并花时间上传它们仍然是某些人的障碍。
这就是为什么人们选择使用各种工具对其重要数据进行例行自动备份的原因。 在本教程中,我们将围绕 s3cmd
命令行工具构建一个脚本,可用于将数据快速上传到 DigitalOcean Spaces。 然后,我们将使用 crontab
定期调用备份脚本并将文件上传到我们的空间。
先决条件
对于本教程,您将需要:
- 一个带有非 root sudo 用户的 Ubuntu 20.04 x64 Droplet。 这可以按照我们的 Ubuntu 20.04 初始服务器设置指南进行配置。
- DigitalOcean Space 和 API 访问密钥。
- Droplet 上安装的 s3cmd 命令行工具。
熟悉 shell 脚本和 cron
作业调度程序也会有所帮助。 有关一些指导和其他上下文,请考虑阅读“Shell 脚本简介”和“如何在 VPS 上使用 Cron 和 Anacron 安排例行任务”。
有了我们的先决条件,我们准备开始我们的备份自动化过程。
构建我们的备份脚本
有许多工具可用于定期自动将备份文件上传到对象存储服务。 但是,这些可能难以配置,并且可能无法提供太多灵活性。 使用 shell 脚本可以是一种更优雅、更直接的方法来自动化对象存储备份。
在本教程中,我们将编写一个基本的 bash 脚本,它将使用 tar
创建文件或目录的备份。 然后,该脚本将使用 s3cmd
命令行实用程序将该备份上传到 Spaces。
要开始,请登录您的 Droplet 并导航到您的主文件夹:
cd ~
进入主文件夹后,我们将使用 nano
创建一个空文件,我们可以在其中写入脚本:
nano bkupscript.sh
我们现在准备开始在文本编辑器中编写我们的备份脚本。 在我们构建脚本时,我们将按顺序逐节解释它的每个部分。
启动我们的脚本
此时,bkupscript.sh
只是一个空的文本文件。 为了让我们的计算机将我们的可执行文件作为命令调用,我们需要以 hashbang 开始我们的脚本。 hashbang 是一个解释器指令,它允许脚本或数据文件作为命令运行。
在我们的例子中,hashbang 看起来像这样:
bkupscript.sh
#!/bin/bash
通过将它包含在脚本的顶部,我们告诉 shell 在 bash 中运行文件的命令。
声明变量
接下来,我们将告诉我们的脚本它需要知道的变量才能正确运行。 我们可以将这些直接添加到文本文件顶部的 hashbang 下方:
bkupscript.sh
... DATETIME=`date +%y%m%d-%H_%M_%S` SRC=$1 DST=$2 GIVENNAME=$3
让我们来看看我们将这些变量中的每一个分配给什么:
DATETIME
:此变量包含一个时间戳,以附加到生成的文件名,因此备份到我们空间的每个文件都有一个唯一的名称。 此时间戳是通过调用date
命令并格式化输出以显示年份的最后两位数字 (%y
)、月份的两位数字 (%m
) 创建的、日期的两位数 (%d
)、小时 (%H
)、分钟 (%M
) 和秒 (%S
)。SRC
:这是我们要备份的文件或文件夹的 source 路径。$1
表示我们正在从传递给脚本的第一个参数中获取此值。DST
:这个变量代表文件的destination。 在我们的例子中,这是我们上传备份的空间的名称。 该名称将来自传递给脚本的第二个参数,如$2
所示。GIVENNAME
:此变量承载用户为目标文件选择的名称。 生成的文件名将以GIVENNAME
开头,并将DATETIME
连接到它上面。 此名称来自传递给脚本的第三个参数 ($3
)。
提供一些帮助
在编写脚本时,添加一些提示或一般建议会很有帮助,如果用户尝试使用它失败,它们可以帮助用户进行故障排除。
对于我们的备份脚本,我们将在变量下方添加一个名为 showhelp()
的函数。 这将打印一系列消息,以帮助用户在脚本失败时进行故障排除。 在 bash 中添加函数时,我们的语法将如下所示:
bkupscript.sh
... showhelp(){ }
此功能将通过在屏幕上回显一系列使用说明来提供帮助消息。 每条指令都应显示为用双引号括起来的字符串。 您会在下面的示例中注意到,一些字符串的开头或结尾写有 \t
或 \n
。 这些是 转义字符 ,它们提供了有关字符串应如何出现在脚本输出中的具体说明:
\t
表示一个制表符空间\n
表示换行
随意在大括号之间添加任何对您有帮助的使用细节(只要记住在任何字符串前面加上 echo
)。 出于演示目的,我们将添加以下内容:
bkupscript.sh
echo "\n\n############################################" echo "# bkupscript.sh #" echo "############################################" echo "\nThis script will backup files/folders into a single compressed file and will store it in the current folder." echo "In order to work, this script needs the following three parameters in the listed order: " echo "\t- The full path for the folder or file you want to backup." echo "\t- The name of the Space where you want to store the backup at (not the url, just the name)." echo "\t- The name for the backup file (timestamp will be added to the beginning of the filename)\n" echo "Example: sh bkupscript.sh ./testdir testSpace backupdata\n"<^>
最终的 showhelp
函数应如下所示:
bkupscript.sh
... showhelp( echo "\n\n############################################" echo "# bkupscript.sh #" echo "############################################" echo "\nThis script will backup files/folders into a single compressed file and will store it in the current folder." echo "In order to work, this script needs the following three parameters in the listed order: " echo "\t- The full path for the folder or file you want to backup." echo "\t- The name of the Space where you want to store the backup at (not the url, just the name)." echo "\t- The name for the backup file (timestamp will be added to the beginning of the filename)\n" echo "Example: sh bkupscript.sh ./testdir testSpace backupdata\n" }
有了我们的帮助文本,我们可以继续收集我们想要在我们的空间中备份的文件。
收集文件
在我们的脚本可以将任何内容传输到我们的空间之前,它首先需要收集正确的文件并将它们合并到一个包中供我们上传。 我们可以通过使用 tar
实用程序和条件语句来完成此操作。 因为我们使用 tar
来创建存档文件(有时称为“zip”文件),我们将调用此函数 tarandzip()
。
让我们首先声明函数并添加另一个 echo
命令让用户知道脚本已经开始收集文件:
bkupscript.sh
... tarandzip(){ echo "\n##### Gathering files #####\n" }
在 echo
命令下方,我们可以添加一个 tar
命令,它将文件收集和压缩成单个输出文件。
bkupscript.sh
tarandzip(){ echo "\n##### Gathering files #####\n" tar -czvf $GIVENNAME-$DATETIME.tar.gz $SRC }
你会注意到这个 tar
命令是用几个选项和变量调用的:
c
:此标志告诉tar
压缩输出文件。z
:这指示tar
使用gzip
压缩文件。v
:这表示verbose
选项,它指示tar
在输出中显示更多信息。f
:此标志指示tar
使用下一个指示的文件名保存文件。$GIVENNAME-$DATETIME.tar.gz
:脚本调用我们在开头声明的这些变量以创建新文件名。 它通过组合$GIVENNAME
和$DATETIME
变量并将.tar.gz
扩展名添加到末尾以形成新文件名来实现此目的。$SRC
:此变量表示我们指示tar
备份的 源 文件或文件夹。
这个函数现在应该可以做我们想做的事情了,但是我们可以添加更多的 echo
调用来给用户一些关于脚本如何工作的额外信息。 这可以通过添加几个条件语句来完成,如下所示:
bkupscript.sh
if tar -czvf $GIVENNAME-$DATETIME.tar.gz $SRC; then echo "\n##### Done gathering files #####\n" return 0 else echo "\n##### Failed to gather files #####\n" return 1 fi
当调用if
子句时,会执行tar
命令并等待结果。 如果命令的结果是肯定的(意味着它运行成功),then
和 else
之间的行将被执行。 这些都是:
- 回显脚本已成功完成
tar
进程的消息 - 返回
0
的错误代码,以便调用此函数的代码部分知道一切正常。
该函数的 else
部分只有在 tar
命令在执行时发现错误时才会执行。 在这种情况下,子句的 else
分支将:
- 回显表明
tar
命令失败的消息 - 返回错误码
1
,表示出错了
最后,我们以 fi
结束 if/then/else
子句,这在 bash 语言中意味着 if
子句已经结束。
完成的 tarandzip()
函数将如下所示:
bkupscript.sh
tarandzip(){ echo "\n##### Gathering files #####\n" if tar -czvf $GIVENNAME-$DATETIME.tar.gz $SRC; then echo "\n##### Done gathering files #####\n" return 0 else echo "\n##### Failed to gather files #####\n" return 1 fi }
使用我们的 tarandzip()
函数,我们准备好设置脚本来移动我们的备份。
将文件传输到对象存储
此时,我们可以使用 s3cmd
命令获取备份脚本以将文件传输到我们的空间。 与 tarandzip
一样,我们也可以 echo
几个字符串并利用 if/then/else
语句让用户在脚本运行时快速了解脚本的工作方式。
首先,我们将声明我们的函数。 为简单起见,将其命名为 movetoSpace()
:
bkupscript.sh
... movetoSpace(){ }
现在我们可以使用 s3cmd
和我们之前声明的变量来构建将我们的备份文件推送到我们的空间的命令:
bkupscript.sh
movetoSpace(){ s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST }
以下是此命令的每个部分的含义:
s3cmd
:这会调用s3cmd
,这是一个 命令行工具,用于管理对象存储桶 。put
:这是s3cmd
用来上传数据到bucket的命令。$GIVENNAME-$DATETIME.tar.gz
:这是将上传到我们空间的备份的名称。 它由我们声明的第四个和第一个变量组成,然后是.tar.gz
,由之前的tarandzip()
函数创建。s3://$DST;
:这是我们要上传文件的位置。s3://
是一个类似 URI 的模式,专门用于描述在线对象存储位置,而$DST;
是我们之前声明的第三个变量。
我们现在有一个功能可以将我们的存档文件上传到我们的空间。 但是,它不会通知用户其状态。 让我们通过在命令前回显一个字符串来改变这一点,让用户知道它已经启动,并在函数完成后让我们知道它是否成功。
让我们首先通知用户该过程已经开始:
bkupscript.sh
movetoSpace(){ echo "\n##### MOVING TO SPACE #####\n" s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST }
因为该命令将成功或不成功(意味着它会将文件上传到我们的空间或不会),我们可以通过回显 [X216X 中保存的两个字符串之一来让用户知道它是否有效] 声明,如下所示:
bkupscript.sh
... if s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST; then echo "\n##### Done moving files to s3://"$DST" #####\n" return 0 else echo "\n##### Failed to move files to the Space #####\n" return 1 fi
这个条件语句告诉 bash “如果我们的 s3cmd
命令正常运行,那么让用户知道脚本已完成将文件移动到我们的空间。 否则,让用户知道该过程失败。”
如果 s3cmd
进程成功完成,则该函数在屏幕上打印一条消息(then
语句中的第一个 echo
字符串),并返回一个值 [X191X ],通知调用函数操作已经完成。 如果进程失败,then
子句将打印错误消息(第二个 echo
字符串),并返回 1
以便脚本的其余部分知道发生了错误.
总之,movetoSpace()
函数应该如下所示:
bkupscript.sh
movetoSpace(){ echo "\n##### MOVING TO SPACE #####\n" if s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST; then echo "\n##### Done moving files to s3://"$DST" #####\n" return 0 else echo "\n##### Failed to move files to the Space #####\n" return 1 fi }
通过编写 movetoSpace()
函数,我们可以继续确保脚本设置为通过使用条件语句进行流控制以按预期顺序调用函数。
设置流量控制
虽然我们已经为脚本设置了函数,但我们没有提供脚本完成这些函数的命令。 此时,我们可以引入一个调用函数,它将准确地告诉脚本的其余部分如何以及何时运行我们编写的其他函数。
假设一切都已正确配置,当我们运行脚本时,它应该读取输入命令,将值分配给每个变量,执行 tarandzip()
函数,然后使用 movetoSpace()
函数. 如果脚本在任何这些点之间失败,它应该打印我们的 showhelp()
函数的输出,以帮助用户进行故障排除。 我们可以通过在文件底部添加一系列 if/then/else
语句来排序并捕获错误:
bkupscript.sh
... if [ ! -z "$GIVENNAME" ]; then if tarandzip; then movetoSpace else showhelp fi else showhelp fi
上一节中的第一个 if
语句检查传递的第三个变量是否不为空。 它通过以下方式执行此操作:
[ ]
:方括号表示它们之间是test。 在这种情况下,测试是针对特定变量不为空的。!
:在这种情况下,此符号表示not
。-z
:该选项表示一个空字符串。 因此,结合 !,我们要求 而不是空字符串 。$GIVENNAME
:我们这里表示我们不想为空的字符串是赋给变量$GIVENNAME
的值。 我们之所以选择这种方式,是因为在命令行调用脚本时,这个变量被赋予了第三个参数传递的值。 如果我们向脚本传递的参数少于 3 个,则代码将没有第三个参数来将值分配给 $GIVENNAME,因此它将分配一个空字符串并且此测试将失败。
假设第一个测试成功,它将继续执行下一个 if
语句,依此类推。 如果任何 if
语句返回错误,then
子句将调用 showhelp
函数并在输出中显示帮助文本。 本质上,它的作用是将我们之前编写的所有函数粘合在一起,并为 bash 提供以正确顺序执行它们所需的信息。
我们的脚本现在完成了! 您可以验证您的脚本是否与我们在下一节中构建的完整脚本相似。
完整的脚本
我们创建的完整备份脚本应如下所示:
bkupscript.sh
#!/bin/bash DATETIME=`date +%y%m%d-%H_%M_%S` SRC=$1 DST=$2 GIVENNAME=$3 showhelp(){ echo "\n\n############################################" echo "# bkupscript.sh #" echo "############################################" echo "\nThis script will backup files/folders into a single compressed file and will store it in the current folder." echo "In order to work, this script needs the following three parameters in the listed order: " echo "\t- The full path for the folder or file you want to backup." echo "\t- The name of the Space where you want to store the backup at (not the url, just the name)." echo "\t- The name for the backup file (timestamp will be added to the beginning of the filename)\n" echo "Example: sh bkupscript.sh ./testdir testSpace backupdata\n" } tarandzip(){ echo "\n##### Gathering files #####\n" if tar -czvf $GIVENNAME-$DATETIME.tar.gz $SRC; then echo "\n##### Done gathering files #####\n" return 0 else echo "\n##### Failed to gather files #####\n" return 1 fi } movetoSpace(){ echo "\n##### MOVING TO SPACE #####\n" if s3cmd put $GIVENNAME-$DATETIME.tar.gz s3://$DST; then echo "\n##### Done moving files to s3://"$DST" #####\n" return 0 else echo "\n##### Failed to move files to the Space #####\n" return 1 fi } if [ ! -z "$GIVENNAME" ]; then if tarandzip; then movetoSpace else showhelp fi else showhelp fi
验证脚本后,请务必在退出 nano 之前保存并关闭文件(CTRL-x
、y
,然后是 ENTER
)。
测试脚本
现在我们已经完成了脚本的构建,我们可以继续测试它。 这不仅会告诉我们是否正确编写了脚本,而且还会给我们一个练习使用脚本的机会。
在测试这样的脚本时,使用虚拟文件通常是个好主意。 尽管我们知道它不能破坏或删除数据,但通过使用一些不重要的文件对其进行测试来保证安全是明智的。 我们将首先使用 mkdir
命令创建一个目录:
mkdir backupthis
接下来,我们将使用 touch
在此目录中创建两个空文件:
sudo touch backupthis/file1.txt sudo touch backupthis/file2.txt
我们现在可以通过将 backupthis
目录及其内容上传到我们的空间来测试脚本。 这是我们调用脚本时需要使用的格式:
sh bkupscript.sh ./backupthis name_of_your_space testrun
注意 因为 movetoSpace()
函数会自动将 s3://
附加到目标变量(即您的空间名称),所以这个变量应该只是您的空间名称而不是它的完整网址。 例如,如果您的 Space 的 URL 是“https://example-space-name.nyc3.digitaloceanspaces.com”,您可以这样编写测试命令:
sh bkupscript.sh ./backupthis example-space-name testrun
前面的命令将启动脚本并返回如下输出:
Output ##### Gathering files ##### ./backupthis/ ./backupthis/file1.txt ./backupthis/file2.txt ##### Done gathering files ##### ##### MOVING TO SPACE ##### upload: 'testrun-210622-19_34_01.tar.gz' -> 's3://name_of_your_space /testrun-210622-19_34_01.tar.gz' [1 of 1] 162 of 162 100% in 8s 19.81 B/s done ##### Done moving files to s3://name_of_your_space #####
如果您遇到任何错误,请检查您的脚本以确保它与我们的示例匹配。 此外,请确保您的 s3cmd
安装已正确配置,并且您使用的访问密钥和密钥均正确无误。
使用 Crontab 自动备份
成功测试备份脚本后,我们可以设置一个 cron
作业,该作业将使用该脚本对我们的空间执行定期备份。 出于本教程的目的,我们将其设置为每分钟执行一次备份脚本。
首先,我们需要使脚本可执行:
chmod +x bkupscript.sh
现在脚本可以作为命令执行,我们可以编辑 crontab
文件以每分钟运行一次脚本:
crontab -e
第一次运行 crontab -e
时,它会要求您从列表中选择一个编辑器:
no crontab for root - using an empty one Select an editor. To change later, run 'select-editor'. 1. /bin/ed 2. /bin/nano <---- easiest 3. /usr/bin/vim.basic 4. /usr/bin/vim.tiny Choose 1-4 [2]:
您可以选择默认的 nano,或您选择的其他文本编辑器。
一旦进入 crontab
,我们将在已经存在的值的底部添加以下行:
/tmp/crontab.example/crontab
* * * * * ~/bkupscript.sh ~/backupthis nameofyourspace cronupload
要保存更改,请按 CTRL-x
,然后按 y
,然后按 ENTER
。
大约一分钟后,一个新文件将出现在您空间的仪表板中。
如果您让 cron
作业运行而不进行任何更改,您将每分钟将一个新文件复制到您的空间。 确认 cron
运行成功后,请随时重新配置 crontab
以按所需的时间间隔备份文件。
您现在有一个脚本可以定期压缩您的备份并将其发送到 DigitalOcean Space!
结论
在本教程中,我们介绍了如何使用 bash 脚本、crontab
和 DigitalOcean Spaces 为您的重要文件创建定期计划的异地备份。 尽管本教程中提供的脚本仅用于演示目的,但它可以用作构建生产就绪版本的基础,该版本以后可以与 CI/CD 解决方案集成,如 Jenkins、[X236X ]无人机,或特拉维斯CI。
如果您想了解有关 CI/CD 工具的更多信息,可以阅读以下教程:
- 如何在 Centos7 上设置 Jenkins 以实现持续开发集成。
- 如何在 Ubuntu 16.04 上使用 Drone 设置持续集成管道。