Btrfs + Snapper 完全指南:服务器系统更新的"时光机"
前言
每个运维都经历过这样的场景:
1
2
3
4
5
6
7
|
周五 18:00,部署完毕,准备下班。
周五 18:30,老板来电话:"安全漏洞,紧急升级!"
周五 19:00,执行:yum update -y
周五 19:30,升级完成,重启。
周五 19:35,黑屏了——新内核与显卡驱动不兼容。
周五 19:36,看着屏幕上转圈的启动失败动画,
你突然意识到——今晚的约会完了。
|
这不是段子,这是 Linux 运维的真实日常。
Windows 有"系统还原点",macOS 有 Time Machine,为什么 Linux 没有"升级前自动备份"的标准机制?
答案是:以前没有,但现在有了——Btrfs + Snapper。
Snapper 是 openSUSE 出品的快照管理工具,能与 zypper / yum / dnf / apt / pacman 包管理器深度集成,自动在升级前后创建快照,升级失败只需一条命令回滚到上一秒的状态。
这篇文章,我会带你从零搭建这套"系统时光机":
1
2
3
4
5
6
7
8
9
|
Btrfs 文件系统(COW 快照零成本)
+
Snapper 工具(自动创建/清理快照)
+
包管理器集成(升级前自动快照)
+
开机自动 rollback 支持(内核级回滚)
=
服务器升级不再提心吊胆
|
一、原理:为什么 Snapper 是"时光机"
1.1 Snapper 是什么
Snapper 是 openSUSE 项目开发的快照管理工具,由 Arvin Schnell 于 2012 年发起,目前由 openSUSE 团队维护。
它不是文件系统,而是 Btrfs 子卷快照的"管理者",解决三个核心问题:
1
2
3
|
1. 自动创建:配合包管理器,升级/安装前自动打快照
2. 自动清理:根据时间、数目,删除过老的快照
3. 傻瓜回滚:用 snapper rollback 一键还原到某个快照
|
1.2 工作原理图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
包管理器 (yum/dnf/apt)
│
│ 升级前自动调用
↓
Snapper pre-snapshot hook(钩子)
│
│ btrfs subvolume snapshot
↓
[只读快照 #123] ←—— 升级前的"档案"
│
↓
包管理器执行升级
│
↓
Snapper post-snapshot hook(钩子)
│
│ btrfs subvolume snapshot
↓
[只读快照 #124] ←—— 升级后的"现场"
如果升级成功:保留两个快照(保留现场证据)
如果升级失败:snapper undochange → 回滚到 #123
|
1.3 适合谁
| 用户 |
适合程度 |
原因 |
| 个人 Linux 桌面 |
⭐⭐⭐⭐⭐ |
Manjaro / openSUSE Tumbleweed 默认启用 |
| 服务器管理员 |
⭐⭐⭐⭐⭐ |
系统升级前自动快照 |
| 企业运维 |
⭐⭐⭐⭐ |
与自动化运维集成 |
| 开发者本地 VM |
⭐⭐⭐⭐⭐ |
系统折腾不怕坏 |
| 生产数据库服务器 |
⭐⭐⭐ |
不推荐(数据库有自己的备份方案) |
二、准备:搭建 Btrfs 文件系统
2.1 检查现有系统是否已经是 Btrfs
1
2
3
4
5
6
7
8
|
# 查看系统盘文件系统类型
df -Th /
# 典型输出(已使用 Btrfs):
# Filesystem Type Size Used Avail Use% Mounted on
# /dev/sda2 btrfs 100G 45G 55G 45% /
# 如果不是 Btrfs,需要重新规划
|
重要前提:Snapper 依赖 Btrfs 的 COW 快照特性。如果你的根文件系统是 ext4 / XFS,Snapper 用不了。
2.2 新装系统时启用 Btrfs
openSUSE(Snapper 默认开箱即用)
openSUSE Tumbleweed / Leap 默认就是 Btrfs + Snapper,安装时无需任何配置:
1
2
3
4
|
安装 openSUSE 时:
- 根分区类型:默认 Btrfs
- Snapper:默认安装并自动配置
- 安装完,重启后立刻就有快照可用
|
Manjaro / Arch Linux
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 安装时选择 btrfs 文件系统
# 安装 snapper 和 snap-pac
sudo pacman -S snapper snap-pac grub-btrfs
# 创建根目录配置
sudo snapper -c root create-config /
# 创建初始快照(重要:snapper rollback 需要至少两个快照)
sudo snapper -c root create --description "Initial snapshot"
# 启用 snapper 自动清理定时器
sudo systemctl enable --now snapper-timeline.timer
sudo systemctl enable --now snapper-cleanup.timer
|
RHEL 9 / CentOS Stream 9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 安装 snapper
sudo dnf install snapper
# 创建根目录配置
sudo snapper -c root create-config /
# 修改 /etc/fstab,让根目录以 subvol=@ 形式挂载
# (Anaconda 安装 Btrfs 时默认已经做了)
# 重启或重新挂载(snapper 修改了 /etc/fstab)
sudo reboot
# 创建初始快照
sudo snapper -c root create --description "Initial state"
|
Ubuntu / Debian(部分支持)
Ubuntu 22.04+ 默认支持 Btrfs,但 Snapper 在 Ubuntu 上的集成度不如 openSUSE,需要手动配置:
1
2
3
4
5
6
7
8
9
|
# 安装 snapper
sudo apt install snapper
# 创建配置
sudo snapper -c root create-config /
# 关键步骤:Ubuntu 的子卷布局和 openSUSE 不同,需要调整
# Ubuntu 的根目录通常在 subvolid=5(top-level),不是 @ 子卷
# 详细配置见后文"Ubuntu 特殊处理"章节
|
2.3 验证环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 检查 Snapper 是否安装
snapper --version
# snapper 0.10.x
# 查看已有配置
sudo snapper list-configs
# Config | Subvolume
# -------+----------
# root | /
# 查看快照列表(初始可能为空)
sudo snapper list
# # | Type | Pre # | Date | User | Cleanup | Description | Userdata
# ---+--------+-------+--------------------------+------+---------+-----------------------+---------
# 0 | single | | | root | | current |
# 如果报 "IO Error",通常是 /etc/snapper/configs/root 中 SUBVOLUME 配置不对
|
三、配置 Snapper:精细化设置
3.1 配置文件位置
Snapper 的配置都在 /etc/snapper/configs/ 目录下:
1
2
3
4
5
|
ls /etc/snapper/configs/
# root ← 根目录配置(最常用)
# 配置文件内容(默认的 root 配置)
cat /etc/snapper/configs/root
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 关键字段说明:
SUBVOLUME="/"
FSTYPE="btrfs"
QGROUP=""
SPACE_LIMIT="0.5"
FREE_LIMIT="0.2"
# 数字时间线快照保留策略
TIMELINE_CREATE="yes"
TIMELINE_LIMIT_HOURLY="10"
TIMELINE_LIMIT_DAILY="10"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="10"
TIMELINE_LIMIT_YEARLY="0"
# 预/后快照钩子(包管理器集成)
PRE_CLEANUP="yes"
POST_CLEANUP="yes"
|
3.2 修改配置:调整快照保留策略
1
2
|
# 直接编辑配置文件
sudo vim /etc/snapper/configs/root
|
一份实战推荐配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
# 子卷路径
SUBVOLUME="/"
FSTYPE="btrfs"
# 空间限制:快照最多占用 50% 卷空间(防止快照撑爆磁盘)
SPACE_LIMIT="0.5"
# 当卷剩余空间少于 20% 时,自动清理快照
FREE_LIMIT="0.2"
# ===== 数字时间线快照(每隔一段时间自动拍)=====
TIMELINE_CREATE="yes"
# 每小时保留 5 个
TIMELINE_LIMIT_HOURLY="5"
# 每天保留 7 个
TIMELINE_LIMIT_DAILY="7"
# 每周保留 3 个
TIMELINE_LIMIT_WEEKLY="3"
# 每月保留 6 个
TIMELINE_LIMIT_MONTHLY="6"
# 每年保留 0 个(不保留)
TIMELINE_LIMIT_YEARLY="0"
# ===== 预/后快照清理 =====
# 自动清理 yum/zypper/apt 创建的"未命名"快照
PRE_CLEANUP="yes"
POST_CLEANUP="yes"
# 包管理器命令过滤(哪些命令触发预快照)
# ALLOW_USERS=""
# ALLOW_GROUPS=""
|
配置字段详细说明:
1
2
3
4
5
6
7
8
9
10
11
|
SPACE_LIMIT="0.5"
→ 快照最大占用 50% 的卷空间
→ 超过会自动清理最老的快照
FREE_LIMIT="0.2"
→ 当空闲空间少于 20%,自动清理快照
→ 防止磁盘被快照撑爆
TIMELINE_LIMIT_HOURLY="5"
→ 最多保留 5 个"每小时快照"
→ 旧的会被自动清理
|
3.3 重要:Btrfs 子卷布局
openSUSE 标准布局(snapper rollback 完美支持):
1
2
3
4
5
6
7
8
9
|
top-level (subvolid=5)
├── @ ← 根目录 (/)
│ ├── bin/
│ ├── etc/
│ ├── home/
│ └── usr/
├── @home ← /home 目录
├── @snapshots ← 快照存储目录(不要手动修改)
└── @var ← /var 目录
|
/etc/fstab 关键挂载项:
1
2
3
|
UUID=xxxx / btrfs subvol=@,defaults 0 0
UUID=xxxx /home btrfs subvol=@home,defaults 0 0
UUID=xxxx /.snapshots btrfs subvol=@snapshots,defaults 0 0
|
这种布局让 snapper rollback 命令可以无副作用地把系统回滚到某个快照。
3.4 Ubuntu 特殊处理
Ubuntu 22.04 默认把根目录放在 subvolid=5(顶层),而不是 @ 子卷。这种情况下:
选项一:手动改为 openSUSE 风格(推荐)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 1. 从 Live USB 启动
# 2. 挂载 btrfs 顶层
mount -o subvolid=5 /dev/sda2 /mnt
# 3. 创建 @ 子卷
btrfs subvolume create /mnt/@
# 4. 复制所有文件到 @ 子卷
cp -a /mnt/old_root/. /mnt/@/
# 5. 修改 /etc/fstab
# 把 / 挂载改成 subvol=@
# UUID=xxx / btrfs subvol=@,defaults 0 0
# 6. 重启进入新系统
|
选项二:仅用 Snapper 创建快照,不使用 rollback(Ubuntu 默认情况)
Ubuntu 的 Subvolume 布局让 snapper rollback 命令无法直接使用,但手动 undochange 可以工作:
1
2
3
4
5
6
7
|
# 查看快照
sudo snapper list
# 撤销某个快照的所有变更
sudo snapper undochange 13..14
# 如果不想用,可以只保留 create / list / delete
|
四、基础操作:Snapper 入门命令
4.1 创建快照
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 创建单条快照
sudo snapper create --description "升级前手动快照"
# 创建快照时附带重要信息
sudo snapper create \
--description "升级前快照" \
--cleanup-algorithm number
# 创建快照对(用于对比"前后")
sudo snapper create --type pre --description "升级前快照"
# 返回:5
sudo snapper create --type post --pre-number 5 --description "升级后快照"
# 返回:6
|
4.2 查看快照
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# 列出所有快照
sudo snapper list
# 输出示例:
# # | Type | Pre # | Date | User | Cleanup | Description | Userdata
# ---+--------+-------+--------------------------+------+---------+-----------------------+---------
# 0 | single | | | root | | current |
# 1 | single | | 2026-07-20 14:00:00 | root | | 首次启动后状态 |
# 5 | pre | | 2026-07-25 11:30:00 | root | number | yum |
# 6 | post | 5 | 2026-07-25 11:35:00 | root | number | yum |
# 查看某个快照的详细信息
sudo snapper status 5..6
# 查看两个快照之间的差异
sudo snapper diff 5..6
|
4.3 删除快照
1
2
3
4
5
6
7
8
|
# 按编号删除
sudo snapper delete 5
# 删除一批快照(0-10)
sudo snapper delete 1-10
# 自动清理过期快照(根据 TIMELINE_LIMIT_* 配置)
sudo snapper cleanup timeline
|
4.4 关键:快照类型
1
2
3
|
single : 单一快照(手动创建)
pre : 预快照(包管理器升级前自动创建)
post : 后快照(包管理器升级后自动创建)
|
五、与包管理器集成:自动快照实战
5.1 安装 Snapper 钩子
Snapper 与包管理器的"无缝集成"是通过各发行版的 snap-pac / dnf-plugin-snapper / apt-snapper 等插件实现的。
openSUSE(zypper)
1
2
3
4
5
6
7
|
# snapper 默认已经与 zypper 集成
# 升级前自动 pre-snapshot,升级后自动 post-snapshot
sudo zypper update
# 会看到:
# 正在创建 pre snapshot...已完成
# ...升级过程...
# 正在创建 post snapshot...已完成
|
Arch / Manjaro(pacman):snap-pac
1
2
3
4
5
6
7
|
# 安装
sudo pacman -S snap-pac
# 此后 pacman 自动触发快照:
sudo pacman -Syu
# Pre-snapshot created (snap-pac): #7
# Post-snapshot created (snap-pac): #8
|
RHEL / CentOS / Fedora(yum / dnf):dnf-plugin-snapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 安装 dnf-plugin-snapper(Fedora 包含在 snapper 包中)
sudo dnf install dnf-plugin-snapper
# 或 RHEL:
sudo dnf install dnf-plugin-snapper
# 配置
sudo vim /etc/dnf/plugins/snapper.conf
# 关键字段:
# [main]
# create_snapshots = yes
# snapshot_on_install = no ← 安装单个包时是否快照(建议 no,避免快照过多)
# 启用
sudo dnf config-manager --enable snapper
# 测试
sudo dnf install htop
# Pre-snapshot created: #9
# Post-snapshot created: #10
|
Ubuntu / Debian(apt):apt-snapper
1
2
3
4
5
6
7
8
9
10
|
# Ubuntu 上 apt-snapper 不在官方仓库,需要从 snap 或 PPA 安装
sudo snap install apt-snapper
# 或下载 deb 包
wget https://github.com/ultimate-priority/apt-snapper/releases/latest/download/apt-snapper.deb
sudo dpkg -i apt-snapper.deb
# 测试
sudo apt install nginx
# 会自动在升级前后创建快照
|
5.2 验证集成是否生效
1
2
3
4
5
6
7
8
9
|
# 触发一次包安装
sudo dnf install -y tree
# 查看是否创建了快照
sudo snapper list
# 应该看到一对 pre/post 快照
# 查看变更
sudo snapper status <pre-num>..<post-num>
|
5.3 实战演练:升级失败一键回滚
第一步:升级前查看可用快照
1
2
3
|
sudo snapper list
# 5 pre - 2026-07-25 11:30:00 root number yum
# 6 post 5 2026-07-25 11:35:00 root number yum
|
第二步:执行升级(故意制造故障)
1
2
3
4
|
# 假设升级某个包导致系统问题
sudo dnf update -y kernel-*
sudo reboot
# 重启后内核 Panic,进不去系统!
|
第三步:从 GRUB 进入 snapshot 回滚
重启时按 Shift(BIOS)或 Esc(UEFI)进入 GRUB 菜单:
1
2
3
4
5
6
7
|
GRUB 菜单:
┌────────────────────────────────────────┐
│ openSUSE Tumbleweed │
│ openSUSE Tumbleweed (snapshot 5) │ ← 选择这个
│ Advanced options for openSUSE... │
│ ... │
└────────────────────────────────────────┘
|
选择带 “(snapshot X)” 后缀的菜单项,系统会用 snapshot X 的根目录 启动(同时修改子卷指向,让默认启动也指向该快照)。
第四步:登录系统后,把默认启动固定到回滚版本
1
2
3
4
5
6
7
8
|
# 把 snapshot 5 设为默认(注意不是创建新快照,而是切换默认子卷)
sudo snapper rollback 5
# 或者用 btrfs 命令手动修改默认子卷
sudo btrfs subvolume set-default $(btrfs subvolume list / | grep "@/.snapshots/5/snapshot" | awk '{print $2}')
# 重启验证
sudo reboot
|
第五步:如果回滚成功,删除出问题的快照
5.4 不重启回滚:snapper undochange
snapper rollback 需要重启,但 snapper undochange 可以在运行时回滚到某个快照:
1
2
3
4
5
6
7
8
9
|
# 撤销从快照 5 到快照 6 的所有变更
# (会把 6 的状态反向修改回 5 的状态)
sudo snapper undochange 5..6
# 撤销 5..6 的 /etc 下变更(只回滚配置文件)
sudo snapper undochange -f 5..6
# 撤销 5..6 的 /usr 下变更(回滚软件包文件)
sudo snapper undochange -f 5..6 /usr
|
注意:
undochange 会在当前运行的系统上回滚文件,可能导致正在运行的程序崩溃
- 回滚
/etc 风险较高(可能影响正在运行的服务的配置)
- 推荐场景:升级失败后,在重启之前先 undochange,或者干脆重启进 snapshot
六、snap-pac-yr(自动快照增强)
6.1 手动定期快照
除了包管理器触发,还可以让 Snapper 定时自动快照:
1
2
3
4
5
6
7
|
# snapper 自带两个定时器
sudo systemctl status snapper-timeline.timer
sudo systemctl status snapper-cleanup.timer
# 启动并启用
sudo systemctl enable --now snapper-timeline.timer # 每天创建时间线快照
sudo systemctl enable --now snapper-cleanup.timer # 每天清理过期快照
|
默认行为:
snapper-timeline.timer:每小时一次时间线快照
snapper-cleanup.timer:每天一次清理过期快照(按 TIMELINELIMIT* 配置)
6.2 snap-pac 的 pre/post 行为
snap-pac 的默认行为:
1
2
3
4
5
6
7
8
9
10
11
|
# 安装/升级/删除 包都会触发 pre + post 快照
sudo pacman -S htop
# 触发 pre snapshot
# 执行 pacman -S htop
# 触发 post snapshot
# 升级系统
sudo pacman -Syu
# 触发 pre snapshot
# 执行 pacman -Syu
# 触发 post snapshot
|
避免快照过多:可以在 /etc/snapper/configs/root 中开启 PRE_CLEANUP:
1
2
|
PRE_CLEANUP="yes" # 清理无对应 post 的 pre 快照
POST_CLEANUP="yes" # 清理无对应 pre 的 post 快照
|
但通常建议保留 pre + post 对,因为这是升级证据。
七、GRUB 集成:开机选择快照启动
7.1 openSUSE 自动配置
openSUSE 默认在 GRUB 中加入所有快照的启动项:
1
2
3
4
5
6
7
8
|
GRUB 菜单:
openSUSE Tumbleweed
openSUSE Tumbleweed, with Linux 6.x.x-1-default
openSUSE Tumbleweed, with Linux 6.x.x-1-default (recovery mode)
► openSUSE Snapshots
openSUSE Tumbleweed (snapshot 5)
openSUSE Tumbleweed (snapshot 6)
...
|
7.2 Manjaro / Arch 手动配置(grub-btrfs)
1
2
3
4
5
6
7
8
9
10
|
# 安装 grub-btrfs
sudo pacman -S grub-btrfs
# 启用服务(自动从快照检测启动项)
sudo systemctl enable --now grub-btrfsd.service
# 重新生成 GRUB 配置
sudo grub-mkconfig -o /boot/grub/grub.cfg
# 重启后,GRUB 菜单会自动出现 "Arch Linux Snapshots" 子菜单
|
7.3 RHEL / CentOS 手动配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# RHEL 没有现成的 grub-btrfs 包
# 需要手动写 GRUB 入口或用脚本自动生成
# 方案一:每次创建快照后手动更新 GRUB
cat > /etc/grub.d/35_btrfs_snapshots << 'EOF'
#!/bin/sh
# 列出所有快照
SNAPSHOTS=$(btrfs subvolume list -s / | awk '{print $NF}')
for snap in $SNAPSHOTS; do
echo "menuentry 'Snapshot $snap' { ... }"
done
EOF
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
|
八、与备份集成:snapshot → 备份
8.1 完整备份方案
Snapper 快照虽然好用,但它和快照在同一个卷上。如果磁盘物理损坏,快照也救不了你。
1
2
3
4
5
|
灾难等级:
1. 软件故障(升级后系统崩)→ Snapper 快照能救
2. 误删文件 → Snapper 快照能救
3. 文件系统损坏 → Snapper 快照可能也坏
4. 磁盘物理损坏 → Snapper 快照丢失
|
真正的备份需要 send/receive 到另一块盘或远程服务器:
1
2
3
4
5
6
7
8
|
# 把根目录快照发送到外部磁盘
sudo btrfs send /.snapshots/5/snapshot | \
sudo btrfs receive /mnt/backup-disk/snapshots/
# 增量备份(只发送差异)
sudo btrfs send -p /.snapshots/5/snapshot \
-c /.snapshots/6/snapshot | \
sudo btrfs receive /mnt/backup-disk/snapshots/
|
8.2 Snapper + btrbk 自动备份
btrbk 是基于 btrfs send/receive 的专业备份工具,可以和 Snapper 协同:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 安装 btrbk
sudo dnf install btrbk # 或 apt install btrbk
# 配置 /etc/btrbk/btrbk.conf
cat > /etc/btrbk/btrbk.conf << 'EOF'
# 每小时快照保留 7 份
snapshot_dir /.snapshots
# 目标:本地备份盘
target /mnt/backup-disk/btrbk
target_send_compress zstd
EOF
# 启用定时器
sudo systemctl enable --now btrbk.timer
|
九、实战案例:完整的"时光机"工作流
9.1 场景一:服务器日常升级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 1. 升级前查看当前快照
sudo snapper list
# #5 pre - 2026-07-25 11:30:00 root number yum
# #6 post 5 2026-07-25 11:35:00 root number yum
# 2. 执行升级
sudo dnf update -y
# 自动创建 #7 (pre) 和 #8 (post)
# 3. 升级成功
sudo reboot
# 启动正常,升级完成
# 4. 一周后,删除旧快照
sudo snapper cleanup timeline
|
9.2 场景二:升级失败回滚
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 1. 升级后某个服务起不来
sudo dnf update -y openssl
sudo systemctl restart nginx
# Job for nginx.service failed because... OpenSSL 错误
# 2. 查看刚才的快照
sudo snapper list
# #9 pre - 升级前
# #10 post 9 - 升级后(当前问题)
# 3. 方法 A:撤销变更(无需重启,但风险较高)
sudo snapper undochange 9..10
# 4. 方法 B:回滚 + 重启(更安全)
sudo snapper rollback 9
sudo reboot
# 5. 重启后系统回到 #9 的状态
|
9.3 场景三:误删重要文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 1. 不小心删了 /etc/nginx/nginx.conf
sudo rm /etc/nginx/nginx.conf
sudo nginx -t
# nginx: [emerg] open() "/etc/nginx/nginx.conf" failed...
# 2. 查找最近的快照
sudo snapper list
# #11 timeline - 昨天 12:00 自动快照
# 3. 从快照恢复文件
sudo cp /.snapshots/11/snapshot/etc/nginx/nginx.conf /etc/nginx/
# 4. 验证
sudo nginx -t
# nginx: configuration file /etc/nginx/nginx.conf test is successful
|
9.4 场景四:磁盘空间被快照占满
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 1. df 命令发现磁盘快满了
df -h /
# /dev/sda2 100G 90G 10G 90% /
# 2. 查看快照占用
sudo btrfs filesystem du -s /.snapshots/*
# 输出每个快照占用的空间
# 3. 清理旧快照
sudo snapper cleanup timeline
# 4. 如果还不行,手动删除
sudo snapper delete 1-50
# 5. 立即释放空间(btrfs 不会自动回收 extent)
sudo btrfs balance start /
# 6. 验证
df -h /
# /dev/sda2 100G 60G 40G 60% /
|
十、常见问题与排查
10.1 “snapper list” 报 IO Error
1
2
3
4
5
|
# 原因:SUBVOLUME 配置错误
# 解决:检查 /etc/snapper/configs/root
sudo cat /etc/snapper/configs/root | grep SUBVOLUME
# 应该是 SUBVOLUME="/"
# 如果是 SUBVOLUME="/path/to/subvol",确认路径存在
|
10.2 snapper rollback 重启后还是新版本
1
2
3
4
5
6
7
|
# 原因:snapper rollback 需要 GRUB 配合
# 解决:检查 /etc/fstab
grep " / " /etc/fstab
# 应该是 subvol=@ 形式
# 重新生成 GRUB 配置
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
|
10.3 快照太多,磁盘爆了
1
2
3
4
5
6
7
8
|
# 查看所有快照占用
sudo btrfs qgroup show /
# 强制清理
sudo snapper delete $(sudo snapper list --columns number | tail -n +3)
# 平衡(回收已删除快照的空间)
sudo btrfs balance start -dusage=50 /
|
10.4 Snapper 钩子没生效(升级没创建快照)
1
2
3
4
5
6
7
8
9
10
|
# 检查钩子是否安装
rpm -qa | grep dnf-plugin-snapper
# 或
pacman -Q | grep snap-pac
# 检查 snapper 是否运行
sudo systemctl status snapperd
# 手动触发一次快照测试
sudo snapper create --description "测试快照"
|
10.5 Snapper 占用 CPU/IO 资源
1
2
3
4
5
6
7
8
|
# 限制 Snapper 的 I/O 优先级
# 在 /etc/snapper/configs/root 中添加:
BACKGROUND_COMPARISON="no" # 关闭后台对比
EMPTY_PRE_POST_CLEANUP="yes" # 清理空快照
# 减少快照数量
TIMELINE_LIMIT_HOURLY="3"
TIMELINE_LIMIT_DAILY="5"
|
十一、生产环境最佳实践
11.1 推荐的配置文件(生产服务器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# /etc/snapper/configs/root
SUBVOLUME="/"
FSTYPE="btrfs"
# 空间管理:保留 30% 给快照,超过自动清理
SPACE_LIMIT="0.3"
FREE_LIMIT="0.15"
# 时间线快照策略(精简版)
TIMELINE_CREATE="yes"
TIMELINE_LIMIT_HOURLY="3" # 每小时 3 个
TIMELINE_LIMIT_DAILY="5" # 每天 5 个
TIMELINE_LIMIT_WEEKLY="2" # 每周 2 个
TIMELINE_LIMIT_MONTHLY="3" # 每月 3 个
TIMELINE_LIMIT_YEARLY="0"
# 包管理器钩子清理
PRE_CLEANUP="yes"
POST_CLEANUP="yes"
|
11.2 监控脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#!/bin/bash
# /usr/local/bin/snapper-monitor.sh
# 监控 Snapper 状态,异常时报警
SNAP_COUNT=$(sudo snapper list --columns number | tail -n +3 | wc -l)
DISK_USAGE=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt 85 ]; then
echo "WARNING: 磁盘使用率 ${DISK_USAGE}%,快照可能占用了空间"
sudo snapper cleanup timeline
fi
if [ "$SNAP_COUNT" -gt 200 ]; then
echo "WARNING: 快照数量 $SNAP_COUNT 过多,建议清理"
fi
|
1
2
|
# 添加到 crontab,每小时检查
0 * * * * /usr/local/bin/snapper-monitor.sh
|
11.3 安全建议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
1. 定期验证快照可恢复
- 不要以为快照存在就能恢复
- 每月做一次实际恢复演练
2. 重要的快照远程备份
- btrfs send/receive 到远程机器
- 重要升级前的快照,单独备份
3. 不要在数据库服务运行时 undochange
- undochange 会改变正在运行的程序文件
- 可能导致数据库损坏
4. 监控快照空间
- 快照也可能撑爆磁盘
- 设置合理的 SPACE_LIMIT
5. 配合 systemd-tmpfiles 清理 /tmp
- /tmp 中的临时文件不参与快照
- 但 Snapper 不管理它
|
十二、与其他快照工具对比
| 工具 |
文件系统 |
集成方式 |
适合场景 |
| Snapper |
Btrfs |
包管理器钩子 |
服务器系统盘 |
| LVM 快照 |
LVM |
手动 |
老旧 LVM 系统 |
| Timeshift |
Btrfs / rsync |
手动 + 定时 |
桌面用户 |
| ZFS Auto-Snapshot |
ZFS |
cron |
FreeBSD / NAS |
| rsnapshot |
任意 |
cron + rsync |
通用备份 |
Snapper 的独特优势:与包管理器深度集成——其他工具都做不到"升级前自动快照"。
十三、未来展望:Snapper 与 Snapper 2.0
openSUSE 团队正在开发 Snapper 2.0,主要改进:
- 原生 ext4 支持(基于 metadata-level 快照)
- 改进的 LXD/LXC 集成
- 更好的 NFS/SMB 远程快照
- 配置文件格式升级
未来 Snapper 也许会扩展到非 Btrfs 文件系统,但目前 Btrfs + Snapper 仍是最佳组合。
结语
回顾整篇文章,我们搭建了一套完整的"服务器时光机":
1
2
3
4
5
6
7
8
9
10
11
12
|
核心组件:
- Btrfs 文件系统(COW 快照基础)
- Snapper 工具(快照管理器)
- snap-pac / dnf-plugin-snapper(包管理器钩子)
- grub-btrfs(GRUB 启动菜单集成)
- btrbk(远程备份,可选)
核心能力:
1. 升级前自动快照,升级失败一键回滚
2. 配置文件变更可追溯(snapper status / diff)
3. 误删文件从快照恢复
4. 配合 send/receive 做异地备份
|
从今天起,运维人员可以大胆地执行 yum update 了——反正下一秒就能回到过去。
Linux 生态从来不是"靠勇气运维"的系统,Btrfs + Snapper 让我们在追求效率的同时,也有了安全的底气。
运维不应该是孤独的冒险,而应该是有后盾的工程。Snapper,就是那个后盾。
相关推荐阅读: