FreeBSD 7.0-RELEASEではファイルシステムとして新たに、UFS2をジャーナリング機能で高速化する「GEOM journaling」や、OpenSolarisからの移植である「ZFS」が利用できるようになった。これらの新機能はFreeBSDのポリシーに従って、安全性・安定性が確保できるまではオプション扱いであるが、筆者も含めた新しいもの好きにとっては新機能は速攻試したくなるのが人情である。結果、どちらも魅力的で今後FreeBSDでの標準が動く可能性も十分ありうるという印象を得たので報告する。
ファイルシステム発展の歴史-soft updatesとjournaling
FreeBSD 7.0-RELEASEで標準採用されているファイルシステムはUFS2といい、5.1-RELEASEから標準となった「UFS2」と4.5-RELEASEから標準となった「soft updates」との組み合わせで動いている。UFS2に至る歴史は、たとえば技術評論社『FreeBSD Expert』への筆者による寄稿※1 などを読み返して軽くおさらいしてもらうとして、ファイルシステムの進歩の歴史は常に、
- 大容量化に対応したアクセス速度の高速化: スピード
- ファイルシステムの完全性(filesystem integrity): 安全性
との、両立しない二つの指標をいかにバランスさせるかに知恵が使われていることを頭に入れておこう。ファイルシステムは、実際のデータと、データに関する情報(ディレクトリやユーザ情報など)であるメタデータから構成されており、特にメタデータ更新の出来不出来で、ファイルシステムは安全にも危険にも、高速にも低速にもなりうるのである。
一般ユーザがファイルシステムに依存したディスク書き込み性能の限界をを体感できる時は、「ports.tgz展開」のように、ファイルを無数に展開する時である(一方、2Gバイトといった大容量のファイルをコピーする時間は、ハードウェア自身の性能の限界を示している)。データ書き込みは必ずメタデータ情報の更新を伴なうが、更新するべきメタデータの数はファイルの数に比例して増加するので、メタデータ更新のオーバーヘッド分だけ手間が増加して時間がかかる。日頃の生活でも、細切れの仕事が増えれば増えるほど、仕事に取りかかるまでの下準備期間が足枷となって、全体の処理能力が低下することは筆者ならずとも一度は体験したことがあると思う。
日常生活の連想で処理スピードを向上させる方法を考えると、
- 細切れの仕事をまとめて一気に仕上げる
- 仕事をやった振りをして空いている時間に実行する
方法を思いつく。実際「非同期(asynchronous)書き込み」と呼ばれる方法はこの戦略を取っている。すなわち、データが一定の大きさにまとまるまでメモリ上に「溜めておき」、定期的に「後から書き込む」という仕組みである。表1のように、asyncオプションでマウントしたディスクは圧倒的に高速である。
表1 asyncマウントとsyncマウントのports.tgz(7.0-RELEASE付属)展開時間比較。:単位は秒
ファイルシステム |
オプション |
新規展開 |
上書展開 |
削除 |
UFS2 |
非同期書き込み |
46.19 |
58.795 |
24.95 |
UFS2 |
同期書き込み |
145.65 |
142.465 |
92.3 |
ところがこの戦略、うまくいっているうちは良いが、もし事故でもあって「仕事をやった振りをして後からやろうと思っているうちにデータを紛失」しようものなら取り返しが付かなくなる。「すみませんがハードディスクが飛んでしまって云々」と方々に言い訳して仕事の割り振りをやり直す羽目になる。もしも無くしたデータが「お金の振込先リスト」だった場合を想像してもらいたい。詐欺の格好の餌食になってしまうであろう。
FreeBSDはこのような、突然電源が切れるなど、不測の事態が起こったときにもファイルシステムの完全性を保てること、具体的には全ての瞬間で、どこまでのメタデータが正常で、どこからが正常でないかを切りわけられることをインテグリティ(integrity)と呼んで重要視している。このファイルシステムインテグリティが保てないと、システムの予期せぬ終了の後、いきなりファイルが他人のものになったり、1つのファイルが2つのオーナーに化けたりすることが考えられ、セキュリティ上重大な問題となる。
特に、データの書き込み途中で電源が切れた場合、このような問題が起こりうるわけで、4.6-RELEASE以前のFreeBSDでは遅いが最も安全な「同期書き込み」(データが来た時点でディスクに書き込む)方法を使っておいたが、4.6-RELEASE以降では遅延書き込みのもう一つの方法として、「soft updates」という方式を採用したのである。soft updatesは、たとえばデータを更新するときに、
- まっさらの領域に実体のデータとメタデータの領域を確保し
- データとメタデータが完全に用意できた時点で
- 新しい実体へのポインタを張り直す
という戦略を取っている。この手法を使えば、メタデータを作成して書き込み待ちの途中に電源が突然切れたとしても、どこからも参照されていないメタデータがディスクに残るだけなので、「fsck」プログラムによって、ゆっくりとこのゴミを取り除けばよい。このようにしてファイルシステムの完全性は何時も保ったまま、実際のディスクにはまとめて後から書き込むという戦略のお蔭で、ファイルシステムの完全性を保証したまま、同期書き込みに比べて1.5倍の速度向上を実現したのである。
FreeBSDでは現在までのところ、UFS2とsoft updatesの組み合わせが、ファイルシステムの完全性を常に保った上で、なおかつファイル生成削除時に体感できるメタデータ更新のスピードとのバランスが最も良く取れているということで、しばらく(少なくとも4年間)の間標準の座を不動のものとしている。確かにUFS2+soft updates、さらにバックグラウンドfsck三羽烏で颯爽と登場した5.1-RELEASEに接したとき、「UFS最強」と筆者も思ったものである。この最強トリオに打ち勝って標準を取るにはよほどのメリットが必要とされるので、新機能の開発者には辛いハードルであると同時に、ハック冥利に尽きるというものである。
本稿で紹介するGEOM journalingは、「これから行うメタデータ変更の予定を日誌(journal)に書き出しておいて、ジャーナルの記入が終了したところで実際のデータ更新を行う」という方針である。記入が終わっていないjournalのパートを単純に無視することで完全性を保証できる(ZFSも同上の方針である)。この方式の最大のメリットは、突然のシステム停止から再起動をした後でもfsckをかける必要がないことである。7.0-RELEASEではシステムを働かせながらファイルシステムのチェックを行う、background fsckが標準であるので、マシンが異常終了してから再起動するまでの待ち時間は正常時とほとんど変わらないとはいいながら、fsck中のあのハードディスクのガリガリ時間が嫌な読者には朗報であろう。
UFS2+GEOM journalingを使ってみよう
GEOM journalingについてのまとまった論文はまだなく、マニュアルページ、開発者のPawel Jakub Dawidek氏のメール、またはソースコードなどが手掛りになる。
で予習をすると、
- 新しいディスク上での利用(newfsフォーマットする)
- 既存のファイルシステム上で応用(newfsフォーマットしない)
の2つのやり方があると書いてある。FreeBSDを使っている読者にとっては当然、既存のファイルシステム上での応用が魅力的なわけだが、手順を1つでもしくじるとデータは戻ってこないので注意が必要である。実際筆者も手順をしくじってスライスを1つ飛ばしてしまったので特に注意を喚起しておきたい。ハードディスクの容量は右肩上り、値段は右肩下りなので、ここは1台新品のディスクを奮発して、本特集第2回「7.0-RELEASEでディスク丸ごとバックアップ」で紹介した方法などを使って「丸ごとバックアップ」を取ってから試すことをお勧めする。
筆者が用意したディスク(ad4、Hitachi HDT725040VLA360/SATA150)には、ad4s2、ad4s3に3つのパーティションが切ってあり、そのうちad4s3eを/home2として使い、ad4s3fが未使用である(正確には/home3だった領域をミスで壊してしまったわけだが…)。この状況で、
- ad4s3fにjournalingのスライスを新規作成
- ad4s3fをjournalとして、既存スライスad4s3eのジャーナリングを行う
という2つの方法を試してみることにしよう。
(1)ad4s3fにjournalingのスライスを新規作成して使う
カーネルモジュールのロード、ジャーナルラベル付け、フォーマット、マウントの4ステップで完了である。(しつこくて恐縮だが)フォーマットをしたスライスの内容は消えてしまうので注意されたい。
でカーネルモジュールが組み込まれる。万一何かの手違いでモジュール(/boot/kernel/geom_journal.ko)がない場合はエラーになるので、その場合はGENERICカーネルをコンパイルしてインストールする。
で確認すると、筆者の環境では(ビデオキャプチャとかサウンドカードといった余計なものがたくさん入っているため若干見づらいが)、
のように、geom_journal.koモジュールが組み込まれたことがわかる。
次に、ジャーナルのラベリングを行う。
ls -F /dev/ad4s3*としてラベルを確認すると、
のように、ジャーナル機能付きのファイルシステム「ad4s3f.journal」ができている。新しくできたad4s3f.journalをJournalingオプション付きでフォーマット(newfs)する。
できあがったファイルシステムをasyncオプション付きでマウントすれば完了である。
引数なしのmountコマンドでマウントの具合を確認できる。
のように、/mntにgjouurnal付きのUFSとしてマウントされたことがわかる。あとはベンチマークなり何なり、普通に使えばよい。一点、データの同期を行う「sync」コマンドはgjournalデバイに対しては使えないことで、
と指定する必要があることに注意しよう。
その他、「gjournal status」「gjournal list」で詳しく状態を知ることができる。
(1)-2 作製したgjournalデバイスを起動時にマウントする
gjournalが気にいった場合、システムの起動時に自動でマウントしたくなる。カーネルモジュールを起動時に読み込むことに注意し、/boot/loader.confと/etc/fstabの2つのファイルを編集する。
/etc/fstab中のジャーナルデバイスの番号と、マウントポイントは、ユーザの環境によって変更されたい。/home3というディレクトリはデフォルトでは存在しないので、当然
としてマウントポイントを作製しておく必要がある。
fstabの最後の数字が、普通のUFSでは「2(fsckを行う)」となっているのに、今回「0(fsckを行わない)」と指定していることに注目されたい。もはや異常終了後にもfsckが必要ないことを実感できる瞬間である。些細な違いに見えるが、違いのわかる読者にはは感動が湧きあがることと思う。
(1)-3 スピード
FreeBSD 7.0-RELEASEで配布されているports.tgzを、「tar xzf ports.tgz
」コマンドで展開する時間を、UFS2+softupdatesとの間で比較した。測定結果は表2にまとめてある。一般的に第1回目展開時はまっさらのディスクにメタデータを展開することになるのに対して、2回目以降は現在のメタデータを削除する手間がかかるため、2回目以降の方が低速になる。したがって本測定では1回目(新規展開)と2回目以降(上書展開)を分けて表示する。「削除」はrm -rf ports
にかかった時間である。なお、「上書き展開」だけ5回の平均を測定してある(新規展開と削除は1回だけしか測定しなかったのは単なるサボりである)。
表2 softupdatesとgjournal間でのports.tgz(7.0-RELEASE付属)展開時間比較:単位は秒
ファイルシステム | オプション | 新規展開 | 上書展開 | 削除 |
UFS2 |
softupdates |
68.76 |
100.31 |
39.61 |
UFS2 |
gjournal |
40.53 |
52.09 |
25.23 |
すべてのオペレーションで、gjournalの方が圧倒的に高速であるという結果が得られた。
一方、gjournalの欠点は、ジャーナルを記録するディスク領域が余分に必要となることである。デフォルトではジャーナルの大きさは1Gバイトとなっているが、頻繁に読み書きを必要とするような用途では、それ以上確保しておくようなことも必要となろう。
たとえば、スライス「ad4s3f」をジャーナリング有り、無しでnewfsしたときの利用可能な容量をdfコマンドで調べると、次のようになった。
その差は932012kバイト、すなわち910Mバイトとなる。
(1)-4 gjournalをやめる
gjournalをやめるときは、導入時の逆の操作を行うことになる。
- fstabからのエントリ削除
- /boot/loader.confからカーネルモジュール削除
- (必要に応じて)バックアップ
- ラベルクリア
- newfs
このうち、最後の2ステップを書き出すと以下のようになる。
で、きれいさっぱり元通りである。newfsしたらデータも消えるので、バックアップが必要なら取っておく点に注意すること。
(2)既存のスライスにjournaling機能を追加する
すでにデータの入ったディスクを持っている読者にとっては、これから紹介する方法が便利なことはもちろんだが、ラベルの付け方を間違えるとスライスを飛ばしてしまう(実際これで、筆者もad4s3を飛ばしてしまった)ので十分注意が必要である。gjournalは「データプロバイダ」と「ジャーナルプロバイダ」とで構成される。たとえば/home2にマウント中のad4s3e(ufs2+soft updates)をジャーナルに変更する手順は以下のようになる。
で ad4s3eをシステムからいったん切り離す。
でカーネルモジュールを読み込んだ後、
という形式でプロバイダを指定する。ジャーナルプロバイダの指定を省略すると、指定したサイズのブロック(何も指定しないと1Gバイト)が、同じデバイスの末尾に確保される。マニュアルページには、gjournalは「プロバイダの最後のセクタを使う」と書いてある。「最後のセクタ」と聞いて最初「1セクタ(=512バイト)」か? と思ったものだが、そんなに小さいジャーナルはありえないため愚問であった。
たとえばデータプロバイダに「ad4s3e」、ジャーナルプロバイダに「ad4s3f」を使うとすると、
となる。今回の例ではad4s3fスライスは70Gバイトもあるので、まったくもって牛刀なサイズであるが、とりあえず実験ということでこの指定のまま行った。スライスの利用具合によってはgjournalは上書きを拒むので、自分のやろうとしている結果に自信があり、かつラベル付けを拒まれた場合は「-f」オプションで強制上書きする。
/dev/ad4s3e.journalというデバイスができるので、tunefsコマンドを使って、「softupdateを無効」「journalingを有効」にする。
以下、できあがったjournalデバイスをマウントすれば完成である。上記(1)-2節で紹介した手順で起動時に自動的にマウントすることも、もちろん可能である。
ZFSを使ってみよう
ZFSは、Sun Microsystemsが主導するOpenSolarisから移植されたファイルシステムであり、「zpool」と「zfs」の2つのコマンドを直感的に操作するだけで、わざわざ煩雑に設定ファイルをいじることなく、以下に例示するようなさまざまな新機能が利用できることが売りである。
- データを圧縮したファイルシステム(compression)
- データのミラー(mirror)
- 擬似RAID(raidz2)
- スナップショットとデータの復旧(snapshot)
ZFS Wikiページで操作のデモビデオ(フラッシュ形式)をはじめ有益な情報を得られるので、一度見てみることをお勧めする。蛇足ながら、筆者は一度ディスクにダウンロードした後、mplayer(ports/multimedia/mplayer)で読んでいる。
zfsでは、FreeBSDでおなじみのパーティション(fdisk)を切ってスライス(disklabel)に分割する、という方式とは違い、利用可能なディスクなりスライスなりをいったん仮想デバイスとしてまとめて(プール:poolするという)、そこから必要に応じて分配する。利用するコマンドの名前はzpoolであり、実例ではzpoolコマンドでtank(タンク)を作成している(わかりやすいことこの上ない)。先ほどのgjournalの例で使った空きスライス「ad4s3f」を使ってzfsを使ってみることにしよう。
1. zfsファイルシステムを作成する
まず、rc.confにZFS有効の設定を書き込み再起動する。設定を有効にすることで、システム起動時に自動的にzfsファイルシステムをマウントしてくれる。
次に、仮想ディスクである「プール」を作成する。
これで、tankという名前のプールができた。状態を「list」で知ることができる。
zfsコマンドを用い、tankという名前のプールに対してファイルシステムを作成する。
作ったファイルシステムはdfコマンドで確認することができる。
/tank/home3というディレクトリが新しくできた。作ったディレクトリの使い方は普通のUFS2とかわらない。
気になるスピードであるが、例によって、「ports.tgz」の展開にかかる時間を測定してみたところ、UFS2+gjournalと同等の速度であった。このスピードはかなり魅力的である。
表3 ZFSとgjournalでのports.tgz展開時間比較:単位は秒
ファイルシステム |
オプション |
新規展開 |
上書展開 |
削除 |
zfs |
- |
27.15 |
51.705 |
50.48 |
UFS2 |
gjournal |
40.53 |
52.09 |
25.23 |
2. 圧縮機能付きファイルシステムを作製する
zfsは、ファイルを圧縮して保存するcompression機能を持っている。利用の仕方は簡単で、単にcompressionオプションを指定するだけである。
圧縮アルゴリズムは(マニュアルページ曰く)zfs向けに最適化された「lzjb」アルゴリズムである。おなじみのgzipアルゴリズムもcompression=gzip
ないしcompression=gzip-数字
(数字は1から9まででgzipの圧縮率を示す)で利用することができる。
ファイル圧縮はzfs setを行った後のファイル操作から有効になる。たとえば圧縮無しでportsを展開すると299Mバイトのディスク領域を消費するが、圧縮有りでは177Mバイトしか消費しない。その分ファイル展開や上書き展開の速度が犠牲になる。1~2割増といったところである。
表4 compression無しと有りでのports.tgz展開時間比較:単位は秒
ファイルシステム |
オプション |
新規展開 |
上書展開 |
削除 |
zfs |
- |
27.15 |
51.705 |
50.48 |
zfs |
compression |
30.41 |
63.78 |
20.82 |
3. スナップショットを利用する
zfsにはまた、現在のファイルシステムをそっくりそのまま取っておく(snapshot)機能があり、snapshotを指定してファイルシステムを後戻りさせることも簡単に利用できる(蛇足ながら、UFS2でも実はスナップショットを撮ることはできる)。tank/home3のスナップショットを、「tank/home3@today」という名前のスナップショットに取り、ディレクトリ(先ほど展開の実験に使った「ports」ディレクトリがちょうど残っているので)を削除した後、削除する前の状態に戻して(rollback)みることにしよう。
4. ZFSを使うのをやめる
gjournalのときと同じく、zfsのプールを消去するとデータが消えて戻ってこないので、必要に応じてバックアップを行っておく。
で依存性も含めて「tank」プール以下のすべてのデータセットが消去される。続いて
でプールを開放すれば元通りである。
今後zfsを使わないのであれば、/etc/rc.confに追加したzfs_enable="YES"
の行も削除しておくとよいだろう。
5. ZFSの制限
以上便利なZFSであるが、実行のためにはかなりカーネルのメモリを必要とする。メモリを512Mバイトしか積んでいないマシンで起動したところ、以下のような警告が出てきた。
また、ports.tgzを連続して展開していたところ、一度メモリ不足でパニックになりシステムが死んでしまった。常に再現するわけではないが、若干の開発余地が残っていると考えられる。
ファイルシステムの使いどころ徹底比較
本稿で試したファイルシステム間で、ports.tgzの展開時間(スピード)の結果を表5に再掲する。
表5 ファイルシステム間でのports.tgz展開時間比較:単位は秒
ファイルシステム |
オプション |
新規展開 |
上書展開 |
削除 |
UFS2 |
async |
46.19 |
58.795 |
24.95 |
UFS2 |
gjournal |
40.53 |
52.09 |
25.23 |
UFS2 |
softupdates |
68.76 |
100.31 |
39.61 |
UFS2 |
同期書込 |
145.65 |
42.465 |
92.3 |
zfs |
- |
27.15 |
51.705 |
50.48 |
zfs |
compression |
30.41 |
63.78 |
20.82 |
結果、UFS2のasyncとgjournal、それに圧縮無しのzfsとがほぼ同着、次に圧縮有りのzfsの順に速いという結果となった。最も遅いのは同期書き込みのUFS2であり、softupdatesはその中間程度である。システムが落ちた後でfsckをかけなくても済むという点とあわせて、ジャーナリング機能付きのファイルシステムは「かなり魅力的」である。
一方、ジャーナリングの欠点は「ジャーナルのために余計にディスク領域が必要」ということである。実は筆者のモットーを川柳にして白状すると「その宝 捨てたら元に 戻らない 後で泣くのは あなたですから」で、容量が許す限りデータでも何でも「溜めておく」性格で(そのため綺麗好きの学生からは捨てましょうといつもプレッシャーをかけられるのだが(笑))、常用するディスク容量が40Gバイトだった時代も160Gバイトの今でも残り容量が常に1Gバイト前後を彷徨っている。そうなると「いつまでも あると思うな 空きディスク」であって、毎日が快適になる代わりにディスクが底をつく時の1Gバイトの差は果たして大きいのかなあとも思って若干迷うところである。
compression機能付きのzfsはテキストファイルのように圧縮できていないファイルについてはスピードとサイズのバランスが取れている(.tar.gzファイルのように既に圧縮済みのファイルについては圧縮しないというオプションもある)。しかしながら、メモリの消費が大きいことと突然システムがパニックしたという現状から、筆者としてはもう少し枯れるのを待つ気持になる。結局、標準環境で採用するとしたらどのファイルシステムを使うかという問題に対して軟弱な筆者の出した結論は、
- 現在のUFS2+softupdates
- 使い切れなくなるくらいディスクが大容量になればgjournal
- メモリが標準で2Gバイト積めるようになり、実装がもう少し安定したらzfs
という、至極まっとうな(別名、おもしろくない)結論となった。今後の一層の発展に期待したい。
筆を置くにあたって
5回にわたってお届けした本特集で紹介した以外にも、zfsやUFS2には、もっと広くFreeBSDには、さまざまなおもしろい機能が日夜実装され、進化を続けている。筆者は386BSDをPC98 NS/Tの上で使いはじめた頃から、その時その時で提供されていた機能に満足しつつ、次々と登場する新機能に感心する日々を、かれこれ15年近く続けてきた計算になる。7.0-RELEASEの登場を機会に、日々進歩するFreeBSDの世界に踏み入れ、ゆくゆくは開発者の仲間に入ってくれる読者が一人でも多く出てくることを期待したいと思う。