12
29
2013
30

rsync+btrfs+dm-crypt 备份整个系统

本文来自依云's Blog,转载请注明。

生成目录!

目标:增量式备份整个系统

怎么做到增量呢?rsync + btrfs 快照。其实只用 rsync 也是可以做到增量式的1,但是支持子卷的 btrfs 可以做得更好:

  1. 快速删除旧的备份
  2. 更简单的备份逻辑
  3. 子卷可以设置成只读(这是个很重要的优点哦~)
  4. btrfs 支持压缩。系统里有好多文本文件的,遇到压缩效果不好的文件 btrfs 会自动放弃压缩

为什么要备份整个系统呢?——因为配置一个高度定制化的系统麻烦啊,只备份部分数据的话还可能漏掉需要的文件。另一个优点是可以直接启动到某个备份

备份了整个系统,包括各种公开或者隐私的数据,一堆 cookies 和帐号配置,邮件和聊天记录等。难道就不需要加密一下下吗?于是,在 btrfs 之下再加一层 dm-crypt 加密。

介绍完毕,下边进入正题。

准备工作

首先,需要一个足够新的 Linux 内核,因为 btrfs 还是「实验特性」,每个版本都会有大量改进。如果用比较旧的内核就有可能出事。我用的是 3.12.6 版本。其次,安装 rsync 和 cryptsetup。

当然还要准备硬件:一块希捷 BackupPlus 1T USB 3.0 移动硬盘,以及一枚Express Card 34mm 转 USB 3.0 扩展卡,因为我的笔记本没有 3.0 的接口。注意使用 USB 3.0 扩展卡,内核需要载入 pciehp 模块,否则会出现不能识别 3.0 的设备或者后续接上去的设备的情况。Arch 官方内核将这个模块直接编译进内核了,而我自己编译的很不幸没有,只好重新编译了下内核。

PS: 这俩家伙一起配合工作,写入峰值能达到 110MiB/s,比我笔记本自身的硬盘还要快。

然后是分区、格式化。我使用了 GPT 分区表。为了安装 grub 以便启动,最好在开头分配 2M 空间给 grub 使用,不然会很麻烦2。记得给这个分区 bios_grub 标志(GParted「管理标志」里勾上即可)。下一个分区是 ext4 格式的启动分区。我会在这里放一个 Arch Linux Live 系统用于维护任务,以及用于启动到备份的内核和 initramfs。因为备份的分区会被加密,所以必须把内核和 initramfs 放在另外的地方。接下来的一个分区放加密过的备份数据用的。

像这样初始化加密分区:

cryptsetup luksFormat /dev/sdc3

密码要长,但也一定要记住密码,因为除了穷举外是没有办法恢复的。

初始化完毕之后就可以使用密码打开该设备了:

cryptsetup open /dev/sdc3 lilybackup

最后的参数是一个名字,它会是解密后的设备在 /dev/mapper 下的文件名。

如果一切完毕,要记得(在卸载文件系统之后)关闭该设备:

cryptsetup luksClose lilybackup

dm-crypt 是块设备级的加密。我们还要在其上建立文件系统:

mkfs.btrfs /dev/mapper/lilybackup

然后挂载之,并建立相应的目录和子卷结构。比如我的:

* backup (dir)
  * home (dir)
    * current (subvol, rw)
    * 20131016_1423 (subvol, ro)
    * 20131116_2012 (subvol, ro)
    * ...
  * root (dir)
    * current (subvol, rw)
    * 20131016_1821 (subvol, ro)
    * 20131116_2128 (subvol, ro)
    * ...
* run, for boot up directly, with edited /etc/fstab (dir)
  * home (dir)
    * 20131116 (subvol, rw)
    * ...
  * root (dir)
    * 20131116 (subvol, rw)
    * ...
* etc, store information and scripts (subvol, rw)

我把备份数据放到 backup 目录下,/ 和主目录 /home/lilydjwg 分开备份的。每个备份是使用日期和时间命名的快照子卷。除了用于每次同步的 current 目录外其它的子卷都是只读的,以免被意外修改。在 run 目录下是用于直接运行的,可写。这些可以按需建立。

开始备份

这是我备份 / 使用的脚本。是一个 zsh 脚本,这样可以避免 bash 中特殊字符可能带来的问题,虽然 bash 有 shellcheck 可以静态分析出可能有问题的地方。

这个脚本带两到三个参数。第一个是 / 的位置,因为我一般会直接从运行的系统执行备份,但也有可能使用另外的维护系统(比如系统滚挂掉的时候)。第三个参数是用于确认操作的。不加它的话会以 --dry-run 参数来运行 rsync。rsync 很复杂,所以最好先演习一遍以避免不小心手抖了做错事 =w=

为了避免日后对照着 rsync 手册来揣摸每个单字母选项的意义,我在这里全部使用了选项的完整形式。反正我是 zsh 用户,那么长的命令中大部分字符都是 zsh 给我补全出来的 =w=

#!/bin/zsh -e

cd $(dirname $0)

if [[ $# -lt 2 || $# -gt 3 ]]; then
  echo "usage: $0 SRC_DIR DEST_DIR [-w]"
  exit 1
fi

src=$1
dest=$2
doit=$3

if [[ $doit == -w ]]; then
  dry=
else
  dry='-n'
fi

rsync --archive --one-file-system --inplace --hard-links \
  --human-readable --numeric-ids --delete --delete-excluded \
  --acls --xattrs --sparse \
  --itemize-changes --verbose --progress \
  --exclude='*~' --exclude=__pycache__ \
  --exclude-from=root.exclude \
  $src $dest $dry

比较重要的几个 rsync 选项:

--archive
我们要备份,所以请保留所有信息
--one-file-system
只备份这个文件系统的内容,不要跑到 /sys 啊 /proc 啊 /dev 啊 /tmp 这类目录里去了。这也省得自己手动排除
--numeric-ids
文件的所有者信息使用数字而不要解析成用户名/组名。避免在跨系统使用时出差错
--exclude-from=root.exclude
root.exclude文件中读取额外的排除列表
--acls --xattrs
保留文件 ACL 和扩展属性

我发现的 / 里需要排除的目录如下:

/var/cache/*/*
/var/tmp/
/var/abs/local/
/var/lib/mongodb/journal/

其中第一项写成那样是因为,我要保留 /var/cache 下的一级目录。

主目录的备份是类似的过程,只是更加复杂罢了。当我写我的主目录的备份脚本的时候,深切地体会到有圣人说过的一句话——过早的优化是万恶之源。因为我使用 eCryptfs 加密主目录时为了避免可以公开的文件被加密造成性能损失,做了一系列的软链接。它们一直在给我带来各种小麻烦和不爽……

注意这些脚本要以 root 的身份运行。待所有备份脚本跑完之后,对那个 current 子卷做一个只读快照就好了:

sudo btrfs subvolume snapshot -r current $(date +'%Y%m%d_%H%M')

下次要更新备份时是一样的步骤:跑同步脚本,创建新快照。第一次同步会比较慢,跑了一个多小时吧。后边的增量备份就比较快了,十几分钟就好。

从备份启动

要启动,首先把 grub 装过去。把 Arch Linux live 系统的配置写好,当然还有启动备份系统的配置,如下:

search --no-floppy --fs-uuid --set=root 090dcc64-2b6d-421c-8ef6-2ab3321aec62

menuentry "Archlinux-2013.12.01-dual.iso (x86_64)" {
    load_video
    set gfxpayload=keep
    insmod gzio
    insmod ext2

    set isofile="/images/archlinux-2013.12.01-dual.iso"
    echo "Setup loop device..."
    loopback loop $isofile
    echo "Loading kernel..."
    linux (loop)/arch/boot/x86_64/vmlinuz archisolabel=ARCH_201312 img_dev=/dev/disk/by-label/lilyboot img_loop=$isofile earlymodules=loop
    echo "Loading initrd..."
    initrd (loop)/arch/boot/x86_64/archiso.img
}

menuentry "Archlinux-2013.12.01-dual.iso (i686)" {
    load_video
    set gfxpayload=keep
    insmod gzio
    insmod ext2

    set isofile="/images/archlinux-2013.12.01-dual.iso"
    echo "Setup loop device..."
    loopback loop $isofile
    echo "Loading kernel..."
    linux (loop)/arch/boot/i686/vmlinuz archisolabel=ARCH_201312 img_dev=/dev/disk/by-label/lilyboot img_loop=$isofile earlymodules=loop
    echo "Loading initrd..."
    initrd (loop)/arch/boot/i686/archiso.img
}

set ver=3.12.6
menuentry "Arch Linux $ver backup" {
    load_video
    set gfxpayload=keep
    insmod gzio
    insmod ext2

    echo    'Loading Linux kernel ...'
    linux   /boot/vmlinuz-linux-lily-$ver root=/dev/mapper/lilybackup rw cryptdevice=/dev/disk/by-uuid/815d01ea-6390-460f-8c82-84c9e9497423:lilybackup rootflags=compress=lzo,subvolid=0 break=postmount
    echo    'Loading initramfs...'
    initrd  /boot/initramfs-$ver-backup.img
}

各个设备的卷标、UUID 和文件路径自己调整。最后一项需要的文件在后边准备,先介绍一下几个参数:

root
根分区所在的设备。是解密后的设备路径或者用 UUID 也可以。不过没关系的,这里不会有冲突的
cryptdevice
如果使用密码(而不是密钥文件)加密的话,这里是冒号分隔的两个参数:你的加密设备是哪个文件,以及它解密之后叫什么名字。
rootflags
这个是给我们的 btrfs 用的。指定要启用 lzo 算法压缩,使用根子卷。实际上根子卷里不是 Linux 系统的根。这里我只是让脚本把它挂载到/new_root上而已,脚本会因为找不到/sbin/init而进入一个 shell 的
break=postmount
即使根没有问题,也请在挂载好它之后给我一个 shell,我可能需要做一些调整

内核很简单,直接 cp 过去就好了。initramfs 要另外生成。这是我用来生成的 mkinitcpio.conf.crypt 文件:

MODULES="btrfs"
BINARIES="/usr/bin/btrfs"
FILES=""
HOOKS="base udev autodetect modconf block encrypt filesystems keyboard fsck shutdown"
COMPRESSION="xz"

重要的地方:内核模块 btrfs 一定是要的,另外我还需要 btrfs 程序来操作子卷。在 HOOKS 数组的 filesystems 前添加了 encrypt,用于在启动时询问密码并解码根分区。

我还准备添加 vi 程序来着,但是它说终端类型不认识,还说 /var/tmp 目录不存在。于是索性把自己静态链接的 vim 扔到内核一块去了。Vim 内建常见终端类型的数据,不那么挑剔的。zsh 所需要的文件太多,也放弃了。

然后使用 mkinitcpio 命令生成 initramfs 镜像:

sudo mkinitcpio -c mkinitcpio.conf.crypt -g initramfs-backup.img

然后把生成的文件复制到启动分区的相应路径下。

注意:如果你在 Arch Linux live 系统中为另外的内核生成该 initramfs,要指定内核和内核模块路径的根。一定不要将内核模块所在的目录软链接到/lib/modules,那样 mkinitcpio 不会添加任何块设备的内核模块的。我使用的命令如下:

mkinitcpio --kernel /run/archiso/img_dev/boot/vmlinuz-linux-lily-3.12.6 -r /mnt/backup/root/20131227_2044 --config mkinitcpio.conf -g /run/archiso/img_dev/boot/initramfs-3.12.6-backup.img

文件准备完毕,就可以启动过去了。注意我没有在备份分区的 run 目录下建立子卷,因为我准备进入 initramfs 之后再建立它们。

PS: 因为 BIOS 不支持,所以必须从 USB 2.0 来启动。在 Linux 内核启动的时候,可以将移动硬盘接到 USB 3.0 扩展卡上。具体时机是,initramfs 载入完毕,内核开始打印日志的时候。

进入备份系统

启动之后,会进入 initramfs 的 shell。在这个没有任务管理的 ash 中,使用 btrfs 命令在 /new_root/run 目录下建立新的子卷,注意不要加 -r 这个表示只读的选项了:

btrfs subvolume snapshot ../backup/root/20131016_1423 root/20131016
btrfs subvolume snapshot ../backup/home/20131016_1423 home/20131016

因为文件系统树的挂载结构变了,所以得拿准备好的 vim(或者 vi,如果你没准备 vim 的话)去编辑 root/20131016/etc/fstab 文件,将那些不会成功的挂载项都去掉,添加新的正确的项。PS: 如果使用 vim 的话,记得进去先set nocp一下,不然会是兼容模式,和 vi 一样只能撒消一步的。

/dev/mapper/lilybackup  /       btrfs   rw,relatime,compress=lzo,subvol=run/root/xxx     0 0
/dev/mapper/lilybackup  /home/lilydjwg  btrfs   rw,relatime,compress=lzo,subvol=run/home/xxx     0 0

然后 cd /,卸载 /new_root 并重新以子卷挂载之:

cd /
umount /new_root
mount -o compress=lzo,subvol=run/root/20131016 /dev/mapper/lilybackup /new_root

如果是因为找不到 /sbin/init 而进来这个 shell 的,那么就tail /init,最后那行是需要执行的命令(当然有些修改):

exec env -i "TERM=$TERM" /usr/bin/switch_root /new_root /sbin/init

如果是因为break=postmount参数而进来的,直接按Ctrl-D退出 shell 即可。

启动会继续进行,systemd 启动了,各种服务陆续启动中~~

如果很不幸地,忘记修改/etc/fstab了,或者有错,那么 systemd 会毫不留情地「Welcome to emergency shell」。不过现在更正也为时未晚。在编辑完 fstab 之后,要先执行下systemctl daemon-reload再退出那个 emergency shell。

接下来应该能一路顺利地到达指定时间的系统啦 =w=

时间机器打造完成哦耶~~

参考资料

Category: Linux | Tags: linux grub grub2 btrfs Arch Linux | Read Count: 31245
zz 说:
Jan 06, 2014 03:20:51 PM

好麻烦...
话说你那个生成目录 点第一次正常 再点一次就坏了

zz 说:
Jan 06, 2014 03:23:20 PM

test阿斯顿撒test

Avatar_small
依云 说:
Jan 06, 2014 05:01:08 PM

谁让你点两次的啦……

zzz 说:
Jan 15, 2014 05:18:16 PM

感觉lz什么都在研究。。。

Avatar_small
依云 说:
Jan 15, 2014 05:49:18 PM

需要什么就研究什么 =w=

chronos 说:
Jan 29, 2014 11:18:54 PM

我也是用btrfs+rsync来做备份的,用rsync从生产环境同步数据到备份机,每天自动生成相应日期的快照。保留60天,60天前的自动删除。这样200多G的数据60天的完整备份也不过才300G不到。

Avatar_small
依云 说:
Jan 30, 2014 11:42:37 AM

哈,我之前搜索时看到过你那篇文章呢。我这里有几个 vbox 虚拟机镜像,开机一次就要整个复制一次,所以之前 100G 的备份现在已经长到 170G 啦。

草陌博客 说:
Jan 30, 2014 02:31:56 PM

博主已经深陷技术的泥潭,难以自拔

chronos 说:
Jan 30, 2014 07:51:30 PM

感觉你比我还会折腾啊,各种东西都玩。

it 说:
Feb 11, 2014 10:32:43 AM

不错的文章,多谢了。
it@powercore.com.cn

xyf1219 说:
May 23, 2017 06:55:53 PM

仙子 请问下你 archlinux 的文件系统是什么的喵

不知道现在全盘 Btrfs 稳不稳定

Avatar_small
依云 说:
May 23, 2017 09:35:11 PM

我的 / 目前还是 ext4,源码等数据用的 btrfs,大文件(电影、软件镜像、虚拟机)用的 xfs,备份用的 btrfs 和 zfs。

目前我还没有遇到关于文件系统的任何稳定性问题。

m31271n 说:
Jun 28, 2017 10:47:02 AM

用了几年,没出现什么问题。

Avatar_small
依云 说:
Jun 28, 2017 11:08:48 AM

我的 / 现在换成 XFS 啦~

puzhou 说:
Dec 02, 2017 08:27:01 PM

使用btrfs的发送子卷功能代替rsync可以吗?我打算用这样的方案备份到移动硬盘上,另外博主的希捷移动硬盘可安好?打算入手一块移动硬盘,纠结于买哪个牌子呢

Avatar_small
依云 说:
Dec 02, 2017 09:28:31 PM

如果你已经在使用 btrfs / zfs 了,用 send / receive 挺好的呀。我的根之前是 ext4 现在是 xfs 所以才没法用那个特性。

文章里说的那块盘早挂了。自由落体30cm左右后被数据线扯住,但是盘从此以后转不起来了。前两天拆盘看到是磁头未能归位。手动把磁头推回去了。但是不知道从什么时候起整个机械系统已经无法启动,加电后只能看到驱动在尝试转动硬盘却完全听不到声音。也不知道是不是我拆盘的时候不小心把哪里弄坏了。

现在使用的硬盘是联想和 SONY 各一块,都还安好。九年前买过一块不知道什么牌子的,电脑显示是西数,前两年开始慢慢的接上只亮灯无响应,也挂了。

puzhou 说:
Dec 03, 2017 02:16:06 PM

嗯嗯,看来硬盘这个东西还是挺脆弱的,,多谢博主指导啦。

nwindy 说:
Nov 15, 2018 07:20:03 PM

应该对snapshot 做个hash

Avatar_small
依云 说:
Nov 15, 2018 08:19:30 PM

做 hash 有什么用呢?btrfs / zfs 有做数据校验的。

nwindy 说:
Nov 15, 2018 11:12:51 PM

涨知识了 =_= 好高级desu

徐艺扬 说:
Feb 07, 2020 10:22:36 AM

我现在在笔记本上装的是LinuxMint,打算下次重装的时候装个Arch试试看……到时候就用得上博主的教程了。

Avatar_small
依云 说:
Feb 07, 2020 01:02:50 PM

其实不同发行版都适用的,原理一样的。

徐艺扬 说:
Feb 08, 2020 12:05:36 PM

主要是LinuxMint比较“开箱即用”,系统搞崩了之后把数据打个包直接重装也不会有太大问题,毕竟我自己自定义的也很少。Arch就不一样了,虽然我至今还没有安装过,但总觉得很复杂……

Avatar_small
依云 说:
Feb 08, 2020 02:17:05 PM

嗯,手动安装自然是复杂一些。不过不作死的话 Arch 很难崩到完全救不回来,所以我从来没有在同一机器上重装过呢。即便是换电脑也可以 rsync 过去。而像 Ubuntu,我只能眼见它慢慢坏掉然后整个地重装。

Mint 出过一次事,官方发布的 iso 文件被篡改过,然后官方在网站上发布了正确版本的 md5 还是 sha1 来着,但是那网站是 http 的。而且那镜像也没有签名。

徐艺扬 说:
Mar 11, 2020 06:11:23 PM

之前我试着装Arch,发现驱动还要手动装就放弃了。
又入了Manjaro,用着很舒服,pacman很好用,但滚着滚着Grub挂了,重装了一次无果。
我又试了几个发行版,KDE Noon,kUbuntu之类的,但总感觉对KDE定制得不够好。于是我装了openSUSE,用着很舒服,除了软件少之外没什么问题。备份用的是btrfs加上自带的snapper。

徐艺扬 说:
Mar 11, 2020 06:13:10 PM

呀,不知道为什么发到外面来了……

Avatar_small
依云 说:
Mar 11, 2020 06:53:41 PM

snapper 那个是快照,不能算是备份。它可以防止误操作或者程序 bug 导致数据丢失,但是不防你的硬盘坏掉。

徐艺扬 说:
Mar 13, 2020 01:11:14 PM

home目录我也有用rsync备份到移动硬盘上,重要数据再给加个云同步。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com