compFUSEd:ディスクスペースの節約に役立つ圧縮ファイルシステム

 Filesystem in Userspace(FUSE)とは、Linuxカーネルに手を付けることなく独自ファイルシステムのインストールを可能にすることを目的としたモジュールだ。こうして追加されるファイルシステムは通常のプログラムの1つとして動作するので、共有ライブラリを使用できる他、Linuxカーネル内部からでは実行困難な処理を行わせることも可能であるのと同時に、当該マシン上のアプリケーション群から見た場合のFUSEファイルシステムは、ごく通常のファイルシステムとして認識されるのである。そして本稿で解説する compFUSEd は、自動圧縮機能を備えたFUSEファイルシステムを提供するためのツールであり、特にテキストドキュメントや実行可能ファイルといった圧縮性の高いファイルを多数扱う場合、compFUSEdの使用はディスクスペースの大幅な節約を持たらしてくれる。

 compFUSEdはオーバーレイファイルシステムの一種として設計されている。これは既存のファイルシステムを“ベース”として、それと同一のファイルシステムを若干の変更を加えた上で提示するという機構だ。そしてcompFUSEdにおける若干の変更とは、ファイルの圧縮/展開処理を意味する。つまりcompFUSEdはターゲットとなるファイルシステムへの書き込み処理に介在して、そうしたデータに圧縮を施し、その結果を“ベース”ファイルシステムに書き込むのである。こうした処理はベースファイルシステムからのファイル読み込み時においても同様に機能し、ファイルの読み出し結果としてはcompFUSEdにより展開された状態のデータが提示されることになる。しかもcompFUSEdファイルシステムによる処理の恩恵は、固有の圧縮機能を有しないアプリケーションであっても享受でき、ディスクへの保存時に自分のデータが圧縮されていたことを意識する必要すらないのだ。

 残念ながら現状でcompFUSEdのパッケージは、Ubuntu、Fedora、openSUSEのリポジトリには収録されておらず、本稿の執筆においてもバージョン200712321のソースを入手した上で、後述する手順による64ビット版Fedora 8マシンを用いたソースからのコンパイルを行っている。ダウンロードページで公開されているcompFUSEdのtarボールはcf-GISMO-date(date部は日付)という名称にされており、おそらくcfというプレフィックスはcompFUSEdを示す略号だろうが、このtarボールを展開して得られるのはCompFusedというディレクトリで、このディレクトリ名に日付やバージョン情報は付けられていない。

 compFUSEdは圧縮ライブラリの、zlib、bzip2、lzo、lzo2をサポートしているが、Fedora 8に関しては、最後2つのライブラリのコンパイルはサポートされていなかった。このように各自のLinux環境でコンパイルできなかったり不具合の生じた圧縮ライブラリをサポート対象外とさせるには、Makefileの編集が必要となる。またデフォルト設定下でビルドされたcompFUSEdは、プロファイラライブラリへのリンク作成を試みるので、こうしたリンクの依存性を取り除くにはRules.makeファイルを編集しなくてはならないようだ。更にこのビルドの場合、シンボルや共有オブジェクトに関するエラーおよび、位置独立コード(PIC:Position-Independent Code)を示す-fPICオプションの未指定時に生じる問題も解決しなくてはならなかった。PICコードの特長はメモリ上の異なる位置に読み込み可能というもので、共有ライブラリのコードをコンパイルする場合に役立つが、それは複数のライブラリが同一のアドレスを使用する場合に必要に応じた移動が行えるからである。実際私の環境では、Rules.makeのCFLAGSに-fPICオプションを追加してから「make clean all」を実行することで正常にコンパイルできるようになってくれた。

$ tar xzvf /.../cf-GISMO-200712321.tgz
$ cd ./CompFused/Gismo/
$ vi Makefile
...
#
# Set to 1 to include support
#
USE_ZLIB=1
USE_BZIP2=1
USE_LZO=0
USE_LZO2=0
...

$ vi Rules.make
CC=gcc
AR=
CFLAGS= -Wall -pedantic -g -D_FILE_OFFSET_BITS=64  -fPIC

LIBS= -lfuse
...

$ make
...

 この場合「make install」のターゲットは存在しないので、ここでのステップは手作業にて行う必要がある。

# mkdir -p /usr/local/etc/
# cp compFUSEd.conf /usr/local/etc/compFUSEd.conf
# cp cf_main /usr/local/bin/compFUSEd
# mkdir /usr/local/lib/compFUSEd/
# cp -av plugins /usr/local/lib/compFUSEd/

 compFUSEdは設定ファイルとして/usr/local/etc/compFUSEd.confをチェックし、homeディレクトリにて拡張子.confの付かない.compFUSEdというファイルを検索する。今回私は、デフォルトの設定をコピーした上で、homeディレクトリにて試験用のマウントポイントをセットアップしてみた。この設定ファイルにおける冒頭の角カッコ部は、圧縮ファイルシステムのマウント位置に関するパス指定である。その後続行はマウントポイントに関する各種のオプション設定で、これらはキーと値をイコール記号で結ぶ形で指定しなくてはならない。ここでのbackendオプションには、圧縮ファイルの読み込みおよび書き込み先を指定し、その次のcompressionとwriterオプションには、ファイルの圧縮方式およびデータの書き込みに関するポリシをそれぞれ指定する。chunk_sizeとchunk_maxパラメータは、圧縮ファイルに対してcompFUSEd内部で施すチャンク(chunk)群への分割指定で、ここでは各チャンクのバイトサイズおよび1つのファイルごとに最大何個のチャンクをRAM上に保持させるかを設定する。なお個々のファイルにて変更を受けたチャンクの保存処理はwriterプラグインの責任とされているが、チャンク群のストレージを効率的に処理するのは複雑なタスクである上、各ユーザが意図するファイルシステムの使用法ごとに異なる操作をしなければならないため、こうした機能をcompFUSEd本体のロジックからwriterプラグインとして分離させたのは賢明な判断と評していいだろう。最後のexcludeパラメータには、compFUSEdでの圧縮対象外とするファイルの拡張子を一覧しておく。ただし除外対象とするファイルの指定に正規表現は使えない。

$ cd ~
$ cp  /usr/local/etc/compFUSEd.conf .compFUSEd
$ vi .compFUSEd
...
[/home/ben/compFUSEd_test]
backend = /home/ben/.compFUSEd_test.backend
compression = /usr/local/lib/compFUSEd/plugins/cf_zlib.so
writer = /usr/local/lib/compFUSEd/plugins/writer_isimple.so
chunk_size = 8192            # That's 8K per chunk (uncompressed)
chunk_max = 100              # Up to 100 chunk of 8K open per file
exclude = gz                 # On this mount we compress everything except .gz files
...
$ compFUSEd ~/compFUSEd_test
Reading config file /usr/local/etc/compFUSEd.conf
done reading configuration file.
Reading config file /home/ben/.compFUSEd
done reading configuration file.
---------------------------------------------
backend /home/ben/.compFUSEd_test.backend
compression /usr/local/lib/compFUSEd/plugins/cf_zlib.so
chunk writer /usr/local/lib/compFUSEd/plugins/writer_isimple.so
chunk size 8192
chunk max 100
compression threshold 0
...
|       compFUSEd GISMO version 1
|               by Johan Parent
|
| Please send bug reports, suggestion to compFUSEd.contact@gmail.com
|
|        - DISCLAIMER: read it!
|        - You run this program at your own risk!
|        - Treat your backups with respect :-P
|        - DO NOT store valuable data on this EXPERIMENTAL filesystem
|        - NEVER modify anything in the backend directory
|          while the compFUSEd filesystem is mounted
|
| Feedback at above mentioned address is welcome
+---------------------------------------------------------------------

$ df -h ~ ~/compFUSEd_test
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup00-LogVol00
                       16G   11G  4.3G  71% /
compFUSEd              16G     0   16G   0% /home/ben/compFUSEd_test

$ fusermount -u ~/compFUSEd_test

 ここで使用可能なwriterプラグインとしてはwriter_isimpleとwriter_smarterという2種類が存在し、両者の最大の相違点は、writer_smarterの場合、良好な圧縮結果が得られないケースではコンパクト化を実行しないというものだ。ここでのコンパクト化とは、圧縮チャンクを移動させることで、当該ファイル上の未使用領域を除去する操作を意味する。例えばあるデータの第3チャンクがアプリケーションに読み出されて、その結果得られた新規のデータを書き込み直すという場合、compFUSEdはこの新規データを新たな第3チャンクとして圧縮するのだが、この新規データが以前のものよりサイズ的に小さかった場合は、第3チャンク以降の全チャンクをファイルの先頭方向に詰める形で移動させるのである。ただしこうした処理は、ファイル全体のサイズが大きく、ファイル先頭部に対応するチャンク群が頻繁に変更されるような場合、過剰な負担がかかってしまう。また通常のファイル変更で頻繁に行われるパターンは、既存ファイルの名称変更および、ファイルの内容全体の再書き込みという操作のはずだ。実際、大多数のテキストエディタはこうした方式を採用しているので、compFUSEdのファイルシステムではテキストエディタによるファイル編集しかしないというユーザの場合、compFUSEdでの書き込み処理にどのプラグインを使用しようとも大差はないことになる。

 いずれにせよ、まったく新規のファイルの書き込みやファイル全体を上書きする場合、compFUSEdによるチャンク群の移動は発生しないので、パフォーマンスが大幅に悪化することはない。つまりコンパクト化による実質的な影響を受けるのは、定常的なデータアクセスを行い特定ファイル中の小型チャンク群を上書きするというタイプのプログラムであり、具体的にはリレーショナルデータベースなどがこれに該当する。逆に言うと、compFUSEdファイルシステムの使用が最も適しているのは、変更頻度の小さいケースだと考えておけばいいだろう。

 今回私はcompFUSEdに対する試験用に、Linux Documentation Projectから提供されているHOWTOファイルマルチページ版をダウンロードしてみた。このtar.bz2ファイルは16MBのサイズを有し、そのすべてを展開した結果は103MBとなるが、compFUSEd経由で格納されたディレクトリは60MBにまで圧縮されるのである。

 このHOWTOファイルを用いたcompFUSEdの試験では、いくつかの不具合に遭遇した。その1つはchunk_sizeの設定値を8192から変更すると極度に不安定化するというもので、この設定をかなり大きな値とすること自体に問題はないはずなのだが、32KB近辺の値に変更したところcompFUSEdはクラッシュしてしまった。またこうした不安定性は、ファイルシステムにおけるディレクトリツリーの格納に関連しても発生するようであり、事前にサブディレクトリ群を作成することなく個々のファイルをcompFUSEdファイルシステムに直接コピーするという操作は問題なく進行するのだが、compFUSEdファイルシステムにてHOWTOのtarボールを直接展開したり、既存の展開済みディレクトリツリーをコピーするという操作を試みると、compFUSEdはクラッシュしてしまうのである。なおここでのcompFUSEdファイルシステムに対する試験に際しては、以下に抜粋した設定ファイル部およびコマンド群を用いた設定変更を行った。

$ vi ~/.compFUSEd
...
[/home/ben/howto]
backend = /home/ben/.howto.backend
compression = /usr/local/lib/compFUSEd/plugins/cf_bzip2.so
writer = /usr/local/lib/compFUSEd/plugins/writer_isimple.so
chunk_max = 100
chunk_size = 1024000
exclude = gif png jpg jpeg xpm xpi gz tar.gz
...
$ mkdir  ~/howto ~/.howto.backend
$ compFUSEd ~/howto
$ cd /tmp
$ tar xjf /.../Linux-html-HOWTOs.tar.bz2
$ find /tmp/HOWTO  -exec cp {} ~/howto/ \;
...

 正常に圧縮できることが既知のファイル群で構成されたディレクトリを格納させ、独自の圧縮機能を有さないアプリケーションにそうしたファイルを読み込ませるという用途であれば、compFUSEdを試してみる価値は充分にあるだろう。ただし現状で残留する若干のバグの存在が、ディレクトリツリーをターゲットにした圧縮ソリューションというcompFUSEdの有用性を引き下げている点が残念である。

Ben Martinは10年以上にわたってファイルシステムに取り組んでおり、博士課程の修了後、現在はlibferris、ファイルシステム、検索ソリューションを中心としたコンサルティング業に従事している。

Linux.com 原文(2008年6月10日)