はじめに
100GBとか200GBぐらいあるサイズのファイルを数百個扱っていると小さいパーティーションにはいつもイライラさせられる。そこで、6TB×12本のHDDでRAID6を組んで実効容量60TBの仮想ドライブ(いわゆるアレイですが、この記事では仮想ドライブで通します。)を作り、それを1つのパーティーションとしてフォーマットすることにした。この作業、何度やったか覚えていないぐらいやっているのだが、悲しいことに毎回毎回、完全に同じところで詰まるので、自分用の備忘録メモも兼ねてやり方の記事を書いておく。もちろん、みなさんにも便利なはず。
RAID6 の仮想ドライブを作る
ここのステップは RAID カードによって全然手順が違うので省略する。起動中に何かホットキーを押すと RAID の管理画面になって、そこで何かをするとできるのが普通。60TB もの大きなドライブを作りたい人はきっと巨大なファイルを扱っていてシーケンシャルアクセスもかなり多いはずなので、RAID のブロックサイズをなるべく大きくしておくとよい。デフォルトでは 64 KB ぐらいが選ばれていることも多いが今回は 1MB を用いた。これを忘れると平気でシーケンシャルリード速度が(最速のブロックサイズに対して)1/5ぐらいになったりするので侮れない。最適なブロックサイズは RAID カードや HDD の種類などにも依るので、スピードにシビアな大きなストレージを作る場合にはちゃんとブロックサイズを変えてベンチマークをした方が良い。ちなみに今回 1MB を用いたのはベンチマーク済みで 1MB が最も良いパフォーマンスであることが分かっていたからなので、決して適当に決めたわけではない。
パーティーションテーブルを作る
RAID6 を構成したばかりのタイミングでは、作った仮想ドライブにパーティションテーブルが存在していないのでパーティーションテーブルを作る必要がある。
パーティーションテーブルと言えば(私にとっては)ディスクの一番最初のセクタ(512バイト)に存在している MBR (Master Boot Record)に書き込まれているパーティション情報のことであったのだが、MBR 方式では LBA アドレス(セクタ番号)を 32 ビット整数で管理しているので 2TB より大きなハードディスクが扱えない。そこで、GPT(GUID Partition Table)を使った新しいタイプのパーティションテーブルを構築する。MBR なら fdisk で作っていたところ、GPT を構築するには parted を叩く必要がある。以下に、/dev/sdc 上に GPT を書き込んで初期化する例を示す。
% sudo parted
GNU Parted 2.1
Using /dev/sdc
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) p
Error: /dev/sdc: unrecognised disk label
(parted) mklabel gpt
(parted) p
Model: LSI MR9260-8i (scsi)
Disk /dev/sdc: 60.0TB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Number Start End Size File system Name Flags
(parted)
最初に p (print) コマンドを実行しても unrecognised disk label
とエラーが出るのは MBR も GPT も無いのでパーティーションが表示できないエラーだ。私はたまにデバイス名を間違えて大事なデータの入ったディスクを良く吹っ飛ばすので、この確認ステップは外せない。これからパーティーションテーブルを書き込もうと(初期化しようと)しているディスクが新規のディスクであり、パーティションテーブルすら存在していない(=大事なデータなどは当然含まれていない)ことを確認するための大事な儀式なのだ。最後に quit しないと(確か)書き込まれないので、CTRL+C で止めてしまったりしたら最初からやりなおし(のはず)。
GPT ができたら次にディスクの空き領域全体を1つのパーティションとして構成する。パーティションを作るコマンドは mkpart である。パーティーションの名前は何でもよい。次にファイルシステムの種類を聞かれるので xfs と答えておく。xfs をサポートしていないカーネルを使っている場合には xfsprogs なりなんなりを入れて使えるようにしておく必要がある(今回はたまたま必要なかった)。ext4 は 8TB が安全圏でいろいろ頑張っても最大 16TB なので当然使えない。ReiserFS は(速くて好きだったけど)作者が大変なことになって以来、なんとなくメンテナンスに不安を感じて使っていないし、JFS は最近使っている人が少ない気がするので怖くて使っていないし、こんな大容量のディスクを実績の薄い btrfs にしてしまうのは業務用としてはあり得なかったので、ファイルシステムの種類は xfs で決まりである。次点で ZFS なら多少許せるが、無理して Linux で使うものではないよね(と私は思う)。
(parted) mkpart
Partition name? []? data
File system type? [ext2]? xfs
Start? 1mb
End? -1
(parted) p
Model: LSI MR9260-8i (scsi)
Disk /dev/sdc: 60.0TB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Number Start End Size File system Name Flags
1 1049kB 60.0TB 60.0TB data
(parted) quit
Information: You may need to update /etc/fstab.
Start は 1mb, End には -1 と答える。先頭には GPT の管理区域とかがあるので 17.4kb だったかその程度のサイズを持って行かれる関係上、Start を 0 に設定することはできない。Start が RAID のブロックサイズで割り切れないときっとパフォーマンスのロスがあると思ったなんとなく気持ち悪いので、なんとなく RAID のブロックサイズの 1MB を Start にしている。17.4kb から 1MB までの領域は無駄になるが、60TB のドライブを持っているときにそれぐらいは誤差だ。私の場合、End の -1 (ドライブの末尾)を忘れてしまっていていつも 45.5TB などと目で見た数字を入力し、最後の端数がパーティションに含まれなくなっていたりすることが多い。ちなみに負の数は末尾からの長さを表しているので、-1s でも -1mb でもなんでも任意の単位が書ける。
今回は特に警告が出ていないが、この Start と End は適当に設定すると GNU parted が「そのアラインメントではまずいですよー。遅いかも?」みたいな警告メッセージを出すことがあるので、Start と End をどう決めたら良いかを書いておく。詳しくはこのスレッドを見てもらえればいいのだが、日本語でもまとめておく。まず、以下のようなコマンド(sdc のところはあなたのデバイスに合わせて変更してくれ)で、そのデバイスの最適な I/O のサイズ、アラインメントオフセット、物理セクタサイズ(普通は 512B か 4KB だ)をそれぞれ調べる。
% cat /sys/block/sdc/queue/optimal_io_size
1048576
% cat /sys/block/sdb/alignment_offset
0
% cat /sys/block/sdc/queue/physical_block_size
512
上の例はリンク先からのパクりなので私の環境における結果ではないが、仮に上のような結果が得られたとすると、「アラインメントオフセット+最適 I/O サイズ」÷「物理セクタサイズ」がパーティーションを開始するべきセクタ番号となる。この場合は (0 + 1048576)/512 = 2048 セクタとなり、Start には 2048s (最後の s はセクタを表す)を指定すれば良い。上のリンク先には「最適I/Oサイズ」として 0 が表示されたら別の記事にある計算方法を使ってね、と言っていて、私の環境ではまさにその状態だった。詳しくはリンク先を読んでほしいのだが、RAID6で作った(いまどきの)普通の仮想ドライブの場合には要するに 1MiB (1024×1024バイト) を Start として使えば良いようだ。
ともかく、ちゃんとアラインメントされたパーティーションを作り終わったところでフォーマットする。今回は /dev/sdc1 をフォーマットするが、sudo mount /dev/sdc1 /mnt
を実行してエラーが出る(/dev/sdc1 は既に作ってフォーマットしてありデータが入っているパーティーションではない)ことを確認してから /dev/sdc1 の名前を壊さないようにシェル履歴から直前の sudo mount /dev/sdc1 /mnt
を呼び出して mount
を mkfs.xfs
に書き換え、実行する。ミスると既存パーティーションを壊すのでデバイス名を絶対に(1から)手打ちしないのが大事。
% sudo mkfs.xfs /dev/sdc1
meta-data=/dev/sdc1 isize=256 agcount=55, agsize=268435455 blks
= sectsz=4096 attr=2, projid32bit=0
data = bsize=4096 blocks=14649916928, imaxpct=1
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0
log =internal log bsize=4096 blocks=521728, version=2
= sectsz=4096 sunit=1 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
/etc/fstab の書き換え
次に、次回起動時にマウントされるように /etc/fstab を書き換える。マウントする際には /dev/sdc1 とか書いても良いのだが、デバイス名は何かのはずみにしょっちゅう変更されてしまっていつも泣きを見るので UUID でマウントしておこう。作ったパーティーションの UUID を調べる。
% cd /dev/disk/by-uuid
% ls -al
total 0
drwxr-xr-x 2 root root 140 Mar 28 23:31 .
drwxr-xr-x 5 root root 100 Mar 29 2015 ..
lrwxrwxrwx 1 root root 10 Mar 28 19:09 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -> ../../dm-1
lrwxrwxrwx 1 root root 10 Mar 28 19:09 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -> ../../dm-0
lrwxrwxrwx 1 root root 13 Mar 28 19:09 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -> ../../md127p1
lrwxrwxrwx 1 root root 10 Mar 28 23:31 ec818a31-d294-fc1a-4cdf-af7486fadfd7 -> ../../sdc1
lrwxrwxrwx 1 root root 10 Mar 28 19:09 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -> ../../dm-2
ここで表示されるデバイス群はもちろん環境によって異なる。表示されるシンボリックリンクの名前(リンク先がデバイス名を表している)がそのデバイスの UUID を表している。この UUID は漏れても構わないような気がするが念のためにほとんどを伏せさせてもらった。これで /dev/sdc1 の UUID がめでたく分かったので /etc/fstab に以下のような一行を追加する。もちろん /mountpoint は実際にマウントしたいパスを書いておくし、defaults のところは何か必要なオプションを記述したらよいし、1 2 のところも適宜 dump とか fsck 向けに設定を書くべし。データしか格納する予定が無いなら下記の例と同じで 1 2
と書いておけば大きな問題は発生しないと思う。
UUID=ec818a31-d294-fc1a-4cdf-af7486fadfd7 /mountpoint xfs defaults 1 2
マウントポイントはちゃんと作っておこう。必要ならパーミッションなどの設定も忘れずに。
% sudo mkdir /mountpoint
最終確認
ここまで終わったら一応マウントしてみると良いだろう。
% sudo mount /mountpoint
% df -h | grep sdc
/dev/sdc1 55T 35M 55T 1% /export
これで 55TiB のパーティーションがめでたく作成された。巨大なファイルも作成し放題である。
リブートが許される環境であれば一度リブートしてマウントされるか確認してもよいだろう。