使用 backupninja 为 Debian 定制备份计划
backupninja是Debian系统(以及基于Debian的发行版)中一个强大的、高度可配置的备份软件。在前一篇文章中,我们探讨了如何安装backupninja以及如何设置两个备份操作并执行。然而,那些只是冰山一角。这一次,我们要讨论如何定制 Handler 和 Helper ,使用这些功能定制策略以完成任何备份需要。
回顾 backupninja
backupninja的一个独特的地方是它可以完全抛弃/etc/backup.d中的纯文本配置文件和操作文件,软件自己会搞定。另外,我们可以编写自定义脚本(又叫 “handler”)放在/usr/share/backupninja 目录下来完成不同类型的备份操作。此外,可以通过ninjahelper的基于ncurses的交互式菜单(又叫“helper”)来指导我们创建一些配置文件,使得人工错误降到最低。
创建定制的Handler与Helper
这一节的目标是创建一个脚本,将home目录以gzip或bzip2压缩包的形式备份起来,不包括音乐与视频文件。我们将这个文件命名为home,将它放在/usr/backup/ninja目录下。
尽管你可以使用默认的tar handler(参考 /usr/share/backupninja/tar 与 /usr/share/backupninja/tar.helper)来达到这个效果,但是我们使用这种方法来展示如何创建实用的 handler 脚本与基于 ncurses 的 helper。你可以根据你的需求来决定如何运用这里的方法。
由于 handlers 来源于主脚本,所以无需以#!/bin/bash开始的释伴行(shebang line)。
我们编写的 handler (/usr/share/backupninja/home)如下所示。已经详细注释了。getconf 函数用来读取备份操作的配置文件。如果你指定了一个变量的值,那么它会覆盖配置文件中对应变量的值:
<span class="com">#/home 目录 handler 脚本</span>
<span class="com"># 每个备份文件会通过 FQDN 来鉴别主机</span>
<span class="pln">getconf backupname</span>
<span class="com"># 备份文件的保存目录</span>
<span class="pln">getconf backupdir</span>
<span class="com"># 默认压缩</span>
<span class="pln">getconf compress</span>
<span class="com"># 包含 /home 目录</span>
<span class="pln">getconf includes</span>
<span class="com">#不包含 *.mp3 与 *.mp4 文件</span>
<span class="pln">getconf excludes</span>
<span class="com"># 要打包备份文件的默认扩展名</span>
<span class="pln">getconf EXTENSION</span>
<span class="com"># tar 程序的绝对路径</span>
<span class="pln">getconf TAR </span><span class="str">`which tar`</span>
<span class="com"># date 程序的绝对路径</span>
<span class="pln">getconf DATE </span><span class="str">`which date`</span>
<span class="com"># 日期格式</span>
<span class="pln">DATEFORMAT</span><span class="pun">=</span><span class="str">"%Y-%m-%d"</span>
<span class="com"># 如果备份目录不存在,以致命错误退出</span>
<span class="kwd">if</span><span class="pun">[</span><span class="pun">!</span><span class="pun">-</span><span class="pln">d </span><span class="str">"$backupdir"</span><span class="pun">]</span>
<span class="kwd">then</span>
<span class="pln">mkdir </span><span class="pun">-</span><span class="pln">p </span><span class="str">"$backupdir"</span><span class="pun">||</span><span class="pln"> fatal </span><span class="str">"Can not make directory $backupdir"</span>
<span class="kwd">fi</span>
<span class="com"># 如果备份目录不可写,同样以致命错误退出</span>
<span class="kwd">if</span><span class="pun">[</span><span class="pun">!</span><span class="pun">-</span><span class="pln">w </span><span class="str">"$backupdir"</span><span class="pun">]</span>
<span class="kwd">then</span>
<span class="pln">fatal </span><span class="str">"Directory $backupdir is not writable"</span>
<span class="kwd">fi</span>
<span class="com"># 根据压缩格式选择对应的tar选项</span>
<span class="kwd">case</span><span class="pln"> $compress </span><span class="kwd">in</span>
<span class="str">"gzip"</span><span class="pun">)</span>
<span class="pln">compress_option</span><span class="pun">=</span><span class="str">"-z"</span>
<span class="pln">EXTENSION</span><span class="pun">=</span><span class="str">"tar.gz"</span>
<span class="pun">;;</span>
<span class="str">"bzip"</span><span class="pun">)</span>
<span class="pln">compress_option</span><span class="pun">=</span><span class="str">"-j"</span>
<span class="pln">EXTENSION</span><span class="pun">=</span><span class="str">"tar.bz2"</span>
<span class="pun">;;</span>
<span class="str">"none"</span><span class="pun">)</span>
<span class="pln">compress_option</span><span class="pun">=</span><span class="str">""</span>
<span class="pun">;;</span>
<span class="pun">*)</span>
<span class="pln">warning </span><span class="str">"Unknown compress filter ($tar_compress)"</span>
<span class="pln">compress_option</span><span class="pun">=</span><span class="str">""</span>
<span class="pln">EXTENSION</span><span class="pun">=</span><span class="str">"tar.gz"</span>
<span class="pun">;;</span>
<span class="kwd">esac</span>
<span class="com"># 不包含一些文件类型/目录</span>
<span class="pln">exclude_options</span><span class="pun">=</span><span class="str">""</span>
<span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> $excludes</span>
<span class="kwd">do</span>
<span class="pln">exclude_options</span><span class="pun">=</span><span class="str">"$exclude_options --exclude $i"</span>
<span class="kwd">done</span>
<span class="com"># 调试信息,执行备份操作</span>
<span class="pln">debug </span><span class="str">"Running backup: "</span><span class="pln"> $TAR </span><span class="pun">-</span><span class="pln">c </span><span class="pun">-</span><span class="pln">p </span><span class="pun">-</span><span class="pln">v $compress_option $exclude_options \</span>
<span class="pun">-</span><span class="pln">f </span><span class="str">"$backupdir/$backupname-"`$DATE "+$DATEFORMAT"`".$EXTENSION"</span><span class="pln"> \</span>
<span class="pln">$includes</span>
<span class="com"># 将标准输出重定向到以.list为扩展的文件 </span>
<span class="com"># 将标准错误输出重定向到以.err为扩展的文件</span>
<span class="pln">$TAR </span><span class="pun">-</span><span class="pln">c </span><span class="pun">-</span><span class="pln">p </span><span class="pun">-</span><span class="pln">v $compress_option $exclude_options \</span>
<span class="pun">-</span><span class="pln">f </span><span class="str">"$backupdir/$backupname-"`$DATE "+$DATEFORMAT"`".$EXTENSION"</span><span class="pln"> \</span>
<span class="pln">$includes \</span>
<span class="pun">></span><span class="str">"$backupdir/$backupname-"`$DATE "+$DATEFORMAT"`</span><span class="pun">.</span><span class="pln">list \</span>
<span class="lit">2</span><span class="pun">></span><span class="str">"$backupdir/$backupname-"`$DATE "+$DATEFORMAT"`</span><span class="pun">.</span><span class="pln">err</span>
<span class="pun">[</span><span class="pln"> $</span><span class="pun">?</span><span class="pun">-</span><span class="pln">ne </span><span class="lit">0</span><span class="pun">]</span><span class="pun">&&</span><span class="pln"> fatal </span><span class="str">"Tar backup failed"</span>
接下来我们将要创建helper文件(/usr/share/backupninja/home.helper)。这样,hendlers将会以菜单的形式在ninjahelper中显示:
<span class="com"># 备份操作描述,以下划线分割单词</span>
<span class="pln">HELPERS</span><span class="pun">=</span><span class="str">"$HELPERS home:backup_of_home_directories"</span>
<span class="pln">home_wizard</span><span class="pun">()</span><span class="pun">{</span>
<span class="pln">home_title</span><span class="pun">=</span><span class="str">"Home action wizard"</span>
<span class="pln">backupname</span><span class="pun">=</span><span class="str">`hostname --fqdn`</span>
<span class="com"># 指定备份操作的时间</span>
<span class="pln">inputBox </span><span class="str">"$home_title"</span><span class="str">"When to run this action?"</span><span class="str">"everyday at 01"</span>
<span class="pun">[</span><span class="pln"> $</span><span class="pun">?</span><span class="pun">=</span><span class="lit">1</span><span class="pun">]</span><span class="pun">&&</span><span class="kwd">return</span>
<span class="pln">home_when_run</span><span class="pun">=</span><span class="str">"when = $REPLY"</span>
<span class="com"># 指定备份文件名</span>
<span class="pln">inputBox </span><span class="str">"$home_title"</span><span class="str">"\"Name\" of backups"</span><span class="str">"$backupname"</span>
<span class="pun">[</span><span class="pln"> $</span><span class="pun">?</span><span class="pun">=</span><span class="lit">1</span><span class="pun">]</span><span class="pun">&&</span><span class="kwd">return</span>
<span class="pln">home_backupname</span><span class="pun">=</span><span class="str">"backupname = $REPLY"</span>
<span class="pln">backupname</span><span class="pun">=</span><span class="str">"$REPLY"</span>
<span class="com"># 指定保存备份文件的默认路径</span>
<span class="pln">inputBox </span><span class="str">"$home_title"</span><span class="str">"Directory where to store the backups"</span><span class="str">"/var/backups/home"</span>
<span class="pun">[</span><span class="pln"> $</span><span class="pun">?</span><span class="pun">=</span><span class="lit">1</span><span class="pun">]</span><span class="pun">&&</span><span class="kwd">return</span>
<span class="pln">home_backupdir</span><span class="pun">=</span><span class="str">"backupdir = $REPLY"</span>
<span class="com"># 指定复选框的默认值</span>
<span class="pln">radioBox </span><span class="str">"$home_title"</span><span class="str">"Compression"</span><span class="pln"> \</span>
<span class="str">"none"</span><span class="str">"No compression"</span><span class="pln"> off \</span>
<span class="str">"gzip"</span><span class="str">"Compress with gzip"</span><span class="pln"> on \</span>
<span class="str">"bzip"</span><span class="str">"Compress with bzip"</span><span class="pln"> off</span>
<span class="pun">[</span><span class="pln"> $</span><span class="pun">?</span><span class="pun">=</span><span class="lit">1</span><span class="pun">]</span><span class="pun">&&</span><span class="kwd">return</span><span class="pun">;</span>
<span class="pln">result</span><span class="pun">=</span><span class="str">"$REPLY"</span>
<span class="pln">home_compress</span><span class="pun">=</span><span class="str">"compress = $REPLY "</span>
<span class="pln">REPLY</span><span class="pun">=</span>
<span class="kwd">while</span><span class="pun">[</span><span class="pun">-</span><span class="pln">z </span><span class="str">"$REPLY"</span><span class="pun">];</span><span class="kwd">do</span>
<span class="pln">formBegin </span><span class="str">"$home_title: Includes"</span>
<span class="pln">formItem </span><span class="str">"Include:"</span><span class="pun">/</span><span class="pln">home</span><span class="pun">/</span><span class="pln">gacanepa</span>
<span class="pln">formDisplay</span>
<span class="pun">[</span><span class="pln"> $</span><span class="pun">?</span><span class="pun">=</span><span class="lit">0</span><span class="pun">]</span><span class="pun">||</span><span class="kwd">return</span><span class="lit">1</span>
<span class="pln">home_includes</span><span class="pun">=</span><span class="str">"includes = "</span>
<span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> $REPLY</span><span class="pun">;</span><span class="kwd">do</span>
<span class="pun">[</span><span class="pun">-</span><span class="pln">n </span><span class="str">"$i"</span><span class="pun">]</span><span class="pun">&&</span><span class="pln"> home_includes</span><span class="pun">=</span><span class="str">"$home_includes $i"</span>
<span class="kwd">done</span>
<span class="kwd">done</span>
<span class="pln">REPLY</span><span class="pun">=</span>
<span class="kwd">while</span><span class="pun">[</span><span class="pun">-</span><span class="pln">z </span><span class="str">"$REPLY"</span><span class="pun">];</span><span class="kwd">do</span>
<span class="pln">formBegin </span><span class="str">"$home_title: Excludes"</span>
<span class="pln">formItem </span><span class="str">"Exclude:"</span><span class="pun">*.</span><span class="pln">mp3</span>
<span class="pln">formItem </span><span class="str">"Exclude:"</span><span class="pun">*.</span><span class="pln">mp4</span>
<span class="com"># 按需增加多个“Exclude”文本框指定其他不须包含的内容</span>
<span class="pln">formItem </span><span class="str">"Exclude:"</span>
<span class="pln">formItem </span><span class="str">"Exclude:"</span>
<span class="pln">formDisplay</span>
<span class="pun">[</span><span class="pln"> $</span><span class="pun">?</span><span class="pun">=</span><span class="lit">0</span><span class="pun">]</span><span class="pun">||</span><span class="kwd">return</span><span class="lit">1</span>
<span class="pln">home_excludes</span><span class="pun">=</span><span class="str">"excludes = "</span>
<span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> $REPLY</span><span class="pun">;</span><span class="kwd">do</span>
<span class="pun">[</span><span class="pun">-</span><span class="pln">n </span><span class="str">"$i"</span><span class="pun">]</span><span class="pun">&&</span><span class="pln"> home_excludes</span><span class="pun">=</span><span class="str">"$home_excludes $i"</span>
<span class="kwd">done</span>
<span class="kwd">done</span>
<span class="com"># 保存配置</span>
<span class="pln">get_next_filename $configdirectory</span><span class="pun">/</span><span class="lit">10.home</span>
<span class="pln">cat </span><span class="pun">></span><span class="pln"> $next_filename </span><span class="pun"><<</span><span class="pln">EOF</span>
<span class="pln">$home_when_run</span>
<span class="pln">$home_backupname</span>
<span class="pln">$home_backupdir</span>
<span class="pln">$home_compress</span>
<span class="pln">$home_includes</span>
<span class="pln">$home_excludes</span>
<span class="com"># tar 程序的路径,必须为GNU tar</span>
<span class="pln">TAR </span><span class="str">`which tar`</span>
<span class="pln">DATE </span><span class="str">`which date`</span>
<span class="pln">DATEFORMAT </span><span class="str">"%Y-%m-%d"</span>
<span class="pln">EXTENSION tar</span>
<span class="pln">EOF</span>
<span class="com"># 将配置文件的权限改为600</span>
<span class="pln">chmod </span><span class="lit">600</span><span class="pln"> $next_filename</span>
<span class="pun">}</span>
运行 ninjahelper
当创建了名为home的handler脚本以及对应的home.helper后,运行ninjahelper命令创建一个新的备份操作。
#ninjahelper
选择 create a new backup action(创建一个新的备份操作)。
接下来将看到可选的操作类型,这里选择“backup of home directories"(备份home目录):
然后会显示在helper中设置的默认值(这里只显示了3个)。可以编辑文本框中的值。注意,关于“when”变量的语法,参考文档的日程安排章节。
当完成备份操作的创建后,它会显示在ninjahelper的初始化菜单中:
按回车键显示这个备份操作的选项。因为它非常简单,所以我们可以随便对它进行一些实验。
注意,“run this action now"(立即运行)选项会不顾日程表安排的时间而立即进行备份操作:
备份操作会发生一些错误,debug会提供一些有用的信息以帮助你定位错误并纠正。例如,当备份操作有错误并且没有被纠正,那么当它运行时将会打印出如下所示的错误信息。
上面的图片告诉我们,用于完成备份操作的连接没有建立,因为它所需要链接的远程主机似乎宕机了。另外,在helper文件中指定的目标目录不存在。当纠正这些问题后,重新开始备份操作。
需要牢记的事情:
- 当你在/usr/share/backupninja 下新建了一个自定义脚本(如foobar)来处理特殊的备份操作时,那么你还需要编写与之对应的helper(foobar.helper)文件,ninjahelper 将通过它生成名为10.foobar(下一个操作为11,以此类推)的文件,保存在/etc/backup.d目录下,而这个文件才是备份操作的真正的配置文件。
- 可以通过ninjahelper设定好备份操作的执行时间,或按照“when”变量中设置的频率来执行。
总结
在这篇文章中,我们探讨了如何从头创建我们自己的备份操作,以及如何向ninjahelper添加相关的菜单以生成对应的配置文件。通过上一篇与这一篇文章,我希望我已经给出了足够好的理由让你继续研究,或者至少应该尝试一下。