简介
备份重要数据是管理任何计算机基础设施的重要组成部分。虽然每个人对备份的需求不同,但在异地维护备份数据不失为一种好的做法。
将数据副本发送到异地的过程曾经是一项重大的后勤挑战。但随着Crashplan和Dropbox等云存储服务的出现,以及DigitalOcean Spaces等对象存储解决方案的发展,这项工作现在变得简单多了。尽管如此,记住备份文件和花时间上传文件仍然是一些人的障碍。
因此,人们选择使用各种工具对重要数据进行例行自动备份。在本教程中,我们将围绕 "s3cmd "命令行工具创建一个脚本,用于将数据快速上传到DigitalOcean空间。然后,我们将使用 "crontab "定期调用备份脚本,并将文件上传到我们的空间。
先决条件
本教程需要
- 一个带有非 root sudo 用户的 Ubuntu 20.04 x64 Droplet。可按照我们的Ubuntu 20.04初始服务器设置指南进行配置。
- DigitalOcean空间和API访问密钥。
- Droplet上安装的
s3cmd
命令行工具。
熟悉 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
命令并等待结果。如果命令的结果是肯定的(意味着运行成功),则会执行 then
和 else
之间的行。这些内容是
- 回声提示脚本已成功完成 "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-x
,y
,然后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 为重要文件创建定期异地备份。虽然本教程中介绍的脚本仅用于演示目的,但它可用作构建生产就绪版本的基础,之后可与 Jenkins、Drone 或 Travis CI等 CI/CD 解决方案集成。
如果你想进一步了解 CI/CD 工具,可以阅读以下教程: