2014年7月2日
ZFSがあればGitなんか要らない?
(2013-08-20)by Zef Hemel
本記事は、原著者の許諾のもとに翻訳・掲載しております。
ZFS を少し触ってみました。ZFSはOracle(その前はSun)の次世代ファイルシステムです。元はSolaris向けに開発されたものですが、オープンソースなのでLinux(0.6.1以降が 製品版として安定しているようです と Mac にも移植されています。ファイルシステムといっても、ZFSはボリュームマネージャでもあるので、ディスクのパーティション管理もやってくれます。ZFSがなぜそんなにクールかといえば、データ破損対策、RAIDのサポート、スナップショットやコピーオンライトの実装、そしてバックアップ時のフレキシブルで効率の良いデータ転送などが装備されているからです。ZFSを使って、バージョン管理システムのGit(Gitじゃないバージョン管理システムでも構わないのですが)のいろいろな機能をどこまで実行できるのか、お見せしようと思います。もちろん、本気で”ちゃんとした”バージョン管理システムを捨てたほうがいいと言うつもりはありませんよ。でもファイルシステムのレベルでここまでできると理解するのにはいい例だと思います。
ZFSをインストールするのは簡単です。Macだったら OpenZFS on OS X のサイトに行ってパッケージをインストールすればいいのです。Ubuntuでは次のようにします。
$ sudo apt-add-repository ppa:zfs-native/stable
$ sudo apt-get update
$ sudo apt-get install ubuntu-zfs
プールとファイルシステム
さて準備ができたので新たにZFSストレージプールとファイルシステムを構築してみましょう。ドライブがあればそれを利用すればいいですし、ない場合はディスクのかわりにファイルを作成すれば、試してみるには十分です。例えば10Gのファイルを作成するにはこのようにddコマンドを使います。
$ dd if=/dev/zero of=/tmp/disk1.img bs=1024 count=10485760
RAIDの設定を試してみるには、disk1.imgとは違う名前でもう1つファイルを作りましょう。次にzpool createを使ってストレージプールを作成します。ディスクがあるならディスクラベル(例えば/dev/sda、/dev/sdbなど)、またはID(/dev/disk/by-id/…)も利用できます。今回はファイルヘのフルパスを利用します。
様々なタイプのプールを作ることができますが、ミラー化するには以下のようにします。
$ sudo zpool create mypool mirror /tmp/disk1.img /tmp/disk2.img
ここでは”mypool”という名前のプールが、2つの”デバイス”をミラーして、/mypool (Linuxの場合。Macでは/Volumes/mypool)直下にマウントしています。使用可能な容量を確認するにはzfs listを使います。
$ sudo zfs list
NAME USED AVAIL REFER MOUNTPOINT
mypool 433Ki 9,78Gi 370Ki /Volumes/mypool
もう1つの方法としては、すべてのデバイスから使用可能な領域を集めて、それを1つの大きなドライブとして扱う方法もあります。mypoolを既に作成してしまっていたら破棄してください。
$ sudo zpool destroy mypool
それからミラーされていないプールを作成します。
$ sudo zpool create mypool /tmp/disk1.img /tmp/disk2.img
$ sudo zfs list
NAME USED AVAIL REFER MOUNTPOINT
mypool 439Ki 19,6Gi 370Ki /Volumes/mypool
約20Gの容量が利用可能になっています。
ストレージプールについては途中でディスクを追加したり交換したりなど、 いろいろとできることがあります が、今のところはシンプルな設定にしておきましょう。
ここまで/Volumes/mypoolまたは/mypool直下にファイルをマウントしてきましたが、このようなZFSの使い方はあまりお勧めしません。そのかわりに、プール内にいくつかの異なるファイルシステムを作成してみましょう。それぞれのファイルシステムに対して、暗号化・圧縮・割り当てなどの様々なプロパティを設定することができます。また、ファイルシステムごとに個別のスナップショットを取ることもできます。他にも、SambaやNFSを介してファイルシステムを共有したり、別のサーバ上のプールにファイルシステムのスナップショットを送ったりできます。
このように、ファイルシステムはかなりスゴいのです。
ZFSファイルシステムではzfsコマンドを使います(プールの際にzpoolコマンドを使うのと同じです)。
$ sudo zfs create mypool/test
これでファイルシステムが新規作成され、/mypool/test(Macの場合は/Volumes/mypool/testとなります)下にマウントされます。ちなみに、ファイルシステム(およびプール)は、-mオプションでどこにでもマウントできます。面白いのですが、実は作成後でもマウントポイントを変更することができるんです。
$ sudo zfs set mountpoint=/test mypool/test
こうすれば、ファイルシステムを/test下に再マウントできます。ファイルシステムのプロパティを確認するには、zfs get allコマンドを使います。
$ sudo zfs get all mypool/test
NAME PROPERTY VALUE SOURCE
mypool/test type filesystem -
mypool/test creation di aug 20 14:47 2013 -
mypool/test used 442Ki -
mypool/test available 9,78Gi -
mypool/test referenced 442Ki -
mypool/test compressratio 1.00x -
mypool/test mounted yes -
mypool/test quota none default
mypool/test reservation none default
mypool/test recordsize 128Ki default
mypool/test mountpoint /test local
mypool/test checksum on default
mypool/test compression off default
mypool/test atime on default
mypool/test devices on default
mypool/test exec on default
mypool/test setuid on default
mypool/test readonly off default
mypool/test snapdir hidden default
mypool/test canmount on default
mypool/test copies 1 default
mypool/test version 5 -
mypool/test utf8only on -
mypool/test normalization formD -
mypool/test casesensitivity sensitive -
mypool/test refquota none default
mypool/test refreservation none default
mypool/test primarycache all default
mypool/test secondarycache all default
mypool/test usedbysnapshots 0 -
mypool/test usedbydataset 442Ki -
mypool/test usedbychildren 0 -
mypool/test usedbyrefreservation 0 -
mypool/test logbias latency default
mypool/test sync standard default
上記のプロパティの中には、便利な機能がたくさんあります。例えば、圧縮をする場合はこうなります。
$ sudo zfs set compression=on mypool/test
これで、コマンド実行後にファイルシステムに書き込まれたものはすべて圧縮されます。
Gitなんか要らない?
GitのかわりにZFSを採用するのは、名案とは言えないかもしれません。ただし、ZFSがファイルシステムのレベルでできることを理解していただくために、Git特有の操作を例にしていくつかご紹介しましょう。
* リポジトリの作成
* バージョンのコミットとタグ付け
* ブランチの作成
* 別ストレージプール(別筐体を含む)からのプッシュとプル
お気づきかと思いますが、上のリストにはマージ機能がありませんね。私の知る限りでは、ZFSはサポートを行っていません。
リポジトリの作成
まずファイルシステムを作成しましょう。今回の解説用に、階層構造を作ります。このファイルシステムを”zfsgit”と名付けます。もちろん、ファイルシステムの階層は、いくらでも深くすることができます。次に、ファイルシステムのルートをカレントユーザにchownします。そうすれば、ファイル作成・更新・削除のためにsudoする必要はありません。
$ sudo zfs create mypool/projects
$ sudo zfs create mypool/projects/zfsgit
$ sudo chown $(whoami) /Volumes/mypool/projects/zfsgit
$ cd /Volumes/mypool/projects/zfsgit
これでリポジトリと同様のものが準備できました。リポジトリに関しては解決です。
では、ファイルを作成して何か書き込んでみましょう。
$ echo "Hello" > file.txt
バージョンのコミットとタグ付け
コミットとタグ付け、つまり、復旧できるように過去のバージョンを保持しておくには、ZFSの”スナップショット”を使います。ZFSスナップショットには、明示的に名前をつけなければなりません。最初のスナップショットを”firstcommit”と名付けましょう。ファイルシステム名の後に@とスナップショット名を付加します。
$ sudo zfs snapshot mypool/projects/zfsgit@firstcommit
では、下記のようにファイルを少しだけ変更してみましょう。
$ echo "world" >> file.txt'''
結果を確認します。
$ sudo zfs diff mypool/projects/zfsgit@firstcommit
M /Volumes/mypool/projects/zfsgit/file.txt
残念ながらテキストレベルの差分は確認できないのですが、少なくともどのファイルが変更されたかは分かります。では、再度コミットを実行してみましょう。
$ sudo zfs snapshot mypool/projects/zfsgit@secondcommit
現在のスナップショットを一覧表示するには、次のコマンドを実行します。
$ sudo zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
mypool/projects/zfsgit@firstcommit 146Ki - 370Ki -
mypool/projects/zfsgit@secondcommit 0 - 386Ki -
ここで、さらにファイルに変更を加えます。
$ echo "ladies..." >> file.txt
うーん、イマイチですね。では、1つ前のスナップショットにロールバックしてみましょう。
$ sudo zfs rollback mypool/projects/zfsgit@secondcommit
$ cat file.txt
Hello
world
無事、1つ前のバージョンに戻りました。
ブランチの作成
機能的に見てGitのブランチ作成と同じことが、zfs cloneで実現できます。次のコマンドを実行すれば、特定のスナップショットを元にしたファイルシステムのクローンを作成できるのです。
$ sudo zfs clone mypool/projects/zfsgit@firstcommit mypool/projects/zfsgit_branch
これで、コピーオンライト形式の新しいファイルシステムがmypool/projects/zfsgit_branch以下にマウントされました。この処理はデータのコピーを伴わないため非常に軽く、初回に追加のディスク領域を消費することはまずありません。
リポジトリのプッシュとプル
ZFSでは、異なるストレージプールにファイルシステムを転送することができます。転送先はローカル、リモートを問わず、差分転送も可能です。では、実際にやってみましょう。まず”mypool2″という名前の新しいストレージプールをローカル環境に作成してください。これで、任意のスナップショットを別のストレージプールに”プッシュ”する準備が整いました。実際のコマンドは下記の通りです(rootユーザで実行してください)。
$ zfs send mypool/projects/zfsgit@firstcommit | zfs receive mypool2/zfsgit
下のように、SSHを介した処理と同じ動きをすると考えれば理解しやすいでしょう。
$ zfs send mypool/projects/zfsgit@firstcommit | ssh root@myserver zfs receive mypool/zfsgit
上記のようにすれば、スナップショット作成時のファイルシステム全体がプッシュされます。また、過去に1つ前のスナップショットをプッシュしていた場合は、-iオプションを付加することで前回と今回の差分だけをプッシュすることもできます。
$ zfs send -i mypool/projects/zfsgit@firstcommit mypool/projects/zfsgit@secondcommit | zfs receive mypool2/zfsgit
この差分オプションは、大規模なファイルシステムのバックアップを定期的に行う場合に有効です。もちろん上記のコマンドはUNIXのパイプを使用しているに過ぎませんから、zfs sendの出力結果をファイルに書き込んで、Amazon S3などにアップロードするといった処理も行えます。
$ zfs send mypool/projects/zfsgit@firstcommit > backup.dump
一方、ファイルシステムをプッシュではなく”プル”するには、次のようにSSHを介してZFSのreceiveコマンドを使います。
ssh root@myserver zfs send mypool/zfsgit@secondcommit | zfs receive mypool/zfsgit
ZFSを採用すべきか
ZFSは、少なくともSolarisやLinux上ではかなり使い勝手が良く、安定しています。Mac上でも安定稼働するどうかは、今の時点ではまだ確信が持てません。Linuxのルートファイルシステムとして採用するには、現時点ではまだ少し問題があるように思います。しかし、それもすぐに解決されることでしょう。私はまだ信頼性とパフォーマンスを語れるほどZFSを十分に使い込んでいませんが、ネット上での評判は良いようです。
ただし、ZFSが唯一の選択肢というわけではありません。例えば LinuxのBtrfs にも同じような機能が多く備わっています。ただBtrfsは比較的新しいシステムでまだ成熟していないため、ZFSほど安定していません。どちらにせよ、こうしたファイルシステムには楽しめる機能がたくさん実装されています。ZFSについてもっと詳しく知りたい方は、 Oracle Solaris ZFS 管理ガイド をぜひチェックしてみてください。大変読みやすく、LinuxとMacの両方に対応しています。
Git Logo by Jason Long is licensed under the Creative Commons Attribution 3.0 Unported License
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa