横着プログラミング 第8回: pdumpfs: 毎日のスナップショットを保存する

最終更新日: 2002-11-18 (公開日: 2002-11-18)

Unix Magazine 誌に 2002年1月号から 2003年2月号にかけて連載し ていた記事の元の原稿です。


The steady state of disks is full. 
(ディスクの定常状態はフルだ)
-- Ken Thompson

*1

ハードディスクの容量がどんなに増えても、ディスクがいっぱいに なるまで必ず消費される、という法則がある。実際、私はつい 3年 ほど前までは 4Gバイトのハードディスクで間に合っていたが、現 在は 70 Gバイトのディスクをほぼフルに使っている。

70Gバイトも何に使っているか改めて調べてみると、

  1. MP3の音楽データ
  2. MP3で録音したラジオ講座
  3. Linux ディストリビューションなどのアーカイブ
  4. 英和や国語などの電子辞書
  5. 論文などの文書アーカイブ
  6. ホームディレクトリ以下のファイル

の順にディスクを消費していることがわかった。このうち、1から5 までは CDをリッピングするなり Webからダウンロードするなりし て手に入れたいわば他人のコンテンツであるのに対し、6 は自作の プログラムやメール、メモといった個人的なコンテンツである点が 異なる。私にとって特に大切なのは後者だが、前者も手間をかけて 蓄積したものであり、失いたくない。

2001年4月号の「VMware で UNIX」で大江将史氏は、修士論文を提 出する前日にディスクがクラッシュして論文をすべて打ち直したと いう涙ぐましい逸話を紹介している。私もそこまで悲惨ではないも のの、 1週間集中して書いたソースコードをすべて失うという痛い 目にあったことがある。以来、バックアップには気を使って生活を 送っている。

バックアップの方法は大きく次の 2つに分けて考えられる。

  1. ディスク単位でバックアップする
  2. ファイル単位でバックアップする

前者の方法は RAID システムを構成したり DATテープにダンプする といった大掛かりな仕組みを必要だが、後者は cp コマンドで大切 なファイルを別のディレクトリにコピーするといった手軽な方法で 行える。いざというときの復旧方法も後者なら簡単である。

そこで、今回はファイル単位でバックアップするノウハウを取り上 げ、私が開発した pdumpfs というバックアップシステムを紹介す る。

バックアップの基本

バックアップの基本はファイルをコピーすることである。たとえば /home/satoru というディレクトリを /backup の下にバックアップ するには

% cp -Rp /home/satoru /backup

と実行すればいい。/backup/satoru というディレクトリが作成さ れ、/home/satoru 以下のファイルがすべてコピーされる。cp の -R オプションはディレクトリをまるごとコピーする、 -p はファ イルのタイムスタンプやパーミッションなどの属性を保持するとい う意味を持つ。

Linux や Cygwin などの環境で GNU fileutils *2 の cp を使っている場合は

% cp -a /home/satoru /backup

のように -a オプションを使うとコピーの際にシンボリックリンク やハードリンクが適切に扱われて便利である。また、GNU の cp で は

% cp -au /home/satoru /backup

のように -u オプションを指定すると、コピー先にすでに存在する ファイルよりも新しいファイルだけがコピーされる。この機能は、 大量のファイルを定期的にコピーする際に時間を短縮する効果があ る。

cp コマンドの注意点

訂正: この部分の記述は FreeBSD 4.5 の /bin/cp コマンド の挙動を元にして書きました。しかし、後から調べたところ、 NetBSD 1.5.1 では同様の挙動を示すものの、GNU の fileutils の cp コマンドや Solaris 2.5 の /bin/cp では

% cp -Rp /home/satoru /backup 

% cp -Rp /home/satoru/ /backup 

も同じ動作をすることがわかりました。このため、下の

cp コマンドを使ってディレクトリをまるごとコピーする際、コピー 
元ディレクトリの末尾に / をつけたときとつけないときで挙動が 
異なるので注意する必要がある。

という主張はすべてのシステムで通用するわけではありません。不 正確な記事を書いてしまい、もうしわけありません。

...

cp コマンドを使ってディレクトリをまるごとコピーする際、コピー 元ディレクトリの末尾に / をつけたときとつけないときで挙動が 異なるので注意する必要がある。たとえば、最初の例のように

% cp -Rp /home/satoru /backup

と実行したときは、/home/satoru 以下のファイルは /backup/satoru というディレクトリ以下にコピーされる。つまり、 /home/satoru/foo.txt というファイルは /backup/satoru/foo.txt としてコピーされる。

一方、次のようにコピー元ディレクトリの末尾に / をつけると、

% cp -Rp /home/satoru/ /backup

/home/satoru 以下のファイルは /backup というディレクトリの直 下にコピーされる。つまり、 /home/satoru/foo.txt というファイ ルは /backup/foo.txt としてコピーされる。

この挙動の違いはたいへん紛らわしいため、私は混乱を避けるため に、できるだけコピー元のディレクトリに / をつけない前者の表 記だけを利用している。ただし、この方針をとると、次のような場 面で悩まされる。

# ディレクトリ foo だけが存在する
% ls
foo

# foo の中に abc.txt が存在する
% ls foo
abc.txt

# ディレクトリ foo をまるごと bar としてコピー
% cp -Rp foo bar

# ディレクトリ bar が作成された
% ls
foo bar

# foo/abc.txt を編集
% vi foo/abc.txt

# ふたたび同じようにコピーすると...
% cp -Rp foo bar

# 同じようにコピーしたつもりなのに bar の下に foo がコピーされてしまった! 
% ls bar
abc.txt   foo 

この場合は仕方がないので、foo の末尾に / をつけて bar の直下 にコピーしている。

% cp -Rp foo/ bar

安全なバックアップ

バックアップの目的はファイルの紛失を防ぐことにある。ファイル を紛失する原因は主に次の 2つが考えられる。

  1. 不用意に大切なファイルを削除してしまう (例: rm -rf /home/satoru/work/unimag)
  2. ディスククラッシュ (例: ハードディスクが物理的に故障する)

1の操作ミスによるファイルの紛失の場合は、大切なファイルを別 のディレクトリにバックアップしておけば、元のファイ ルを取り戻すことができる。たとえば /home/satoru 以下のファイ ルをすべて /backup にバックアップしておけば、/home/satoru 以 下のファイルを紛失しても /backup から取り戻せる。

しかし、このようにバックアップしていても、2のディスククラッ シュの場合は、 /home/satoru と /backup が物理的に同じハード ディスクに存在した場合は取り戻せる可能性は非常に低い。ハード ディスクは計算機の部品の中でも特に壊れやすいため、ディスクク ラッシュには備えていないと不安である。そこで、私は /backup に 2台目のハードディスクを割り当てて、物理的に異なるハードディ スクにバックアップを取っている。このようにしておけば、1台の ハードディスクが故障してももう 1台の方からファイルを取り戻せ る。

物理的に異なるハードディスクにバックアップを取っていても、地 震や火事などで計算機がまるごと破壊されるような場合には対処で きない。こういった事態に備えるには、次に紹介する rsync など を利用して地理的に異なる位置に存在する計算機にネットワーク越 しにバックアップする方法がある。また、ノートPCのように 2台の ハードディスクを内蔵できない計算機ではネットワーク越しのバッ クアップは不可欠な手段である。

rsync を利用したバックアップ

cp コマンドを利用したバックアップには、ファイルの削除が反映 されないというという欠点がある。たとえば、次のように実行した 場合、2回目のバックアップの後も /backup/satoru/tmp は残って しまう。

% cp -au /home/satoru /backup # バックアップ
% rm -rf /home/satoru/tmp     # ゴミファイルをすべて削除
% cp -au /home/satoru /backup # ふたたびバックアップ

削除したファイルがバックアップ先に残るというこの挙動は、ファ イルの紛失を防ぐという安全性という点では悪くないが、ゴミがど んどん溜まっていくのは気持ちが悪い。そこで、 rsync *3 を使うと、ファイルの削除をバッ クアップ先に反映させ、2つのディレクトリの内容を完全に同期で きる。たとえば rsync を使って /home/satoru というディレクト リを /backup の下にバックアップするには

% rsync -vau --delete --progress /home/satoru /backup

と実行すればいい。各オプションの意味は次の通りである。

ネットワーク越しのバックアップ

rsync は ssh*4 と組み合わせて、ネッ トワーク越しにファイルをバックアップする機能を備えている。 sshとは、暗号化された通信路でリモートログインするためのソフ トウェアである。たとえば、/home/satoru というディレクトリを foo.example.jp というホストの/backup の下にバックアップする には次のように実行する。

% rsync -vaue ssh --delete --progress /home/satoru foo.example.jp:/backup

rsync の注意点

rsync の --delete オプションは、コピー先のファイルが削除され るという性質をよく理解して慎重に取り扱う必要がある。たとえば、 次のような失敗はありがちである。

# Unix Magazine の原稿を間違って消してしまった
% rm -rf /home/satoru/work/unimag  

# うかつにもその直後にバックアップ
% rsync -vau --delete --progress /home/satoru /backup

# バックアップ先のファイルも消えてしまった!
% ls /backup/satoru/work/unimag 
ls: /backup/satoru/work/unimag: No such file or directory  

もうひとつ注意が必要なのは、cp コマンドと同様に、コピー元ディ レクトリ末尾の / の有無で挙動が変わるという仕様である。たと えば、

% rsync -vau --delete --progress /home/satoru /backup

% rsync -vau --delete --progress /home/satoru/ /backup

を比べると、前者の場合は /home/satoru 以下のファイルが /backup/satoru 以下にコピーされるのに対し、後者の場合は /home/satoru 以下のファイルが /backup の直下にコピーされると いう点が異なる。つまり、前者の場合は/home/satoru/foo.txt が /backup/satoru/foo.txt にコピーされ、後者の場合は /backup/foo.txt としてコピーされる。


delete オプションの危険性とコピー元ディレクトリ末尾の / の 有無による混乱に加えて root 権限が組み合わさると、 rsync が 引き起こす惨事は悪夢となる。私は以前に次のような出来事の傍観 者になったことがある。

共用の計算機において、foo 氏が NFSでマウントされた自分のホー ムディレクトリ /home/foo を、ローカルのディスク上の /local/home というディレクトリの下に移そうと考えた。そこで、

% rsync -au --delete /home/foo /local/home

と実行しようとしたところ、どういうわけか foo氏は間違って root 権限で

# rsync -au --delete /home/foo/ /local/home

と実行してしまい、/local/home 以下の他の人のホームディレクト リを消してしまった。幸い滅多に使われない計算機であったため被 害はそれほど大きくなかったが、たまたまこの計算機を好んで使っ ていた bar氏は数か月の作業が吹っ飛んでしまったと嘆き悲しんで いた (バックアップが取ってあれば…)。不幸な話である。

このような惨事を避けるためには、rsync に -n オプションをつけ て実行するといい。rsync の -n オプションは make の -n オプショ ンと同様に、ファイルのコピーや削除といった実際の処理を行わず に、どんな処理が行われるかを事前に確認するためのものである。

バックアップの自動化

バックアップは定期的に行うことが望ましい。しかし、「気が向い たときにバックアップしよう」という方針では、ついつい忘れがち になってしまう。そこで、Unix の cron の仕組みを使ってバック アップを自動化するといい。cron とは、決められた時間にコマン ドを実行するためのシステムである。

cron を利用するには

% crontab -e

のように実行して、crontab と呼ばれるファイルを編集する。 crontab コマンドを実行すると、環境変数 EDITOR で設定されたエ ディタが呼び出される。たとえば毎晩 1:23 に rsync コマンドで バックアップを行うには crontab に次の行を書き込めばいい。

23 01 * * * rsync -au --delete /home/satoru /backup

crontab では時・分だけでなく曜日・月・年の単位での指定も行え るため、より高度なバックアップのスケジュールを組むこともでき る。詳しくは crontab のマニュアルを参照していただきたい。

このようにバックアップを毎晩自動で行えば、ファイルを紛失した ときに手軽に前日のファイルを取り戻せる。しかし、この方法では、 1週間前や 1か月前のファイルは取り戻せないという不満が残る。 たとえば、私の場合は、バージョン管理を怠ってソースコードを修 正しているうちに収拾がつかなくなってしまうことがときどきある が、こうした場面では 2, 3日ほど遡って過去のファイルを参照した い。バージョン管理を怠るのがいけないという意見もあるが、ちょっ としたプログラムの場合は、いちいち CVS*5 で管理するのは いかにも面倒くさい。そこで、私は過去のファイルをいつでも参照 できるバックアップシステム pdumpfs を開発した。

pdumpfs とは?

pdumpfs*6 は毎日のス ナップショットを保存するバックアップシステムである。pdumpfs で毎日バックアップを取っていれば、 1週間前や 1か月前といった 過去のファイルをいつでも取り戻すことができる。

毎日のスナップショットを保存するというアイディアは新しいもの ではなく、Plan9*7 とい う OS に標準で採用されているバックアップシステム dumpfs から 借用したアイディアである。Plan9 についての日本語の情報源とし ては愛知大学の有澤健治教授のサイト *8 が詳しい。

pdumpfs のインストール

pdumpfs のインストールは次のように行う。原稿執筆時点での最新 版はバージョン 0.5である。

% gzip -dc pdumpfs-0.5.tar.gz | tar xvf - 
% cd pdumpfs-0.5
% su
Password: (rootのパスワードを入力)
% cp pdumpfs /usr/local/bin

pdumpfs はオブジェクト指向スクリプト言語 Ruby*9で約 200 行の単純なプロ グラムである。もし Ruby の実行ファイル ruby が /usr/local/bin/ruby 以外にインストールされている場合は、 pdumpfs の 1行目の

#! /usr/local/bin/ruby

という行を修正する。

pdumpfs の使い方

pdumpfs の使い方は簡単である。たとえば /home/satoru というディ レクトリを /backup の下にバックアップするには

% pdumpfs /home/satoru /backup

のように実行すればいい。pdumpfs にはコマンドラインオプション は一切存在しない。また、cp や rsync とは異なり、コピー元ディ レクトリ末尾の / の有無による挙動の違いはない。

毎朝 5時に pdumpfs で /home/satoru を /backup の下にバックアッ プするには crontab に次の設定を加えればいい。

00 05 * * * pdumpfs /home/satoru /backup >/backup/log 2>/backup/error-log

/backup/log には通常のメッセージが、 /backup/error-log には エラーメッセージが記録される。

私は /pdumpfs に 3台目のハードディスクを割り当てて、昨年の11 月から自分のホームディレクトリを pdumpfs でバックアップして いる。運用を始めてから 8か月経過した現時点での /pdumpfs のディ スク消費量は 24GB である。1日あたり 100MB のディスクを消費し ている計算になる。このペースなら 70Gバイトのディスクが埋まる まであと 1年と4か月ほど持つ見込みである。

pdumpfs の仕組み

pdumpfs はバックアップ先ディレクトリに「年/月/日」の形式でス ナップショットを保存する。たとえば、2002年7月15日に pdumpfs を次のように実行すると

% pdumpfs /home/satoru /backup

/backup/2002/07/15/satoru というディレクトリが作成され、 /home/satoru 以下のファイルがその日のスナップショットとして コピーされる。翌日に同じコマンドを実行すると /backup/2002/07/16/satoru にスナップショットが作成される。 pdumpfs はこのように毎日のスナップショットを年月日で分類する ことによって、過去のファイルを日付を元に参照できるようにして いる。

このような毎日のスナップショットの保存は cp コマンドでもシェ ルスクリプトでちょっと工夫するだけで行えるが、単純にすべての ファイルを毎日コピーしていると、

という問題を抱えることになる。そこで、 pdumpfs はこの 2つの 問題を解決するために、2日目以降は前日のスナップショットに 対する差分としてバックアップを行うという方針を採用している。

具体的には、前日から更新のあったファイルのみを新たにコピーし、 更新されなかったファイルは前日のスナップショットのファイルに 対するハードリンクとして記録することによって、差分のバックアッ プを実現している。

前日から更新のあったファイルを調べる処理は、次のメソッドを利 用して行っている。

def same_file? (f1, f2)
  File.symlink?(f1) == false && File.symlink?(f2) == false && 
  File.file?(f1) && File.file?(f2) && 
  File.size(f1) == File.size(f2) && File.mtime(f1) == File.mtime(f2)
end

このメソッドは 2つのファイルを比較して、タイムスタンプとファ イルサイズがともに同じなら true を返し、どちらかでも異なれば false を返す。pdumpfs は前日のファイルと現在のファイルをこの メソッドで比較して、false であればファイルに更新があったと判 別する。ファイルの中身の確認は行っていないが、よほどめずらし いケースでない限り、この方法で十分に更新の有無を判別できる。

なぜハードリンク?

ハードリンクとは、あるファイルを別の名前 (ファイル名) でアク セスするためのファイルシステムの機能である。同様の機能にシン ボリックリンクがあるが、シンボリックリンクではリンク先のファ イルを削除するとリンクしたファイルにアクセスできなくなるのに 対し、ハードリンクではリンク先のファイルを削除してもリンクし たファイルにアクセスできるという点が異なる。

ハードリンクとシンボリックリンクの仕組みの違いについては 2001年8月号の本紙連載「シェルの魔術」に詳しく解説されている ので、ここではその性質の違いだけを簡単にみてみることにする。 コマンドラインから次のように実行すると、両者の性質の違いがわ かる。

シンボリックリンクの場合

% touch foo      # 空のファイル foo を作成
% ln -s foo bar  # foo へのシンボリックリンク bar を作成
% rm foo         # foo を削除すると
% ls bar         # リンクしたファイルにアクセスできなくなる
ls: bar: No such file or directory

ハードリンクの場合

% touch foo      # 空のファイル foo を作成
% ln foo bar     # foo へのハードリンク bar を作成
% rm foo         # foo を削除しても
% ls bar         # リンクしたファイルにアクセスできる
bar

pdumpfs はハードリンクのこの性質を利用して、差分によるバック アップを実現している。このため、

% rm -rf /backup/2002/07/15/satoru

のように前日のスナップショットをごっそり削除しても /backup/2002/07/16/satoru のスナップショットは被害を受けずに すむ。シンボリックリンクを利用した場合にはこうはいかない。

pdumpfs の制限事項

pdumpfs には次のような制限がある。

このうち、特に最後の制限には注意する必要がある。私は VMWare *10 の 2Gバイトのディスクイメージをホームディ レクトリに置いて pdumpfs を運用していたところ、毎晩このファ イルのコピーが行われてすぐにディスクが一杯になってしまった。 仕方がないので、現在は VMWare のディスクイメージは、ホームディ レクトリの外に置いて pdumpfs によるバックアップの対象外とし ている (代わりに rsync で /backup にバックアップしている)。

pdumpfs の応用

pdumpfs は元々、自分のホームディレクトリをバックアップするた めに作成したソフトウェアだが、 snapshot.debian.net*11 では pdumpfs を応用して、Debian GNU/Linux のディストリビューショ ンのスナップショットの提供を行っている。通常のアーカイブサイ トでは最新の Debian パッケージしか入手できないが、このサイト では過去のパッケージを遡って入手できる。たとえば、2002年 7月 14日の時点の Debian パッケージが欲しい場合は、 <http://snapshot.debian.net/archive/2002/07/14/> のような URLでアクセスすればいい。

snapshot.debian.net の Webサイトにはディスク消費量の推移を表 すグラフが掲載されている。このグラフをみると 1日あたり 0.6 G バイトのディスクが消費されていることがわかる。Debian GNU/Linux のディストリビューション全体のサイズが 50 Gバイト であることを考えると、snapshot.debian.net で使用されている 160 Gバイトのハードディスクでは約半年間分のスナップショット を保存できる計算になる。

ディスク消費量の推移
ディスク消費量の推移

おまけ: タイムスタンプ保存のすすめ

cpコマンドで -p オプションをつけずにファイルをコピーすると、 ファイルのタイムスタンプには現在の時刻が設定される。普段はこ れで困ることはないが、デジタルカメラで撮影した写真をコピーす るときにうっかり -p オプションをつけ忘れると、いつ撮影した写 真なのかわからなくなって不便である。

一方、タイムスタンプはソフトウェア考古学の見地からも貴重な資 料である。たとえば、全文検索システム Namazu*12 のサイトの<http://namazu.org/oldies/> にアクセスすると、初期 のバージョンのアーカイブが残っている。この一覧を見ると、どの バージョンがいつ公開されたかがよくわかるだけでなく、深夜や早 朝の公開が多いという傾向もつかめる。

namazu-010.tar.gz       25-Jul-1997 20:55    24k  
namazu-011.tar.gz       30-Jul-1997 05:14    27k  
namazu-012.tar.gz       31-Jul-1997 18:42    28k  
namazu-013.tar.gz       04-Aug-1997 02:13    35k  
namazu-014.tar.gz       04-Aug-1997 05:15    29k  
namazu-020.tar.gz       11-Aug-1997 16:02    29k  
namazu-021.tar.gz       11-Aug-1997 19:16    29k  
namazu-022.tar.gz       16-Aug-1997 06:15    30k  

実は、私は Namazu の Webサイトを移転する際にこれらのファイル をタイムスタンプを残さずに持ってきてしまい、後日それに気づい て古いサイトからファイルを取り直して正しいタイムスタンプを復 元したという経緯がある。タイムスタンプは気をつけていないと消 えてしまうもののようだ。

おわりに

今回はファイル単位でバックアップするノウハウを取り上げ、私が 開発した pdumpfs というバックアップシステムを紹介した。バッ クアップの大切さは 1度痛い目にあわないとわからないと言われる が、冒頭で紹介した大江氏や私の失敗を悪い見本として、バックアッ プを欠かさずに平和な Unix生活を送っていただきたい。


Satoru Takabayashi

*1ある日の fortune コマンドの出力から
*2<http://www.gnu.org/software/fileutils/fileutils.html>
*3<http://www.rsync.org/>
*4<http://openssh.com/>
*5<http://www.cvshome.org/>。2001年1月号の本誌連載「Unix Communication Notes」に詳しい解説がある。
*6<http://0xcc.net/pdumpfs/>
*7<http://www.cs.bell-labs.com/plan9dist/>
*8<http://plan9.aichi-u.ac.jp/>
*9<http://www.ruby-lang.org/>
*10<http://vmware.com/>。本誌1999年10月号の VMWare の特集記 事を参照のこと。
*11<http://snapshot.debian.net/>
*12<http://namazu.org/>。1997年に私が開発を始めたフリー ソフトウェア。現在はNamazu Project で共同開発を行っている。