字数 1470,阅读大约需 8 分钟
Btrfs 快照 + send/receive = 强大的备份与同步系统。本篇讲清快照原理、send/receive 流程、增量备份、与 snapper 的集成。
一、快照是什么?
Btrfs 快照 = subvolume 的只读或可写引用副本。
原始 subvolume: /mnt/data
引用 extent: A, B, C
快照 /mnt/data@2024-01-01:
引用 extent: A, B, C(共享,不复制)
修改 /mnt/data(覆盖 extent B):
/mnt/data → A, B', C
/mnt/data@2024-01-01 → A, B, C(保留旧 B)
关键点:
二、创建快照
2.1 可写快照
# 创建一个可写快照
$ btrfs subvolume snapshot /mnt/data /mnt/data-new
Create snapshot of '/mnt/data' in '/mnt/data-new'
# 验证
$ btrfs subvolume list /mnt
ID 256 gen 8 top level 5 path data
ID 350 gen 10 top level 5 path data-new
2.2 只读快照
# 创建只读快照(推荐用于备份)
$ btrfs subvolume snapshot -r /mnt/data /mnt/snapshots/data@2024-01-01
Create readonly snapshot of '/mnt/data' in '/mnt/snapshots/data@2024-01-01'
# 验证
$ btrfs subvolume show /mnt/snapshots/data@2024-01-01
Name: data@2024-01-01
Flags: readonly
2.3 递归快照(多 subvolume)
# 一次性拍多个 subvolume
$ for subvol in @ @home data; do
btrfs subvolume snapshot -r /mnt/$subvol /mnt/snapshots/${subvol}@$(date +%Y%m%d)
done
三、查看快照
# 列出所有快照
$ btrfs subvolume list -s /mnt
ID 350 gen 10 top level 5 path snapshots/data@2024-01-01
ID 351 gen 12 top level 5 path snapshots/data@2024-01-15
# 看快照占用
$ btrfs subvolume show /mnt/snapshots/data@2024-01-01
Name: data@2024-01-01
...
Snapshot(s):
data-new (320M used)
占用 = 与父 subvolume 差异的 extent 大小。
四、回滚(Rollback)
Btrfs 不直接支持 rollback,但可以用以下方法实现:
4.1 替换回滚
# 1. 把快照改名为原 subvolume
$ mv /mnt/data /mnt/data.broken
$ mv /mnt/snapshots/data@2024-01-01 /mnt/data
4.2 在快照上工作
# 1. 拍快照
$ btrfs subvolume snapshot -r /mnt/data /mnt/snapshots/data@before-upgrade
# 2. 升级...
# 3. 失败?用快照替换
$ mv /mnt/data /mnt/data.bad
$ btrfs subvolume snapshot /mnt/snapshots/data@before-upgrade /mnt/data
4.3 启动时回滚
系统盘场景(@ 是根,@home 是 home):
# 启动进 live USB
# 挂载 Btrfs
$ mount /dev/sda2 /mnt
# 查快照
$ ls /mnt/@/.snapshots/
# 回滚
$ mv /mnt/@ /mnt/@.broken
$ mv /mnt/@/.snapshots/1/snapshot /mnt/@
# 重启
五、send/receive:流式备份
5.1 什么是 send/recv
- •
btrfs send:把 subvolume 或快照导出为流(类似 tar 流) - •
btrfs receive:把流导入为新 subvolume
完整:btrfs send snap1 | btrfs receive /backup
增量:btrfs send -p snap1 snap2 | btrfs receive /backup
5.2 完整发送
# 1. 拍快照
$ btrfs subvolume snapshot -r /mnt/data /mnt/snapshots/data@base
# 2. 发送
$ btrfs send /mnt/snapshots/data@base > /backup/data-full.btrfs
# 或直接接收
$ btrfs send /mnt/snapshots/data@base | btrfs receive /backup
5.3 增量发送
# 1. 拍新快照
$ btrfs subvolume snapshot -r /mnt/data /mnt/snapshots/data@v2
# 2. 增量发送(基于 base)
$ btrfs send -p /mnt/snapshots/data@base /mnt/snapshots/data@v2 | btrfs receive /backup
# ^^^^^^ parent snap
# ^^^^^ new snap
接收端必须有 parent 快照,才能做增量。
5.4 远程发送
# 1. 本地拍快照
$ btrfs subvolume snapshot -r /mnt/data /mnt/snapshots/data@2024-01-15
# 2. 发送到远程
$ btrfs send /mnt/snapshots/data@2024-01-15 | ssh backup-server btrfs receive /backup/data
# 或
$ btrfs send /mnt/snapshots/data@2024-01-15 | ssh backup-server "cat > /backup/data-2024-01-15.btrfs"
优势:
- • 不需要客户端软件(只有 btrfs-progs + ssh)
六、自动化:snapper
6.1 什么是 snapper
snapper 是 openSUSE 开发的 Btrfs 快照管理工具:
- • 与
btrfs send/recv 配合做远程备份
6.2 安装与初始化
# openSUSE
$ zypper install snapper
# Ubuntu
$ apt install snapper
# 创建配置(针对 / 挂载点)
$ snapper -c root create-config /
# 看
$ snapper list-configs
Config | subvolume
-------+----------
root | /
6.3 自动拍快照
# 手动拍
$ snapper -c root create --description "before upgrade"
# 时间线快照(snapper 自动)
$ snapper -c root create --command "yum update" --description "system update"
# 看
$ snapper -c root list
# | Type | Pre # | Date | User | Cleanup | Description
---+--------+-------+--------------------------+------+---------+--------------------
0 | single | | | root | | current
1 | single | | 2024-01-15 03:00:01 | root | | before upgrade
6.4 自动清理
# /etc/snapper/configs/root
TIMELINE_CREATE="yes"
TIMELINE_CLEANUP="yes"
TIMELINE_MIN_AGE="1800" # 30 分钟
TIMELINE_LIMIT_HOURLY="10"
TIMELINE_LIMIT_DAILY="10"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="10"
TIMELINE_LIMIT_YEARLY="10"
snapper 自动保留:
6.5 snapper 与 send/recv
# snapper 配合 btrfs-send 做远程备份
$ snapper -c root send 1 2 3 | ssh backup-server "btrfs receive /backup/root"
实际上手写脚本更灵活。
七、实战:完整备份流程
7.1 本地备份脚本
#!/bin/bash
# /usr/local/bin/btrfs-backup-local.sh
SUBVOL=$1
DEST=$2
SNAP_DATE=$(date +%Y%m%d-%H%M%S)
if [ -z "$SUBVOL" ] || [ -z "$DEST" ]; then
echo "Usage: $0 <subvol> <dest>"
exit 1
fi
# 1. 拍快照
btrfs subvolume snapshot -r "/mnt/$SUBVOL" "/mnt/snapshots/${SUBVOL}@${SNAP_DATE}"
# 2. 找最新父快照
LATEST_SNAP=$(ls -1 /mnt/snapshots/${SUBVOL}@* 2>/dev/null | sort | tail -1)
# 3. 发送
if [ -z "$LATEST_SNAP" ]; then
# 完整
btrfs send "/mnt/snapshots/${SUBVOL}@${SNAP_DATE}" | btrfs receive "$DEST"
else
# 增量
btrfs send -p "$LATEST_SNAP" "/mnt/snapshots/${SUBVOL}@${SNAP_DATE}" | btrfs receive "$DEST"
fi
# 4. 清理 7 天前快照
find /mnt/snapshots -name "${SUBVOL}@*" -mtime +7 -exec btrfs subvolume delete {} \;
7.2 远程备份
#!/bin/bash
# /usr/local/bin/btrfs-backup-remote.sh
SUBVOL=$1
REMOTE=$2 # user@host:/path
SNAP_DATE=$(date +%Y%m%d-%H%M%S)
# 1. 拍快照
btrfs subvolume snapshot -r "/mnt/$SUBVOL" "/mnt/snapshots/${SUBVOL}@${SNAP_DATE}"
# 2. 远程发送
LATEST_SNAP=$(ls -1 /mnt/snapshots/${SUBVOL}@* 2>/dev/null | sort | tail -1)
if [ -z "$LATEST_SNAP" ]; then
btrfs send "/mnt/snapshots/${SUBVOL}@${SNAP_DATE}" | ssh "${REMOTE%:*}" "btrfs receive ${REMOTE#*:}"
else
btrfs send -p "$LATEST_SNAP" "/mnt/snapshots/${SUBVOL}@${SNAP_DATE}" | ssh "${REMOTE%:*}" "btrfs receive ${REMOTE#*:}"
fi
7.3 cron 自动化
# /etc/cron.d/btrfs-backup
0 2 * * * root /usr/local/bin/btrfs-backup-remote.sh data backup@backup-nas:/backup/data
0 3 * * * root /usr/local/bin/btrfs-backup-remote.sh home backup@backup-nas:/backup/home
八、send/recv 限制
| |
|---|
| writeable subvolume 不能直接发 |
| |
| 跨内核版本不兼容 | |
| 跨主机不同 FS UUID 报错 | |
九、恢复 send/recv 备份
# 1. 接收端:找到快照
$ btrfs subvolume list /backup
# 看 ID 和 path
# 2. 接收回来
$ btrfs send /backup/data@2024-01-15 | btrfs receive /mnt/restored
# 3. 挂载
$ mount -o subvol=restored /dev/sdX /mnt/restored
十、本篇小结
| |
|---|
| btrfs subvolume snapshot /src /dst |
| btrfs subvolume snapshot -r /src /dst |
| btrfs send snap1 |
| btrfs send -p snap_old snap_new |
| btrfs receive /dest |
| btrfs send ... | ssh host btrfs receive ... |
| snapper |
核心要点:
- • send/recv = 流式传输(管道/SSH)
- • snapper 是 openSUSE 标准做法