ZFS - スナップショットいつやるか?今でしょ!

こういうニュースを見るたびに、こう嘆かざるを得ません。

* [GitLab.comが操作ミスで本番データベース喪失]

[GitLab.comが操作ミスで本番データベース喪失]: http://www.publickey1.jp/blog/17/gitlabcom56.html

「ああ、ZFS使ってれば」、と。


http://gihyo.jp/dev/serial/01/swift-introduction/0018

> ZFSはこれまでのファイルシステムの常識を覆す画期的なファイルシステムでした。fsckを不要にするトランザクション，パーティションという概念を過去のものにするデータセット，ファイルシステム自体のundoを可能にするスナップショット，エラーを自動検知し，可能であれば自動修復するチェックサム，RAIDホールがないRAID-Z……「Z=最後のファイルシステム」という自信がその名に込められたZFSは，今は亡きSun Microsystemsの最後の遺産でもあります

僕にZFSの話をさせると長くなります -- ので、今回はスナップショットを中心に手短に。

## バックアップを難しくしているのは何か?

ここで、ある小さな*nixシステムのフルバックアップを取ることにしましょう。簡潔のため、バックアップ元の全システムは`/`、バックアップ先は`/mnt`としておきます。こうですか?

````
rsync -Haxv / /mnt
````

残念ながら、これはうまくいくとは言えません。なぜか?これを実行している間にファイルは追加されたり削除されたり更新したりする可能性があるからです。その可能性を排除するためには、ファイルシステムに書き込む可能性のある全てのプロセスを停止する必要があります。つまりサービスを止めるということです。確実を期すなら、シングルユーザーモード、もっと確実を期すのであれば別システムを起動して....つまりバックアップ中は他に何もできなくなっちゃうというわけです。

バックアップの難しさ。それは「バックアップ中にバックアップ元が変わってしまう」に尽きると言っても過言ではありません。

## スナップショットとストリーム

それを解決するのが、スナップショットという概念です。要はある時点におけるファイルシステムの状態を凍結した状態があれば、凍結後に何がいくら変わっても凍結した瞬間のデータは失われないというわけです。スナップショットはZFSの専売特許ではありませんが、zfsのそれは特に使いやすくなっています。`tank/dataset`という名前のデータセットが`/tank/dataset`にマウントされているとして

`zfs snapshot tank/dataset@snapname`

これだけです。一瞬で終わります。一瞬で終わるのでサービスを止める必要ありません。そしてスナップショットを取った瞬間の中身は`/tank/dataset/.zfs/snapshot/snapname`で参照できます。`rm -rf /tank/dataset/*`しても大丈夫!

この中身をrsyncしてもいいのですが、ここからがZFSの本領発揮です。ZFSではデータセットの中身をシリアライズできるのです。

````
zfs send tank/dataset@snapname > dataset@snapname.zfs
````

これで、`dataset@snapname.zfs`にスナップショットを復元するのに必要な全てのデータがおさまりました。これを別のデータセットに復元するには

````
zfs receive tank/backup < dataset@snapname.zfs
````

とすればよい。ここで`<`と`>`に注目。そう。ストリームというだけあって、`zfs send`と`zfs receive`は`|`でつなげるのです。

````
zfs send tank/dataset@snapname | zfs receive tank/backup
````

これの何が嬉しいか?ずっと高速なのです!`tar`や`rsync`のようなファイル単位の読み書きは、細かいファイルが多ければ多いほど遅いのですが、その代わり「このディレクトリだけ」という細かい指定ができます。`dd`のようなブロック単位の読み書きはランダムアクセスがほとんど発生せずずっと高速ですが、パーティション丸ごと一本という大雑把な指定しか出来ません。ZFSにおける「データセット」は概念としては「パーティション」ですが、ディレクトリを作成するのとほとんど変わらないコストで作成(と破棄)が出来るので、両者のいいとこどりができるのです。

参考までに、先日自鯖のZFSアレイを再構築した際にファイル数2000万以上、容量4TBのデータセットを、すでに2TBほど埋まっていたSeagate Archive 8TBに`send|receive`した際に[pv]で時間を計測したのですが、

[pv]: http://www.ivarch.com/programs/pv.shtml

````
 4.3TiB 7:33:28 [ 165MiB/s]
````

と、期待通りほとんどランダムアクセスなしの速度が出ました。

これだけでもかなり嬉しいのですが、zfsでは差分ストリームも作れるのです。

`````
zfs snapshot tank/dataset@before
zfs send tank/dataset@before | zfs receive tank/backup
`````

されてる状態で

````
zfs snapshot tank/dataset@after
zfs send -I tank/dataset@before tank/dataset@after | zfs receive tank/backup
````

とした場合、読み書きされるのは`@before`と`@after`の差分だけです。差分だけあって、差分がARC(キャッシュ)に乗っている状態だと、バックアップ元のドライブにはほとんどアクセスしません。この様子をアクセスランプで見るとちょっとした感動を得られますヨ。

## 人が事故るなら自動化すればいいのに - zfstoolsのススメ

ZFSではスナップショットのコストがあまりに安いので、定期的に自動でスナップショットを撮りたくなるのは人類の自然な欲求というものでしょう。シェルスクリプトでも簡単に出来るのですが、オススメはzfstoolsです。

* https://github.com/bdrewery/zfstools

見ての通りgithubにも上がっているのですが、FreeBSDならportsもあります。これの特に優れている点は、MySQLとPostgreSQLにも対応している点。指定一つでスナップショットを撮る前にデータベースをflushしてくれます。指定期間をすぎたスナップショットの破棄も自動でしてくれるので、一度設定したらあとは放置プレイでOK。

いや、OKではありません。スナップショットはバックアップではないのですから。さすがにバックアップの部分まで自動化してくれる一般ツールは見当たらないのですが、ここまで道具立てが揃っていれば、利用シーンごとのスクリプトを書くのもさほど苦ではありません。実際某所では100行未満の拙作スクリプトが、zfstoolsが5分おきに取ったスナップショットを5分おきにリモートバックアップしています。

余談ですが、[APFS]導入の暁には、[Time Machine]の実装も現在のディレクトリーベースからスナップショットベースに変わると私は見ています。[Time Capsule]の新製品がなかなか出ない理由も、もしかしてこれだったりして。

[APFS]: https://ja.wikipedia.org/wiki/Apple_File_System
[Time Machine]: https://support.apple.com/ja-jp/HT201250
[Time Capsule]: http://www.apple.com/jp/airmac-time-capsule/

## use ZFS, period.

ファイルシステムというものは基本的にOSに禿げしく依存するものですが、[OpenZFS]のおかげでZFSはクロスプラットフォームでもあります。FreeBSDのデータセットをUbuntuにsendしたり、それをさらにmacOSでimportしたり…UbuntuがWindows 10で動くようになりましたが、[ZFS on Linux]も移植してくれませんかねえ。そうすればパンピー的にもポータブルなディスクアレイが容易に実現するのに。

[OpenZFS]: http://www.open-zfs.org/wiki/Main_Page
[ZFS on Linux]: http://zfsonlinux.org

というわけで、ZFSはいいぞ。

Dan the Unofficial ZFS Evangelist