自宅サーバー構築譚：zfsスナップショットの操作

今回は一回休み、というか一般的な zfs スナップショットの操作の紹介です。他の話題に紛れてしまっているので、ここに纏めました。

- スナップショットの作成
- 作成したスナップショットの一覧表示
- ロールバック
- スナップショットの削除

こういうのを Linux コマンドと組み合わせて一工夫したワンライナー集みたいな感じになります。

# スナップショットの作成

コマンドは```zfs snapshot```です。日時は、保存されてるのかも知れませんが表示できない（ように感じる）ので、日時を名前の頭に付けておいた方が良いです。

```console
zfs snapshot tank/main/ubuntu@$(date +%Y%m%d_%H%M%S)_test
```

# スナップショットの一覧表示

コマンドは```zfs list -t snapshot```です。他のコマンドと組み合わせてワンライナーを作る事を考えた場合、少々バリエーションがあります。

## データセットの指定

我が家の場合```tank/main/ubuntu```がルートディレクトリで、システム関連を格納しています。ユーザーデータなどは```tank/sub0```のサブデータセットに格納しています。

```console
zfs list -t snapshot tank/main/ubuntu
```

## 新しい順

日時の新しい順に表示する場合はオプションスイッチ```-S name```を付けます。作成する時に名前の先頭に日時を付加したので、これで日時の順番になります。

これの良い所は、```head -n2```などとして最新の物だけ表示させる事が出来る点です。業務でエビデンスが必要な場合や、公開記事を書く場合に、余計な情報を出さずに済みます。

```console
zfs list -t snapshot -S name tank/main/ubuntu | head -n2
```

例えばこんな結果になります。

```console
# zfs list -t snapshot -S name tank/main/ubuntu | head -n2
NAME                                                      USED  AVAIL     REFER  MOUNTPOINT
tank/main/ubuntu@20220322_173258_test                      96K      -     1.45G  -
```

## スナップショット名のみ表示

オプション```-o name```で、スナップショット名のみ表示になります。

```console
zfs list -t snapshot -S name -o name tank/main/ubuntu | head -n2
```

こんな表示になります。

```console
# zfs list -t snapshot -S name -o name tank/main/ubuntu | head -n2
NAME
tank/main/ubuntu@20220322_173258_test
```

## ヘッダ行を抑制

オプション```-H```で、ヘッダ行を抑制して表示しないようにします。

```console
zfs list -t snapshot -S name -H tank/main/ubuntu | head -n1
```

こんな表示になります。

```console
# zfs list -t snapshot -S name -H tank/main/ubuntu | head -n1
tank/main/ubuntu@20220322_173258_test   96K    -       1.45G   -
```

## 目的のスナップショット名のみ取り出す

これらのオプションスイッチを組み合わせた上で、```head```と```tail```コマンド、あるいは```grep```コマンドでフィルタリングすれば、お望みのスナップショット名のみ取り出す事が出来ます。

例えばこんな感じ。

```console
# zfs list -t snapshot -S name -o name -H tank/main/ubuntu | head -n1
tank/main/ubuntu@20220322_173258_test
```

```console
# zfs list -t snapshot -S name -o name -H tank/main/ubuntu | grep 20220322
tank/main/ubuntu@20220322_173258_test
```

```console
# zfs list -t snapshot -S name -o name -H tank/main/ubuntu | grep test
tank/main/ubuntu@20220322_173258_test
```

```grep```コマンドの出力には色が付いたりしますが、それは気にしない方向で。

# ロールバック

スナップショットを撮った後に加えた変更を、全部丸っと無かった事にします。バージョン管理みたいな感触ですが、シンボリックリンクだろうがソケットだろうが何だろうがファイルシステムに加えられた変更全部が対象になってるのがスゴイ。

コマンドは```zfs rollback```なんですが、```xargs```コマンドを利用する事で、パラメータとして与えるスナップショットを上述の一覧表示で取り出したスナップショット名から与えるようにします。

メモリ内容（キャッシュ）との不整合などを考えると、```zfs rollback```の直後に```reboot```した方が良いです。

```console
zfs list -t snapshot -o name tank/main/ubuntu | grep 20220322 | xargs -rt zfs rollback
reboot
```

ちなみに```xargs```コマンドのオプション```-t```は、実際に実行したコマンドを標準出力に出力してくれます。
オプション```-r```は、もし```grep```失敗などで何も拾えなかった場合にはコマンドを実行しません。```-r```を指定しなかったら引数無しで1回実行されてしまいます。

（2023/01/03追記ここから）
[Ubuntu 22.04 LTS on ZFS をインストール](https://qiita.com/katz_engineer/items/1ffea0a145da7025e10d)する場合には、データセットはUbuntuデフォルトになるべく合わせる方向でやっています。その結果として、システムのロールバック時には、下記コマンドを実行する必要があります。

要するに、aptのパッケージ管理も巻き戻す必要があるのですが、その為に```/var/lib/apt```と```/var/lib/dpkg```も処理します。

なんでこんな面倒臭い事になったかというと、```/var/lib```を別のデータセットにしたからですね。```/var/lib```が別になってる理由は```/var```を別のデータセットにしたからです。

[これもそれなりに理由があるようです。](https://didrocks.fr/2020/06/19/zfs-focus-on-ubuntu-20.04-lts-zsys-properties-on-zfs-datasets/)仕方が無いですねぇ。面倒臭いですが、飲み込みます。

```console
zfs list -t snapshot -o name tank/ROOT/ubuntu | grep 20220322 | xargs -rt zfs rollback
zfs list -t snapshot -o name tank/ROOT/ubuntu/var/lib/apt | grep 20220322 | xargs -rt zfs rollback
zfs list -t snapshot -o name tank/ROOT/ubuntu/var/lib/dpkg | grep 20220322 | xargs -rt zfs rollback
reboot
```
（2023/01/03追記ここまで）

# スナップショットの削除

コマンドは```zfs destroy```です。上述の一覧表示で取り出したスナップショット名を与えるようにすると間違いも減るでしょう。

```console
zfs list -t snapshot -o name tank/main/ubuntu | grep 20220322 | xargs -rt zfs destroy
```

# スナップショットは素晴らしい

:thumbsup_tone2: