2009年10月28日水曜日

【LVM】1TBのHDD増設レポ

LVM(Logical Volume Manager)は、パーティションの前後関係やディスク容量を気にすること無く、望むなら二つ以上のディスクに渡ったり別のボリュームを飛び越えてでもボリュームを作ることが出来るツールで、このブログでは一度も言及していないが私はいつもお世話になっている。

今回は、右図のように、1TBのHDD(SATA2)を組み入れる。もともと、ボロボロの80GBのHDD(以下、sdc)がバックアップ用、OSを含むマスターファイルが120GBのボロボロのHDD(以下、sdb)に割り当てられていた。それぞれ、backup、storageというVGを割り当てて管理していたが、今回バックアップが溢れそうになったこと、サーバにデータを置いてnfsで管理する方法を採ることにしたことなどが原因で、新品の1TB(以下、sda)のHDDを購入した。既存の2つはIDEであるうえ、どちらもたいした容量がないため、二つ合わせてbackupとする※。また、アクセス速度と容量を兼ね備えた新品のHDDには、storageの役割をになわせるのが最適だ。つまり
  1. sdaをLVMでフォーマットし、
  2. sdbのデータをsdaに移行し、
  3. sdbをbackupに参加させる
という作業が必要となってくる。自宅サーバであること、当日疲れていたことなどがあり、あまり止めても要られないので、サーバの停止時間は1時間程度に収めたいが、LVMを使用していればOSの再インストールなしでこれらの一連の作業ができた。

※ pdumpfsで、一時ファイルや大きすぎるファイル(動画など)を除外してバックアップしているので、バックアップにはたいした容量は必要ない

1. sdaのフォーマット

このセクションは、新しいディスクをフォーマットしたりするだけなのでサービスを動かしたままで行えるが、運用中のディスクからコピーをとるので、CDブートが望ましい。
ただのフォーマットだが、これが案外ややこしい。sdbをバックアップに降格させるため、sdbのMBRと/bootパーティションをコピーすることが必要になる。
今回の場合は、sdbはsdb1(200MB,ext2)、sdb2(120GB,lvm)となっている。したがって、sda1とMBRだけあればいい。
ここで荒技なのだが、ddを使ってコピーをとる。
# dd if=/dev/sdb of=/dev/sda
これの欠点は、sdbの大きさ分、つまり120GBをすべてコピーしてしまうということ。当然意味が無いので、数秒で止める。すると、コピー速度が出てくるので、sdb1の大きさから、必要な部分がコピーし終わるまでの時間を計算して、その時間待つ。これで、狙った部分のみコピーできる。ちゃんと大きさを指定することもできるが、どうせフォーマット前のディスク、こっちのほうがはるかに速い。
あとは、fdiskでsda2を作成してやればできあがり。sda2はゴミが入っているので、pvcreateに-ff(force)オプションをつける。
# pvcreate -ff /dev/sda2
# vgextend storage /dev/sda2

ここまで終われば、現在VGstorageは現在1.12TBの大所帯になっている。これから、0.12TBを追い出すわけだが、ここからはオンラインではできないので、CDブートで残りの作業を進める。
# reboot


2. sdbからデータを追い出す

現在、storageはsda2とsdb2が使われているが、sdbは一度空にしなければならないので、sdbからデータを追い出す。そのためのコマンドが用意されている:pvmoveだ。
# pvmove -v /dev/sdb2

これで、すべてのLVがコピーされるまで待つ。ddとちがってLVだけをコピーするのでまだ速いが、結構時間がかかるので、-vオプションで暇つぶしをするのは必須。
そして、これが終わったら/dev/sdb2をstorageから脱退させる。
# vgreduce -v server /dev/sdb2

実際には、pvmove終了後からはサービスの稼働は可能と思われるが、念のためこのまま最後まで作業を進めた。

3. sdbをbackupにする

これで、sdb2は宙に浮いてしまった。これを今度はbackupに参加させる。また、backupの唯一のLV「backup01」を200GBまで拡大する。backup01はxfsなので、xfs_growfsコマンドを使えばマウントしたままリサイズすることができる。ひとつ注意点は、xfs_growfsは、デバイスファイルではなくマウントポイントを指定するということ。
# vgextend backup /dev/sdb2
# xfs_growfs /backup


まとめ

以上の行程を踏めば、pvmoveの時間に依存するが1時間かからずにすべての作業が完了する。割とクリティカルな端末でもLVMが効力を発揮できることが実証できたと思う。
ただ、今回のpvmoveは今回初めて使ったので、もしかしたら運用中に実行できたのかもしれない。これが可能ならば、システムを一切止めること無くディスクを増設することができるようになるかもしれない。

2009年10月17日土曜日

重複ファイルを表示「difflatten」

ファイル名などには意味がなく、内容だけに意味があるファイルの集合に、同じルールで適当に集められたファイルをマージする話。(製作動機は後述)

概要

普通、ファイルが同じかどうかはdiffコマンドで調べることができる。
$ diff a.jpg b.jpg
しかし、最初に述べたような状況の場合、diff a.jpg storage/などとはできない。私は「echo storage/* | xargs diff a.jpg」と考えたがこれはstorage以下にディレクトリがあるとうまくいかない。また、一気に多数のファイルの重複を確認しようとした場合、ちょっと頭をひねらなければならない。

そこで、「ディレクトリAとディレクトリBの中のファイルで同じものがあれば、そのファイル名を出力する」スクリプトを書いた。外出っぽいけどちょっと見つからなかったので。

うちのWebページ
で配布してます。

以下のように実行すると、「A = B」のような行が出てくる。これは、二つのファイルが同じであることを示している。

$ difflatten.rb src/ dst/
src/1880.jpg = dst/images/sky/sunset14.jpeg
src/994.jpg = dst/images/material/takuan.jpg


これを応用すれば、以下のようにするとdst/以下のファイルでsrc/以下にもあるファイルは(dst側から)すべて削除できる。

$ difflatten.rb src/ dst/ | awk -F' = ' '{print $2}' | xargs rm


仕組み

今回はRubyで実装し、ライブラリは標準のものしか使っていない。
まず、src以下を再帰的に走査して、すべてのファイルのサイズを取得し、dst以下を走査して、srcと同じサイズのものがあれば、ダイジェスト(SHA512)で二つを比較している。当初はダイジェストのみで比較していたが、srcに500個、dstに2000個程度のファイルを入れて試したところ37秒もかかってしまった。そこで、まずサイズを比較することで、ダイジェストの計算回数を最小限に抑え、実行時間を0.2秒にまで縮めることに成功した。実際には、dstに同じファイルが2つ無い限り、ダイジェストより直接比較した方が速いのだが、そこまで速度的に不利にはならないと思われる。

動機とか

最近、というかずっと画像収集をしているのだが、あまり時間も裂けないのでとりあえずzipで落としてきて、「うーん」と思った奴は適宜削除、というスタンスをとっていた。しかし結構同じ画像が入っていることが多かったので、自分のコレクションに入れる前に重複を確認するスクリプトを作って既に持っている画像を弾き出そうと考えた。
もちろん、加工されるとハッシュ値が変わるので、本当は画像の類似度などを考慮する必要があるが、補助的には使えると思う。
今回は「削除する」ではなく「重複を表示する」だけに止めたので、いろんな使い方ができるかもしれない。

ちなみに、動作にはruby(作者環境1.8)がインストールされていること、diffなのに同じ物を表示してるじゃん!とか言わないこと、引数を必ず2個与えることが要求されます。