Ubuntu 22.04で外部ストレージをBtrfsにしてスナップショットを活用する
はじめに
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、をマウントします。
マウントオプションはディスク性能を高められるかなと考え指定していますが、正直今回の用途に合致するのかは不明です。
/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が指定されているところです。
/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