当前位置:首页>Linux>Linux zfs 快照与克隆:瞬间创建的魔法

Linux zfs 快照与克隆:瞬间创建的魔法

  • 2026-09-18 16:00:54
Linux zfs 快照与克隆:瞬间创建的魔法

字数 1605,阅读大约需 9 分钟

ZFS 快照是 ZFS 最"魔幻"的特性之一——几乎不占空间、瞬间创建,比 LVM 快照好用十倍。本篇讲清原理、用法、自动化和最佳实践。

一、快照是什么?

ZFS 快照 = dataset 在某时刻的只读副本。

tank/data
    ├── 原始 block: 100, 200, 300
    │
    ├── 快照@before:
    │   └── 共享原始 block(指针共享)
    │
    └── 修改后(覆盖 block 200):
        ├── 新 block 200' 在位置 500
        └── 旧 block 200 仍在位置 200(被 @before 引用)

关键特性:

  • • 创建快照 = 瞬间(O(1) 操作)
  • • 快照本身几乎不占空间(只记录指针变更)
  • • 快照 = 只读
  • • 可以无限创建

二、创建快照

2.1 基本用法

# 创建快照,命名为 snap1
$ zfs snapshot tank/data@snap1

# 用自定义时间

$ zfs snapshot tank/data@2024-01-15
$ zfs snapshot tank/data@2024-01-15-0300

# 一次拍多个 dataset

$ zfs snapshot tank/home/alice@daily
$ zfs snapshot tank/home/bob@daily

2.2 看快照

# 列出所有快照
$ zfs list -t snapshot
NAME                       USED  AVAIL  REFER  MOUNTPOINT
tank/data@snap1             8K      -   100M  -
tank/home/alice@daily      16K      -   1.2G  -
tank/home/bob@daily         8K      -   800M  -

# 看某个 dataset 的所有快照

$ zfs list -t snapshot -r tank/data

# 详细信息

$ zfs get all tank/data@snap1

USED 字段是快照本身占的空间(COW 复制的数据)。

2.3 访问快照数据

ZFS 默认隐藏快照(通过 .zfs 隐藏目录),但可以访问:

# 列出隐藏目录
$ ls -la /tank/data/.zfs
dr-xr-xr-x  ... snapshot/

# 访问快照数据

$ ls /tank/data/.zfs/snapshot/snap1/
file1.txt  file2.txt  ...

或者用 ZFS 命令访问:

# 克隆(见下文)
$ zfs clone tank/data@snap1 tank/data-snap1

# 发送

$ zfs send tank/data@snap1

2.4 启用可见的 .zfs 目录

$ zfs set snapdir=visible tank/data

# 之后 /tank/data/.zfs/snapshot/ 可以直接 ls/cd

三、克隆(Clone)

克隆 = 从快照创建的可写 dataset。

# 1. 创建快照
$ zfs snapshot tank/data@snap1

# 2. 从快照克隆

$ zfs clone tank/data@snap1 tank/data-clone

# 3. 克隆可以独立修改

$ echo "new" > /tank/data-clone/file.txt

# 4. 源 dataset 不受影响

$ cat /tank/data/file.txt
(原始内容)

3.1 克隆的注意事项

  • • 克隆不是独立 dataset,是依赖原快照的
  • • 克隆本质上是写时复制:未修改的 block 仍引用原快照
  • • 删除原快照前必须先 promote 克隆(见下文)
  • • 克隆可以挂在原 dataset 路径下(canmount=noauto)

3.2 promote 克隆

把克隆"提升"为独立 dataset:

# 把 tank/data-clone 提升为顶层
$ zfs promote tank/data-clone

# 之后可以删除原 tank/data@snap1

$ zfs destroy tank/data@snap1

重要:删除有克隆的快照,不会删除数据,但会保留这个依赖关系。
promote 把依赖翻转:原 dataset 反过来成为克隆。

四、销毁快照

# 销毁单个快照
$ zfs destroy tank/data@snap1

# 销毁所有快照

$ zfs destroy tank/data@%

# 递归销毁(含子 dataset 的所有快照)

$ zfs destroy -r tank/data@%

警告:删除快照后,当前 dataset 中"被快照引用"的旧版本数据立即被回收。

五、快照与性能

5.1 性能影响

  • • 快照几乎不占额外 I/O
  • • 修改 dataset 时,被快照覆盖的 block会先复制到快照空间(COW)
普通 dataset 写:
  block 100 → 新位置 500(一次性写)

有快照的 dataset 写:
  block 100 → 复制 100 到快照空间
              写新数据到 500
              两次写操作

所以:大量快照 + 大量写入会拖慢性能。

5.2 监控

# 看快照占用
$ zfs list -t snapshot -o name,used,referenced

# 看 dataset + 快照总占用

$ zfs list -o name,used,usedbychildren,usedbydatasets,usedbysnapshots

六、自动快照

6.1 手工 cron 方案

# 每天凌晨 3 点拍快照,保留 30 天
$ cat /etc/cron.d/zfs-snapshot
0 3 * * * root /usr/sbin/zfs snapshot -r tank@auto-$(date +\%Y\%m\%d)
30 3 * * * root /usr/sbin/zfs destroy -r tank@auto-$(date -d '30 days ago' +\%Y\%m\%d)

6.2 高级工具:zfs-auto-snapshot

# 安装(Debian/Ubuntu)
$ apt install zfs-auto-snapshot

# 启动

$ systemctl enable --now zfs-auto-snapshot.timer

# 配置(/etc/zfs-auto-snapshot.conf)

# 默认:每 15 分钟拍快照,保留 N 天


# 手动拍

$ zfs-auto-snapshot --verbose --keep=10 tank/data

6.3 高级工具:sanoid + syncoid(强烈推荐)

sanoid 是 ZFS 自动快照的"瑞士军刀":

# 安装
$ apt install sanoid

# 配置文件 /etc/sanoid/sanoid.conf

[tank/data]
    use_template = production
    recursive = yes

[tank/home]
    use_template = production

[template_production]
    frequent = 4
    hourly = 24
    daily = 30
    monthly = 6
    yearly = 1
    autosnap = yes
    autoprune = yes

sanoid 拍快照,syncoid 同步到另一台机器(zfs send/recv 包装)。

# 同步 tank/data 到 backup 机器
$ syncoid tank/data backup:backup/data

# 增量同步

$ syncoid tank/data backup:backup/data

七、实战:快照备份策略

7.1 "3-2-1" 备份原则

  • • 3 份数据副本
  • • 2 种不同存储介质
  • • 1 份在异地

ZFS 天然支持:

# 1 份:主 pool
# 1 份:本地备份 pool(不同盘)

# 1 份:远程 backup 机器(zfs send/recv)

7.2 自动备份脚本

#!/bin/bash
# /usr/local/bin/zfs-backup.sh


SOURCE=tank/data
TARGET=backup-machine:backup/data
SNAP=auto-$(date +%Y%m%d-%H%M%S)

# 1. 拍快照

zfs snapshot $SOURCE@$SNAP

# 2. 同步到远程(增量)

syncoid --no-sync-snap $SOURCE $TARGET

# 3. 清理 7 天前快照

zfs destroy $SOURCE@$(date -d '7 days ago' +%Y%m%d-%H%M%S)

八、快照常见问题

1. "dataset is busy"

# 通常是 dataset 还在被访问
$ lsof | grep tank/data
# 或

$ fuser /tank/data

2. "cannot destroy snapshot: has dependent clones"

# 必须先 promote 克隆或删除克隆
$ zfs list -t snapshot
$ zfs list -o clones tank/data@snap1
# NAME                   ...

# tank/data-clone@origin


# 选项 1:删除克隆

$ zfs destroy tank/data-clone

# 选项 2:promote 克隆(推荐)

$ zfs promote tank/data-clone
$ zfs destroy tank/data@snap1

3. 快照占满空间

# 看占用
$ zfs list -t snapshot -o name,used

# 删旧的

$ zfs destroy tank/data@old-snap

# 调整快照保留策略

$ vim /etc/sanoid/sanoid.conf

4. .zfs/snapshot 不可见

$ zfs set snapdir=visible tank/data

但修改 .zfs 目录的内容是不允许的(只读)。

九、快照的高级玩法

9.1 一次性可写副本

# 想做"基于生产的临时测试环境":
$ zfs snapshot tank/db@now
$ zfs clone tank/db@now tank/db-test
$ zfs set mountpoint=/var/lib/pgsql-test tank/db-test

# 之后随便改,不影响生产


# 改完废弃

$ zfs destroy tank/db-test
$ zfs destroy tank/db@now

9.2 容器镜像

# 基础镜像
$ zfs create tank/images/ubuntu-base
# 安装软件...


# 给每个容器做 snapshot + clone

$ zfs snapshot tank/images/ubuntu-base@v1
$ zfs clone tank/images/ubuntu-base@v1 tank/images/ubuntu-container-001
$ zfs clone tank/images/ubuntu-base@v1 tank/images/ubuntu-container-002

# 每个 container 独立可写,但共享 base block(极省空间)

9.3 数据库升级前快照

$ zfs snapshot tank/db@before-pg-upgrade
# 升级 PG

$ pg_upgradecluster ...
# 失败?回滚:

$ zfs rollback tank/db@before-pg-upgrade

rollback 是另一个"魔法"操作,后续详细介绍。

十、本篇小结

操作
命令
创建快照
zfs snapshot tank/data@snap1
看快照
zfs list -t snapshot
访问数据
/tank/data/.zfs/snapshot/snap1/
创建克隆
zfs clone tank/data@snap1 tank/data-clone
提升克隆
zfs promote tank/data-clone
删快照
zfs destroy tank/data@snap1
删所有
zfs destroy tank/data@%

核心要点:

  • • 快照几乎免费——不要怕多拍
  • • 用 sanoid / syncoid 自动化
  • • 克隆 = 可写的"时光机"
  • • 3-2-1 备份原则 + 异地同步

最新文章

随机文章