🐙

Ubuntu 22.04で外部ストレージをBtrfsにしてスナップショットを活用する

2023/02/01に公開

はじめに

SynologyのNASをずっと使い続けていて、その中のDockerでマイクラサーバのコンテナを立ち上げて遊んでいます。

NASにはスナップショットの機能があり、毎時0分にマイクラのワールドデータのスナップショットを取る仕組みを設定しています。

なにかマイクラワールドで「やっちゃった」時でも最悪0分~59分前のデータに戻せる安心感があるため、必須機能と化しています。

ただし、うちのSynology NASは他にも仮想マシン、監視カメラの録画、クライアントPCバックアップ、別拠点とのファイル同期など多様な用途で利用しているため負荷が半端ない感じです。

Ubuntu 22.04 ServerにDocker環境を移行させていくなかで、スナップショット機能が簡単に使えるBtrfs+snapperを外付けストレージに導入したので、その経過メモを投稿します。

物理ストレージ

以下の一覧で、sda, sdc, sddが今回追加するストレージです。

sdbはSATA接続のOS領域が入ったSSDですが、今回は対象外です(ext4のまま)。

$ lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
(snip)
sda      8:0    0 931.5G  0 disk 
└─sda1   8:1    0 931.5G  0 part 
sdb      8:16   0 223.6G  0 disk 
├─sdb1   8:17   0     1G  0 part /boot/efi
└─sdb2   8:18   0 222.5G  0 part /
sdc      8:32   0 931.5G  0 disk 
└─sdc1   8:33   0 931.5G  0 part 
sdd      8:48   0 931.5G  0 disk 
└─sdd1   8:49   0 931.5G  0 part 
  • sda
    • 内蔵HDD(1TB)
    • SATA3
  • sdc
    • 外付HDD(1TB)
    • USB3
  • sdd
    • 外付HDD(1TB)
    • USB3

各ストレージはfdiskを使って事前にパーティション1をフルサイズで確保してあります。

論理ボリューム

内蔵HDD「/dev/sda1」はRAID構成を組まずに単独でBtrfsフォーマットします。

外付HDD「/dev/sdc1」「/dev/sdd1」は2台でRAID1としてBtrfsフォーマットします。

それぞれ-Lオプションでマウント時のラベル名を指定しました。

コマンド例
sudo mkfs.btrfs -L HDDSINGLE /dev/sda1
sudo mkfs.btrfs -L HDDRAID -d raid1 /dev/sdc1 /dev/sdd1

論理ボリュームの作成結果は以下です。「HDDRAID」はちゃんと2台の物理ストレージが利用されていますね。

実行結果
$ sudo btrfs filesystem show
Label: 'HDDRAID'  uuid: 309e6336-40f4-442a-98f8-deffe99cf92d
        Total devices 2 FS bytes used 224.00KiB
        devid    1 size 931.51GiB used 2.01GiB path /dev/sdc1
        devid    2 size 931.51GiB used 2.01GiB path /dev/sdd1

Label: 'HDDSINGLE'  uuid: 50613f1e-3405-4ea8-9482-862197730b64
        Total devices 1 FS bytes used 192.00KiB
        devid    1 size 931.51GiB used 2.02GiB path /dev/sda1

マウント

Btrfsはサブボリューム(subvolume)の考え方があり、各ボリューム(/dev/sda1など)の中に小ボリュームが作成できます。

実際に「/mnt」配下にマウントするのは後から作るsubvolumeを指定しますが、まずは親ボリュームをマウントします。

まずはマウントポイントを作成。

コマンド例
sudo mkdir /volume1
sudo mkdir /volume2

/etc/fstabに以下の行を追加します。

「/volume1」は非RAIDの内蔵HDD、「/volume2」はRAID1の外付HDD、をマウントします。

マウントオプションはディスク性能を高められるかなと考え指定していますが、正直今回の用途に合致するのかは不明です。

/etc/fstab
/dev/disk/by-label/HDDSINGLE /volume1 btrfs noatime,compress-force=lzo,space_cache=v2 0 0
/dev/disk/by-label/HDDRAID   /volume2 btrfs noatime,compress-force=lzo,space_cache=v2 0 0

手動でマウントします。

コマンド例
sudo mount /volume1
sudo mount /volume2

実際には「/mnt/temp」を一時的に利用する予定のファイル置き場、「/mnt/data」を自動スナップショットもあるRAID1の安心領域にしたいと考えます。

各subvolumeを作成します。ディレクトリ名はsubvolumeと判別しやすいように「@」を先頭に付与しました。

コマンド例
sudo btrfs subvolume create /volume1/@temp
sudo btrfs subvolume create /volume2/@data
sudo mkdir -p /mnt/temp
sudo mkdir -p /mnt/data
実行結果
$ sudo btrfs subvolume create /volume1/@temp
Create subvolume '/volume1/@temp'
$ sudo btrfs subvolume create /volume2/@data
Create subvolume '/volume2/@data'
$ sudo mkdir -p /mnt/temp
$ sudo mkdir -p /mnt/data

/etc/fstabに以下の行を追加します。先ほどの分も合わせて今回は合計4行の追加となります。

先ほどと異なるのが、マウントオプションに「subvol=/@temp」などとsubvolumeが指定されているところです。

/etc/fstab
/dev/disk/by-label/HDDSINGLE /mnt/temp btrfs subvol=/@temp,noatime,compress-force=lzo,space_cache=v2 0 0
/dev/disk/by-label/HDDRAID   /mnt/data btrfs subvol=/@data,noatime,compress-force=lzo,space_cache=v2 0 0

手動でマウントします。

コマンド例
sudo mount /mnt/temp
sudo mount /mnt/data

結果マウント状態は以下になります。

実行結果
$ df
Filesystem                        1K-blocks       Used  Available Use% Mounted on
(snip)
/dev/sda1                         976761560       3648  974646976   1% /volume1
/dev/sdc1                         976761560       3536  975703744   1% /volume2
/dev/sda1                         976761560       3648  974646976   1% /mnt/temp
/dev/sdc1                         976761560       3536  975703744   1% /mnt/data

btrfsコマンドによるスナップショットの確認

まずは純粋にbtrfsコマンドを覚える意味でも直接スナップショット関連のコマンドを叩いて動作を確認します。

毎回sudoするのも面倒なので、sudoグループに属している人(自分)はアクセスできるように権限を変更します。

実行例
sudo chgrp sudo /mnt/data
sudo chgrp sudo /mnt/temp
sudo chmod g+w /mnt/data
sudo chmod g+w /mnt/temp

/mnt/data配下にサンプルとして大事なファイルを作成します。

実行例
$ cat /mnt/data/daiji-file.txt 
これはとっても大事なファイル
↓これを消してはいけない
ほげほげ

以下のコマンドでスナップショットの作成~削除までの流れを確認しました。

実行例
# スナップショットボリューム用のディレクトリを作成
$ sudo mkdir -p /volume2/@snapshot/@data

# スナップショット作成
$ sudo btrfs subvolume snapshot -r /volume2/@data /volume2/@snapshot/@data/20230201-test
Create a snapshot of '/volume2/@data' in '/volume2/@snapshot/@data/20230201-test'

# subvolumeの一覧表示
$ sudo btrfs subvolume list /volume2
ID 257 gen 97 top level 5 path @data
ID 261 gen 97 top level 5 path @snapshot/@data/20230201-test

# 元の大事なファイルの3行目以降を削除
$ sed -i -n 1,2p /mnt/data/daiji-file.txt 
$ cat /mnt/data/daiji-file.txt 
これはとっても大事なファイル
↓これを消してはいけない

# 先のスナップショット内のファイルは無事な事を確認
$ cat /volume2/@snapshot/@data/20230201-test/daiji-file.txt 
これはとっても大事なファイル
↓これを消してはいけない
ほげほげ

# スナップショット内からファイルをコピーで復元する
$ cp -p /volume2/@snapshot/@data/20230201-test/daiji-file.txt /mnt/data/.
$ cat /mnt/data/daiji-file.txt 
これはとっても大事なファイル
↓これを消してはいけない
ほげほげ

# スナップショットのsubvolumeを削除
$ sudo btrfs subvolume delete /volume2/@snapshot/@data/20230201-test
Delete subvolume (no-commit): '/volume2/@snapshot/@data/20230201-test'

snapper

初期設定

前章にあるスナップショット操作を簡単に定期実行してくれるツールが「snapper」です。

まずはパッケージの追加インストール。

実行例
sudo apt update
sudo apt install snapper -y

「/mnt/data」でもいいのですが、親ボリューム「/volume2/@data」に関するconfigファイルを作成。

実行例
sudo snapper -c data-volume create-config -f btrfs /volume2/@data

こちらを実行すると「/etc/snapper/configs」配下に「data-volume」が作成されます。

中身ですが、今回は特に変更していません。

デフォルトテンプレートでは毎時で10日間スナップショットを保持してくれて、更に月ごとにも長期間(10年)良い感じに保持してくれるようです。

snapperを定期実行してもらうように、以下のコマンドでsystemdを有効化します。

実行例
sudo systemctl enable snapper-boot.timer snapper-cleanup.timer snapper-timeline.timer

手動によるテスト

スナップショットの作成を「--type pre」指定で行います。

実行例
sudo snapper -c data-volume create --type pre --print-number --description "test"

一般的には「--type pre」によるスナップショット作成後にファイル更新とか諸々の作業するのが良いそうです。

実行例
$ sudo snapper -c data-volume create --type pre --print-number --description "test"
1
$ sudo snapper -c data-volume list
 # | Type   | Pre # | Date                            | User | Cleanup | Description | Userdata
---+--------+-------+---------------------------------+------+---------+-------------+---------
0  | single |       |                                 | root |         | current     |         
1  | pre    |       | Wed 01 Feb 2023 06:37:25 AM JST | root |         | test        |         
$ sudo tree -d /volume2/@data/.snapshots
/volume2/@data/.snapshots
└── 1
    ├── info.xml
    └── snapshot
        └── daiji-file.txt

subvolume配下に「.snapshots」ディレクトリが作成され、その配下にsnapperが管理しているID(今回は1)のディレクトリ配下にスナップショットが作成されています。

更に色々とオプションを変えながらスナップショットを作成していきます。

実行例
# 先ほどの--type preの対となる作業終了時に作るべきスナップショット(勝手には消えない)
sudo snapper -c data-volume create --type post --pre-number 1 --print-number --description "test"

# --type singleで単純に作成したスナップショット(勝手には消えない)
sudo snapper -c data-volume create --type single --description "test2"

# --type single、--cleanup-algorithm timelineを指定したスナップショット(10日後に消える)
sudo snapper -c data-volume create --type single --description "test3" --cleanup-algorithm timeline

各コマンドは一瞬で実行完了します。さすが、コピーオンライト(CoW)ファイルシステム。

実行結果
$ sudo snapper -c data-volume list
 # | Type   | Pre # | Date                            | User | Cleanup  | Description | Userdata
---+--------+-------+---------------------------------+------+----------+-------------+---------
0  | single |       |                                 | root |          | current     |         
1  | pre    |       | Wed 01 Feb 2023 06:37:25 AM JST | root |          | test        |         
2  | post   |     1 | Wed 01 Feb 2023 06:46:16 AM JST | root |          | test        |         
3  | single |       | Wed 01 Feb 2023 06:47:57 AM JST | root |          | test2       |         
4  | single |       | Wed 01 Feb 2023 06:51:58 AM JST | root | timeline | test3       |  

この検証中に時間が00分を過ぎたので「snapper list」を再実行したところ、ちゃんと#5にsystemdから実行されたsnapperがスナップショットを作成してくれていました。

実行例
$ sudo snapper -c data-volume list
[sudo] password for yotan: 
 # | Type   | Pre # | Date                            | User | Cleanup  | Description | Userdata
---+--------+-------+---------------------------------+------+----------+-------------+---------
0  | single |       |                                 | root |          | current     |         
1  | pre    |       | Wed 01 Feb 2023 06:37:25 AM JST | root |          | test        |         
2  | post   |     1 | Wed 01 Feb 2023 06:46:16 AM JST | root |          | test        |         
3  | single |       | Wed 01 Feb 2023 06:47:57 AM JST | root |          | test2       |         
4  | single |       | Wed 01 Feb 2023 06:51:58 AM JST | root | timeline | test3       |         
5  | single |       | Wed 01 Feb 2023 07:00:01 AM JST | root | timeline | timeline    |    

とりあえずもう、放置してよさそうなのでテストで作った#1~#4のスナップショットは削除しました。

実行例
$ sudo snapper -c data-volume delete 1 2 3 4
$ sudo snapper -c data-volume list
 # | Type   | Pre # | Date                            | User | Cleanup  | Description | Userdata
---+--------+-------+---------------------------------+------+----------+-------------+---------
0  | single |       |                                 | root |          | current     |         
5  | single |       | Wed 01 Feb 2023 07:00:01 AM JST | root | timeline | timeline    |    

おわりに

これで、/mnt/data配下がうちのSynoloy NASレベルに近づいたので、安心してDocker環境の移行が行えそうです。

まだ、Btrfsの一部分の機能しか試せませんでしたが、とりあえずここまでとします。

参考になれば幸いです。

Discussion