BorgBackup —— 增量备份方案

by wzyboy

wzyboy’s blog / 2017-12-17 22:50

生活在电子时代,人们每天都在产生数据。写过的文字,拍过的照片,收集过的网络内容,都是数据。对于程序员们来说,写过的代码,服务器的数据库、日志等,也都是数据。然而硬盘故障、服务器宕机等灾害却随时都有可能危害数据的安全。所以数据需要备份。正如某程序员的语录:

冗余不做,日子甭过;备份不做,十恶不赦!

Xin LI

本文介绍一款优秀的备份方案:BorgBackup

一、为什么选择 BorgBackup

BorgBackup 的主要优势(总结自官方文档):

  • 高效:BorgBackup 会将文件按数据块去重,只有改动的数据块才会被备份。一个 25 GiB 的虚拟机磁盘文件,只改动了 1 GiB,那就只会新增备份这 1 GiB 的数据;
  • 高速:核心算法使用 C 编译,使用缓存快速跳过未改动过的文件以加快备份速度;
  • 加密:数据默认是 AES-256 加密并且 HMAC-SHA256 校验的;
  • 压缩:支持多种压缩算法,可自动检测数据是否属于可被压缩的类型;
  • 异地备份:原生支持 SSH 备份到异地服务器,也可使用 NFS 等网络存储;
  • 可挂载:可以直接用 FUSE 挂载一个备份存档读取里面的数据;
  • 跨平台:支持 Linux, macOS, BSD, Windows (Cygwin / WSL) 等多种平台;
  • 开源:安全可审计,易于修改。

目前 BorgBackup 是我 Linux 的笔记本及工作电脑、Windows 游戏机、以及所有 Linux 服务器的所采用的备份方案,已稳定运行数月,十分优秀。

二、安装与配置 BorgBackup

安装

BorgBackup 由 Python + C 写成。你可以直接从源代码编译,从 PyPI 安装,也可以从发行版仓库中安装。BorgBackup 的 1.1.x 分支相比 1.0.x 分支增加了很多有用的新功能和性能提升,建议使用 1.1.x。

最方便的安装方式是——不用安装。BorgBackup 官方提供了使用 PyInstaller 制作的单文件程序,包含了 Python 3 解释器及所有依赖,可以直接运行于各平台上,十分适合批量部署于服务器集群之中,免除编译安装各种依赖之痛苦。

基本命令

BorgBackup 的备份由 repo 和 archive 的概念,类似于 Git 的 repo 和 commit 的关系。同一个 BorgBackup repo 里的各 archive 之间互相去重,同一份数据只会被备份一次:

# 初始化一个名为 my-backup-repo 的 repo   $ borg init -e repokey my-backup-repo   Enter new passphrase:   Enter same passphrase again:   Do you want your passphrase to be displayed for verification? [yN]: n      # 创建一个 archive-1 的 archive,内含 Music 目录   $ du -sh ~/Music/   2.2G    /home/wzyboy/Music/   $ borg create my-backup-repo::archive-1 ~/Music   Enter passphrase for key /home/wzyboy/tmp/my-backup-repo:      # 可以看到整个 repo 的大小与 Music 目录一致   $ du -sh my-backup-repo   2.2G    my-backup-repo      # 创建一个 archive-2 的 archive,内含 Music 和 Desktop 目录   $ du -sh ~/Desktop   170M    /home/wzyboy/Desktop   $ borg create my-backup-repo::archive-2 ~/Music ~/Desktop   Enter passphrase for key /home/wzyboy/tmp/my-backup-repo:   $ borg list my-backup-repo   Enter passphrase for key /home/wzyboy/tmp/my-backup-repo:   archive-1                            Sun, 2017-12-17 18:10:58 [af49569db9cc4899ea99e59f54581e5321fd2d800357a21ad1b6f09ec36f6f32]   archive-2                            Sun, 2017-12-17 18:12:27 [734a9776b60b46896b17d62fbc01dc37b256dba7afb14050226f2e9c193f43bf]      # 由于 Music 目录里的数据在 archive-1 里已经有了,所以 archive-2 创建之后整个 repo 的大小只增长了 Desktop 的大小   $ du -sh my-backup-repo/   2.4G    my-backup-repo/      # 从 repo 中移除 archive。只有当所有 archive 都不再包含某数据块的内容时,该数据块才会被真正从 repo 中移除并释放磁盘空间   $ borg delete my-backup-repo::archive-1   Enter passphrase for key /home/wzyboy/tmp/my-backup-repo:   $ borg list my-backup-repo   Enter passphrase for key /home/wzyboy/tmp/my-backup-repo:   archive-2                            Sun, 2017-12-17 18:12:27 [734a9776b60b46896b17d62fbc01dc37b256dba7afb14050226f2e9c193f43bf]   

更多 borg 命令的参数可通过 borg -h 查看。

远端 repo

以上命令中 repo 的地址为 my-backup-repo,这是当前目录下的一个目录,如果你好奇地进去看一眼,会发现里面主要是一堆以数字命名的目录和文件,每个文件大小都差不多。这就是你备份的数据,去重、压缩、加密之后被切碎了放在那里的。

一个本地磁盘上备份目录是没有什么意义的,本地磁盘挂了,数据和备份数据就全部没有了。BorgBackup 原生支持基于 SSH 的远端 repo,其格式和 Git / rsync 有几分相似:username@hostname:/path/to/repo::archive-1。以上命令中的所有本地 repo 都可以直接换成远端 repo 写法。这样的远端 repo 是通过 SSH 访问的,所以安全性和 SSH 是一样的。使用远端 repo 需要远端机器上已经装有 BorgBackup,正如使用 rsync 需要远端机器上已经装有 rsync 一样。

如果不方便在远端机器上安装 BorgBackup,可以使用 NFS / SSHFS / Samba 等各种网络文件系统把远端机器的磁盘映射到本地,然后用本地 repo 的方式访问,达到异地备份的效果。

Borgmatic

BorgBackup 是没有配置文件的,所有配置项都是在运行的时候通过命令行参数或是环境变量来改变。当你在命令行里把 borg 的各种参数把玩熟悉之后,就可以把它们拼成一个自动化脚本丢给 cron 或是 systemd timer 定期执行了。

对于像我一样不想维护一个脆弱的 shell 脚本的人来说,可以使用第三方项目 Borgmatic。它通过 YAML 配置文件声明 BorgBackup 备份设置,然后拼装好 BorgBackup 命令来运行,并且它有完善的错误处理和钩子机制,方便发送备份成功的通知或备份失败的报警。

Ansible Playbook 懒人包

我写了一个 Ansible Playbook,可用于一条龙完成 BorgBackup + borgmatic + systemd timer + authorized_keys 的配置。这个 Ansible Playbook 会做以下事情:

  1. 从 GitHub 下载一个单文件版本的 BorgBackup 放到 /usr/local/bin/ 里;
  2. 安装 Borgmatic 到 /opt/borgmatic/ 里;
  3. /etc/systemd/system/ 里放一个 systemd timer,在每天 00:00-02:00 中随机一个时间点跑备份;
  4. 如果传了 -e borg_setup_pubkey=True,则会额外把备份源机器的 SSH 公钥安装到备份目标机器上。

整个 Playbook 是幂等的,安全无毒副作用。

三、BorgBackup 最佳实践

加密与压缩

BorgBackup 的加密有 repokey 和 keyfile 两种模式。前者是把 key 存储在 repo 目录里,用 passphrase 加密起来,而 passphrase 由你记忆,后者则是把 key 存储在 repo 外面。如果你对备份宿主的掌控力不足,可以考虑使用后者,当然,后者的风险就是这个 keyfile 本身也需要备份,产生了鸡生蛋与蛋生鸡的问题。

BorgBackup 也支持对 repo 进行压缩后存储。如果你的数据大部分都是可压缩的类型,开启一个快速的 LZ4 压缩能节省不少空间;如果你的数据都是照片、视频等不可压缩类型,压缩就没什么必要了。1.1.x 分支的 BorgBackup 提供了 -C auto,lz4 这样的选项,可以自动判断数据是否属于可压缩类型。

备份宿主

BorgBackup 的远端 repo 模式,因为有加密和校验,所以并不需要客户端去信任服务端,所以你不需要自己去准备服务端,完全可以买别人提供的 BorgBackup 宿主服务。著名备份方案提供商 rsync.net (是的,就是 rsync 这个软件的商业公司)就为 BorgBackup 用户提供了折扣价。25 GiB 存储空间,一年的价格仅 9 USD。你得到的是一个可以已经预装了 BorgBackup 的 SSH 账号。目前我自己的各 VPS 就是备份到 rsync.net 的。

rsync.net 的 SSH 账号是个受限账号,只能运行 borg1, rsync 等少量命令(注:rsync.net 的服务器上 borg 是 BorgBackup 0.x,使用时注意设置 --remote-path borg1),因此我的 Ansible Playbook 中对 rsync.net 的备份宿主做了特殊支持,用专门的方法来安装 SSH 公钥。

另一种思路是去购买「存储型 VPS」,这种机器一般是 OpenVZ 架构,CPU 和 RAM 配置都很差,但是会挂载一个巨大的磁盘,价格上比较划算。在这个 GitHub issue 里可以看到大家对这些 VPS 商家的讨论。

如前文所说,BorgBackup 的 repo 目录里是一堆切碎的数据文件,以数字命名。这个数字文件名是单调递增的,不会重复,并且 BorgBackup 在备份过程中,只会创建或删除整个文件,不会对已有的文件进行修改。这样的特性非常适合对象存储。因此除了上面两种方法,也可以考虑将 BorgBackup repo 用 rclone 同步到 Amazon S3, BackBlaze B2 等各种对象存储。

3-2-1 原则

备份和存储提供商 BackBlaze 提出过一个 3-2-1 原则,指:

  • 数据有 3 份拷贝;
  • 2 份存储在本地不同的存储器上;
  • 1 份存储在异地。

在使用 BorgBackup 备份时也可以考虑这样的原则。比如我笔记本电脑上的数据会通过 BorgBackup 备份到 NAS 上,而 NAS 上的 BorgBackup repo 会用 rclone 整体备份到 BackBlaze B2 对象存储。这就满足了 3-2-1 原则。当我的笔记本电脑损坏时,我可以快速从家庭 NAS 中恢复数据到新电脑上,当因为火灾、地震、洪水等原因笔记本电脑和 NAS 一起坏了,我可以从 B2 异地恢复数据。rclone 支持把一个对象存储用 FUSE 挂载,因此从异地恢复时不需要把所有数据下载下来,只需要挂载之后用 BorgBackup 去读取 FUSE 即可按需恢复。

自动留存

BorgBackup 提供了自动留存算法。通过 borg prune 命令,BorgBackup 可以按照特定的规则自动移除旧的 archive。比如对于一个每天备份的 repo 可以设置这样的 prune 规则:

  • 近一周内,留存每天的备份;
  • 近一个月内,每周至少留一份备份;
  • 近半年内,每月至少留一份备份;
  • 近十年内,每年至少留一份备份。

这样既可以达到最大留存时间,又相对节省备份空间。这些设置自然也是可以在 Borgmatic 中配置的。

四、后记

这是本周写的第三篇博客了。一年多没写博客的我,在迁移完博客之后突然就变得高产了。今天早上在观察 Nginx 日志时(想看看有没有漏迁移而坏掉的永久链接),突然有三条连续日志引起了我的注意:

223.89.xx.xx - - [17/Dec/2017:03:31:04 +0000] "GET /post/1092.html HTTP/1.1" 200 19615 "https://www.inoreader.com/" "Amazon CloudFront"   223.89.xx.xx - - [17/Dec/2017:03:31:05 +0000] "GET /static/css/style.css?h=48644d84 HTTP/1.1" 200 467 "https://wzyboy.im/post/1092.html" "Amazon CloudFront"   223.89.xx.xx - - [17/Dec/2017:03:31:06 +0000] "GET /static/css/bootstrap.min.css HTTP/1.1" 200 121200 "https://wzyboy.im/post/1092.html" "Amazon CloudFront"

天哪,一个活的订阅用户,打开了我昨天刚发布新博客文章!明明已经一年多没更新过博客了,居然还有用户在订阅着。更重要的是,居然还有用户在使用 RSS / Atom 阅读器。这让我非常感动,觉得还是应该多写博客,有什么想法要整理成文字,不能闷在大脑里消散掉或是倒进日记里沉底。

那么,欢迎大家订阅本博客的 Atom Feed: https://wzyboy.im/feed.xml

最后,祝大家常做备份,远离数据丢失。

本文地址:https://wzyboy.im/post/1106.html

Shared via Inoreader