いろいろなスナップショット技術を使ってみる
この世にはスナップショットという便利なものがあるらしいと耳にしました。
噂によれば以下のようなことができるようなのです。
- ある時点のファイルシステムやボリューム全体のコピーのようなものを高速に採取できる(「ある時点」の「全体の複製」が取れるので整合性を失わずにバックアップが取れる)
- スナップショットを作成してもディスクの使用量が倍になるわけではない。変更差分のみをいい感じに管理してくれる。
この記事では以下のスナップショット技術を使ってみました。
操作確認は基本的に Ubuntu 18.04 で行いました。ただし Stratis の検証だけは Fedora 29 で行いました。
LVM snapshot
以下のような状態で /dev/sdb1 を LVM で利用します。
shora@bionic:~$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 32G 0 disk ├─sda1 8:1 0 512M 0 part /boot/efi └─sda2 8:2 0 31.5G 0 part / sdb 8:16 0 32G 0 disk └─sdb1 8:17 0 32G 0 part shora@bionic:~$
/dev/sdb1 を LVM 用の物理ボリュームとして設定して、ボリュームグループ vg1 を構成します。
shora@bionic:~$ sudo pvcreate /dev/sdb1 Physical volume "/dev/sdb1" successfully created. shora@bionic:~$ sudo pvs PV VG Fmt Attr PSize PFree /dev/sdb1 lvm2 --- <32.00g <32.00g shora@bionic:~$ sudo vgcreate vg1 /dev/sdb1 Volume group "vg1" successfully created shora@bionic:~$ sudo vgs VG #PV #LV #SN Attr VSize VFree vg1 1 0 0 wz--n- <32.00g <32.00g shora@bionic:~$
vg1 から論理ボリュームを切り出します。容量は32GBの半分の16GBとしました。
shora@bionic:~$ sudo lvcreate -L 16G vg1 Logical volume "lvol0" created. shora@bionic:~$ sudo lvs LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert lvol0 vg1 -wi-a----- 16.00g shora@bionic:~$
切り出した論理ボリュームを XFS でフォーマットして /mnt/lvol0 にマウントします。
shora@bionic:~$ sudo mkfs.xfs /dev/vg1/lvol0 meta-data=/dev/vg1/lvol0 isize=512 agcount=4, agsize=1048576 blks = sectsz=4096 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=0, rmapbt=0, reflink=0 data = bsize=4096 blocks=4194304, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=1 log =internal log bsize=4096 blocks=2560, version=2 = sectsz=4096 sunit=1 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 shora@bionic:~$ sudo mkdir /mnt/lvol0 shora@bionic:~$ sudo mount /dev/vg1/lvol0 /mnt/lvol0 shora@bionic:~$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 32G 0 disk ├─sda1 8:1 0 512M 0 part /boot/efi └─sda2 8:2 0 31.5G 0 part / sdb 8:16 0 32G 0 disk └─sdb1 8:17 0 32G 0 part └─vg1-lvol0 253:0 0 16G 0 lvm /mnt/lvol0 shora@bionic:~$ df -Th Filesystem Type Size Used Avail Use% Mounted on (略) /dev/mapper/vg1-lvol0 xfs 16G 49M 16G 1% /mnt/lvol0 shora@bionic:~$
/mnt/lvol0 に適当にファイルを作ります。内容は hoge とします。
shora@bionic:/mnt/lvol0$ echo 'hoge' > hello.txt shora@bionic:/mnt/lvol0$ cat hello.txt hoge shora@bionic:/mnt/lvol0$ ls -la total 8 drwxr-xr-x 2 shora shora 23 Jan 4 16:05 . drwxr-xr-x 3 root root 4096 Jan 4 16:03 .. -rw-rw-r-- 1 shora shora 5 Jan 4 16:05 hello.txt shora@bionic:/mnt/lvol0$
スナップショットボリュームを作成します。スナップショット元のボリュームとの変更差分を保持する領域 (Copy on Write table) のサイズは1GBとします。
shora@bionic:/mnt/lvol0$ sudo lvcreate -L 1G -s /dev/vg1/lvol0 Using default stripesize 64.00 KiB. Logical volume "lvol1" created. shora@bionic:/mnt/lvol0$
作成された /dev/vg1/lvol0 は 16GB に見えますが、変更を加えることができる領域のサイズは 1GB です。
shora@bionic:/mnt/lvol0$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 32G 0 disk ├─sda1 8:1 0 512M 0 part /boot/efi └─sda2 8:2 0 31.5G 0 part / sdb 8:16 0 32G 0 disk └─sdb1 8:17 0 32G 0 part ├─vg1-lvol0-real 253:1 0 16G 0 lvm │ ├─vg1-lvol0 253:0 0 16G 0 lvm /mnt/lvol0 │ └─vg1-lvol1 253:3 0 16G 0 lvm └─vg1-lvol1-cow 253:2 0 1G 0 lvm └─vg1-lvol1 253:3 0 16G 0 lvm shora@bionic:/mnt/lvol0$ sudo lvdisplay --- Logical volume --- LV Path /dev/vg1/lvol0 LV Name lvol0 VG Name vg1 LV UUID wYki9r-0M1C-H1q6-KXYT-Vy9m-pGX0-yz3xId LV Write Access read/write LV Creation host, time bionic, 2019-01-04 15:57:09 +0000 LV snapshot status source of lvol1 [active] LV Status available # open 1 LV Size 16.00 GiB Current LE 4096 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:0 --- Logical volume --- LV Path /dev/vg1/lvol1 LV Name lvol1 VG Name vg1 LV UUID mssD5E-MxAU-W6Vt-TJHD-h5SL-GNFv-h3ykfs LV Write Access read/write LV Creation host, time bionic, 2019-01-04 16:07:29 +0000 LV snapshot status active destination for lvol0 LV Status available # open 0 LV Size 16.00 GiB Current LE 4096 COW-table size 1.00 GiB COW-table LE 256 Allocated to snapshot 0.01% Snapshot chunk size 4.00 KiB Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:3 shora@bionic:/mnt/lvol0$
作成されたスナップショットボリュームを /mnt/lvol1 にマウントします。"Filesystem has duplicate UUID" というエラーが dmesg に出力されたので -o nouuid
をつけてマウントしました。
shora@bionic:/mnt/lvol0$ sudo mkdir /mnt/lvol1 shora@bionic:/mnt/lvol0$ sudo mount -o nouuid /dev/vg1/lvol1 /mnt/lvol1 shora@bionic:/mnt/lvol0$
スナップショット元のボリュームの内容を fuga に変更しても、スナップショットボリュームにはスナップショット採取時の内容の hoge が保持されています。
shora@bionic:/mnt$ echo 'fuga' > lvol0/hello.txt shora@bionic:/mnt$ cat lvol0/hello.txt fuga shora@bionic:/mnt$ cat lvol1/hello.txt hoge shora@bionic:/mnt$
LVM snapshot の仕組みは以下のページが分かりやすかったです。
https://www.clevernetsystems.com/lvm-snapshots-explained/
LVM thin provisioning
LVM snapshot と LVM thin provisioning の違いは以下のドキュメントにまとめられています。
Red Hat Enterprise Linux 7 2.3. LVM 論理ボリューム - Red Hat Customer Portal
ボリュームグループ vg1 を用意するところまでは同じなので省略します。
thin-provisioning-tools が必要だったので入れました。
shora@bionic:~$ sudo apt install thin-provisioning-tools (略)
lvcreate に -T オプションをつけて thin pool を作成します。加えて -V オプションで thin volume もまとめて作成します。
thin provisioning では thin volume のサイズは thin pool よりも大きく設定することができます。もちろん thin pool よりも大きなデータを書き込もうとするとエラーが出て失敗します。
shora@bionic:~$ sudo lvcreate -T -V 16G -L 16G vg1 Using default stripesize 64.00 KiB. Thin pool volume with chunk size 64.00 KiB can address at most 15.81 TiB of data. Logical volume "lvol2" created. shora@bionic:~$ sudo lvs LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert lvol1 vg1 twi-aotz-- 16.00g 0.00 0.63 lvol2 vg1 Vwi-a-tz-- 16.00g lvol1 0.00 shora@bionic:~$ sudo lvdisplay --- Logical volume --- LV Name lvol1 VG Name vg1 LV UUID 3f2lqs-1UmW-XScV-QCv9-q2fg-OG2w-HKctT2 LV Write Access read/write LV Creation host, time bionic, 2019-01-05 06:50:24 +0000 LV Pool metadata lvol1_tmeta LV Pool data lvol1_tdata LV Status available # open 2 LV Size 16.00 GiB Allocated pool data 0.00% Allocated metadata 0.63% Current LE 4096 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:2 --- Logical volume --- LV Path /dev/vg1/lvol2 LV Name lvol2 VG Name vg1 LV UUID 3v0ueh-2SlV-G5h1-x5Rm-b5Du-zYCX-W6bdvc LV Write Access read/write LV Creation host, time bionic, 2019-01-05 06:50:24 +0000 LV Pool name lvol1 LV Status available # open 0 LV Size 16.00 GiB Mapped size 0.00% Current LE 4096 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:4 shora@bionic:~$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT loop0 7:0 0 89.5M 1 loop /snap/core/6130 loop1 7:1 0 86.9M 1 loop /snap/core/4917 sda 8:0 0 32G 0 disk ├─sda1 8:1 0 512M 0 part /boot/efi └─sda2 8:2 0 31.5G 0 part / sdb 8:16 0 32G 0 disk └─sdb1 8:17 0 32G 0 part ├─vg1-lvol1_tmeta 253:0 0 16M 0 lvm │ └─vg1-lvol1-tpool 253:2 0 16G 0 lvm │ ├─vg1-lvol1 253:3 0 16G 0 lvm │ └─vg1-lvol2 253:4 0 16G 0 lvm └─vg1-lvol1_tdata 253:1 0 16G 0 lvm └─vg1-lvol1-tpool 253:2 0 16G 0 lvm ├─vg1-lvol1 253:3 0 16G 0 lvm └─vg1-lvol2 253:4 0 16G 0 lvm shora@bionic:~$
XFS でフォーマットした後に /mnt/lvol2 にマウントして、適当なファイルを作ります。内容は hoge とします。
shora@bionic:/mnt$ sudo mkdir lvol2 shora@bionic:/mnt$ sudo mount /dev/vg1/lvol2 lvol2 shora@bionic:/mnt$ echo 'hoge' > lvol2/hello.txt shora@bionic:/mnt$
スナップショットボリュームを作成します。lvcreate -s /dev/vg1/lvol2
で作成することもできるのですが、以下のドキュメントに記載の通り、デフォルトでは activation がスキップされるようになっているので、-kn オプションをつけて activation スキップフラグを削除します。
Red Hat Enterprise Linux 7 4.4. 論理ボリュームの管理 - Red Hat Customer Portal
shora@bionic:/mnt/lvol2$ sudo lvcreate -s -kn vg1/lvol2 Using default stripesize 64.00 KiB. WARNING: Sum of all thin volume sizes (32.00 GiB) exceeds the size of thin pool vg1/lvol1 and the size of whole volume group (<32.00 GiB). WARNING: You have not turned on protection against thin pools running out of space. WARNING: Set activation/thin_pool_autoextend_threshold below 100 to trigger automatic extension of thin pools before they get full. Logical volume "lvol3" created. shora@bionic:/mnt/lvol2$
スナップショットボリュームは /mnt/lvol3 にマウントしました。スナップショット元のボリュームの内容を fuga に変更しても、スナップショットボリュームにはスナップショット採取時の内容の hoge が保持されています。
shora@bionic:/mnt$ echo 'fuga' > lvol2/hello.txt shora@bionic:/mnt$ cat lvol2/hello.txt fuga shora@bionic:/mnt$ cat lvol3/hello.txt hoge shora@bionic:/mnt$
Btrfs
btrfs-progs が必要だったので入れました。
shora@bionic:~$ sudo apt install btrfs-progs (略)
/dev/sdc1 を Btrfs でフォーマットします。
shora@bionic:~$ sudo mkfs.btrfs /dev/sdc1 btrfs-progs v4.15.1 See http://btrfs.wiki.kernel.org for more information. Performing full device TRIM /dev/sdc1 (32.00GiB) ... Label: (null) UUID: 52efd491-a8de-45b1-a41c-71e901c8a711 Node size: 16384 Sector size: 4096 Filesystem size: 32.00GiB Block group profiles: Data: single 8.00MiB Metadata: DUP 1.00GiB System: DUP 8.00MiB SSD detected: no Incompat features: extref, skinny-metadata Number of devices: 1 Devices: ID SIZE PATH 1 32.00GiB /dev/sdc1 shora@bionic:~$
/mnt/btrfs にマウントして適当なファイルを作ります。内容は hoge とします。
shora@bionic:/mnt/btrfs$ echo 'hoge' > hello.txt
スナップショットサブボリュームを作成します。スナップショットサブボリュームは Btrfs ボリュームの中にしか作ることができないので /mnt/btrfs/snap に作成しました。
shora@bionic:/mnt/btrfs$ sudo btrfs subvolume snapshot /mnt/btrfs /mnt/btrfs/snap Create a snapshot of '/mnt/btrfs' in '/mnt/btrfs/snap' shora@bionic:/mnt/btrfs$
スナップショット元の内容を fuga に変更しても、スナップショットサブボリュームにはスナップショット採取時の内容の hoge が保持されています。
shora@bionic:/mnt/btrfs$ echo 'fuga' > hello.txt shora@bionic:/mnt/btrfs$ cat hello.txt fuga shora@bionic:/mnt/btrfs$ cat snap/hello.txt hoge shora@bionic:/mnt/btrfs$
ちなみに、Btrfs ボリュームのトップレベルのスナップショットを作成すると、スナップショット元のボリュームの中にスナップショットサブボリュームが作成されて不思議な感じがします。
これを回避するために Btrfs ボリュームの直下にはサブボリュームしか作成しないようにして、LVM における thin-pool のように扱う運用方法も提案されています。
前述のように「スナップショットサブボリュームは Btrfs ボリュームの中にしか作ることができない」という制約がありましたが、Btrfs のマウント時には -o subvol=name
というオプションでサブボリュームをマウントすることができるので、上記の作法に従っていれば、あたかも Btrfs ボリュームの外にスナップショットサブボリュームを作ったかのようなことも実現できます。
ZFS on Linux
最近は ZFS on Linux も手軽に使えるようになっているようなので利用してみました。
Ubuntu では zfsutils-linux をインストールするだけで使えます。
shora@bionic:~$ sudo apt install -y zfsutils-linux
最初にストレージプールを作成します。LVM のボリュームグループのようなものです。ストレージプールの名前には慣例的に tank がよく使われるようなのでそれに従いました*1。
ZFS ではパーティションではなくディスク全体を ZFS 化することを推奨しているので、その作法に従います。ストレージプールを作成する際のディスクの指定方法ですが、/dev/sd* で指定する方法はテスト用途以外では推奨しないような書きぶりだったため /dev/disk/by-id/ で得られる disk id で指定する方法を使いました。
FAQ · zfsonlinux/zfs Wiki · GitHub
shora@bionic:~$ ls -l /dev/disk/by-id/ total 0 lrwxrwxrwx 1 root root 9 Jan 6 05:39 scsi-3600224804bfce04d82ef79cd4cb60d40 -> ../../sdd lrwxrwxrwx 1 root root 9 Jan 6 05:39 wwn-0x600224804bfce04d82ef79cd4cb60d40 -> ../../sdd (略) shora@bionic:~$ sudo zpool create tank scsi-3600224804bfce04d82ef79cd4cb60d40 shora@bionic:~$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 32G 0 disk ├─sda1 8:1 0 512M 0 part /boot/efi └─sda2 8:2 0 31.5G 0 part / sdd 8:48 0 32G 0 disk ├─sdd1 8:49 0 32G 0 part └─sdd9 8:57 0 8M 0 part shora@bionic:~$ sudo zpool status pool: tank state: ONLINE scan: none requested config: NAME STATE READ WRITE CKSUM tank ONLINE 0 0 0 scsi-3600224804bfce04d82ef79cd4cb60d40 ONLINE 0 0 0 errors: No known data errors shora@bionic:~$
最近の Linux ではマウントオプションに relatime(ファイルの最終アクセス時刻が1日以内であれば atime を更新しない)が付与されるようにカーネルのデフォルト設定が行われているのですが、ZFS は関係ないようで relatime が付与されていなかったため設定を変更しました。
shora@bionic:~$ mount (略) tank on /tank type zfs (rw,xattr,noacl) shora@bionic:~$ sudo zfs set relatime=on tank shora@bionic:~$ mount (略) tank on /tank type zfs (rw,relatime,xattr,noacl) shora@bionic:~$
ストレージプールからファイルシステム(Btrfsでいうサブボリュームのようなもの)を切り出します。名前は zol として /mnt/zol にマウントします。
shora@bionic:~$ sudo zfs create -o mountpoint=/mnt/zol tank/zol shora@bionic:~$ zfs list NAME USED AVAIL REFER MOUNTPOINT tank 432K 30.8G 96K /tank tank/zol 96K 30.8G 96K /mnt/zol shora@bionic:~$ mount (略) tank/zol on /mnt/zol type zfs (rw,relatime,xattr,noacl) shora@bionic:~$
適当なファイルを作ります。内容は hoge とします。
shora@bionic:/mnt/zol$ echo 'hoge' > hello.txt shora@bionic:/mnt/zol$
zol のスナップショット snap を作成して /mnt/zol-snap にマウントします。マウント時には -t zfs でファイルシステムを指定してやる必要がありました。
shora@bionic:/mnt$ sudo zfs snapshot tank/zol@snap shora@bionic:/mnt$ zfs list -t snapshot NAME USED AVAIL REFER MOUNTPOINT tank/zol@snap 0B - 100K - shora@bionic:/mnt$ sudo mkdir /mnt/zol-snap shora@bionic:/mnt$ sudo mount -t zfs tank/zol@snap /mnt/zol-snap shora@bionic:/mnt$
スナップショット元のファイルシステムの内容を fuga に変更しても、スナップショットには採取時の内容の hoge が保持されています。
shora@bionic:/mnt$ echo 'fuga' > zol/hello.txt shora@bionic:/mnt$ cat zol/hello.txt fuga shora@bionic:/mnt$ cat zol-snap/hello.txt hoge shora@bionic:/mnt$
Stratis
RedHat が開発中のもので、2018年9月に1.0がリリースされました。Fedora 29 では簡単にインストールできます。
今回試したバージョンは以下の通りです。
[root@localhost ~]# stratis --version 1.0.0 [root@localhost ~]# stratis daemon version 1.0.2 [root@localhost ~]#
pool を作成します。名前は tank とします。ディスク全体を利用することが必須となっているようで、過去にファイルシステムを作成したことがある場合には wipefs でシグネチャを削除することも求められているようでした(詳しくは公式ドキュメント参照)。
[root@localhost ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 32G 0 disk ├─sda1 8:1 0 200M 0 part /boot/efi ├─sda2 8:2 0 1G 0 part /boot └─sda3 8:3 0 30.8G 0 part ├─fedora-root 253:0 0 15G 0 lvm / └─fedora-swap 253:1 0 2G 0 lvm [SWAP] sdb 8:16 0 32G 0 disk [root@localhost ~]# stratis pool create tank /dev/sdb [root@localhost ~]# stratis pool list Name Total Physical Size Total Physical Used tank 32 GiB 68 MiB [root@localhost ~]#
tank 上にファイルシステムを作成します。名前は fs としました。シンプロビジョニングされているようで、ディスクの容量は32GBしかないのに1TBのファイルシステムが作成されていました。
[root@localhost ~]# stratis filesystem create tank fs [root@localhost ~]# stratis filesystem list Pool Name Name Used Created Device UUID tank fs 546 MiB Jan 12 2019 11:05 /stratis/tank/fs 8a42bd174918452db53f03940fb915a8 [root@localhost ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 32G 0 disk ├─sda1 8:1 0 200M 0 part /boot/efi ├─sda2 8:2 0 1G 0 part /boot └─sda3 8:3 0 30.8G 0 part ├─fedora-root 253:0 0 15G 0 lvm / └─fedora-swap 253:1 0 2G 0 lvm [SWAP] sdb 8:16 0 32G 0 disk └─stratis-1-private-6185bbf5fd9e4770acc07296b7a5973b-physical-originsub 253:2 0 32G 0 stratis ├─stratis-1-private-6185bbf5fd9e4770acc07296b7a5973b-flex-thinmeta 253:3 0 32M 0 stratis │ └─stratis-1-private-6185bbf5fd9e4770acc07296b7a5973b-thinpool-pool 253:6 0 32G 0 stratis │ └─stratis-1-6185bbf5fd9e4770acc07296b7a5973b-thin-fs-8a42bd174918452db53f03940fb915a8 253:7 0 1T 0 stratis ├─stratis-1-private-6185bbf5fd9e4770acc07296b7a5973b-flex-thindata 253:4 0 32G 0 stratis │ └─stratis-1-private-6185bbf5fd9e4770acc07296b7a5973b-thinpool-pool 253:6 0 32G 0 stratis │ └─stratis-1-6185bbf5fd9e4770acc07296b7a5973b-thin-fs-8a42bd174918452db53f03940fb915a8 253:7 0 1T 0 stratis └─stratis-1-private-6185bbf5fd9e4770acc07296b7a5973b-flex-mdv 253:5 0 16M 0 stratis [root@localhost ~]#
作成したファイルシステムを /mnt/stratis にマウントします。
[root@localhost ~]# mkdir /mnt/stratis [root@localhost ~]# mount /stratis/tank/fs /mnt/stratis/ [root@localhost ~]# df -Th Filesystem Type Size Used Avail Use% Mounted on (略) /dev/mapper/stratis-1-6185bbf5fd9e4770acc07296b7a5973b-thin-fs-8a42bd174918452db53f03940fb915a8 xfs 1.0T 1.1G 1023G 1% /mnt/stratis [root@localhost ~]# mount (略) /dev/mapper/stratis-1-6185bbf5fd9e4770acc07296b7a5973b-thin-fs-8a42bd174918452db53f03940fb915a8 on /mnt/stratis type xfs (rw,relatime,seclabel,attr2,inode64,sunit=1024,swidth=2048,noquota) [root@localhost ~]#
適当なファイルを作ります。内容は hoge とします。
[root@localhost mnt]# echo 'hoge' > stratis/hello.txt
スナップショット作成して /mnt/snap_stratis にマウントします。
[root@localhost mnt]# stratis filesystem snapshot tank fs snap_fs [root@localhost mnt]# stratis filesystem list Pool Name Name Used Created Device UUID tank fs 546 MiB Jan 12 2019 11:05 /stratis/tank/fs 8a42bd174918452db53f03940fb915a8 tank snap_fs 546 MiB Jan 12 2019 11:12 /stratis/tank/snap_fs 5565e1960a924d3b932833ccc8d97d5c [root@localhost mnt]# mkdir snap_stratis [root@localhost mnt]# mount /stratis/tank/snap_fs snap_stratis
スナップショット元のファイルシステムの内容を fuga に変更しても、スナップショットには採取時の内容の hoge が保持されています。
[root@localhost mnt]# echo 'fuga' > stratis/hello.txt [root@localhost mnt]# cat stratis/hello.txt fuga [root@localhost mnt]# cat snap_stratis/hello.txt hoge [root@localhost mnt]#