unoh.github.com

10テラバイトマシンのつくりかた

Thu Jun 25 23:26:48 -0700 2009


「iPodの残り容量が200MBを切った」と社内で発言してから「iPhoneを買おう!」としきりに言われるようになったbokkoです。そんな私は先月、ホコリをかぶっていたデスクトップPCを筐体ごと買い換えました。今ではMacBookからSSHでログインしてターミナル上で快適な生活を送っています。

今月、2TBのHDDを6本使ったサーバを立てる機会がありまして、今日はその時のお話です。

HDDの容量とストレージサーバ



Webサービスのインフラを構築・運営していると、膨大なデータをどう扱うかといった問題にぶち当たることがあります。仮想化技術の進歩によって複数のOSを1台のマシンで同時に稼働させつつ、物理的なマシンの数を減らすことができるようにはなりましたが、

物理的な媒体であるHDDを1台のマシンに搭載できる数には限りがあり、ソフトウェアであるOSの仮想化みたいにじゃんじゃん増やすことができません。

なので、できる限り容量の大きいディスクを使いたいところです。しかし、RAIDを構築して冗長化したりするとかになるとディスクが余分に必要になるので、マシン1台あたりの最大容量が大幅に減ることになります。

普通のマシンのマザーボードだとSATAの口が数個しかありませんから、かなり厳しくなります。こういう場合、高価ですが、信頼性が高く、たくさんのディスクを扱うことのできるストレージ製品を使うという選択肢があります。この手の製品には大量のHDDを十数本並べてもパーティションがあたかも1個しかないように見えたりするものがあります。

パーティションが1個がしかないことによる利点はそのパーティションに割り当てられた容量を越えない限り、データは単純にそのパーティション内に放り込んでいくだけで、データを参照する際にデータはどこにあるのかなんてほとんど気にしなくて済むことです。

さらに、通常のマシンと違って、マザーボードにSATAの口が4~6本しかないなんてこともなく、大量のディスクを使うことができるので、RAID5やRAID6(およびホットスペア)のようにデータの保存とは別にディスクを大量に使う冗長化構成でも十分なディスク領域が確保できます。

ただ、当然ながらこの選択は初期の金銭的なコストが高くなります。これ以外の方法だと、例えば(テラバイト級の)膨大なデータを扱う場合にはストレージサーバをたくさん並べて、どのファイルがどのサーバのどの場所にあるのかといったメタデータを保存しておいて、

データを参照する際はそのメタデータを元にデータのありかを探すようにし、「実際にはストレージはたくさんあるんだけど、1個しかないように見せる」所謂、分散ストレージ的なシステムを構築するという方法があります。

こちらは初期の金銭的なコストは安くなりますが、さきほど述べた仕組みを(何らかのミドルウェア、例えばMogileFS等を使うにしろおそらくは)自前で構築することになるので、事前にちゃんと設計しないとかえって高くつくことになります。


で、フォト蔵はというと現在はPHPやMySQL、自作のApacheモジュールを駆使して、データを複数のサーバ上のディスクに分散させて読み書きする仕組みを構築しています。
最近はようやくストレージサーバの追加も比較的楽にできるようになりました。あと、新しい仕組みを構築する上で久しぶりに仕事でC言語を使いました。


ただ、時と場合によっては非常に大きなディスク領域を1つのパーティションとして扱いたい場面もあるかと思います。でも、普通のマシンでできるだけ簡単にやりたい。そういう時のためにLVMがあります。


前置きが長くなりました。では、LVMと2TBのHDD6本を使って10テラバイトなマシンを作ったときの様子を紹介します。ただし、写真はありません。

LVM



LVMはLogic Volume Managnerの略で、複数のHDDを1つの論理的なディスク領域(パーティション)として扱ったり、グループ化することができるディスク管理ユーティリティの総称です。
LVMを使うと複数のHDDを使って巨大なディスク領域を持つ1つのパーティションを作成したり、ディスク容量が足りなくなっても新しいHDDを追加していくつかのユーティリティコマンドを走らせるだけでディスク領域を拡張することができます。
最近のLinuxディストリビューションにはAnacondaをはじめとする便利なインストーラやGUIアプリケーションがあり、インストール時にパーティションの作成からフォーマットはもちろん、LVMの設定もグラフィカルに行うことができますが、ここでは敢えてコマンドラインでやります。

LVMの構成要素



LVMを使う前にLVMの重要な3つの構成要素について軽く紹介します。



PVは物理的なディスク領域(パーティション)を指し、この物理的なディスク領域をまとめたものがVG(ボリュームグループ)です。また、LVはこのVG内に作成され、実際にユーザが使う論理的なディスク領域(パーティション)になります。このへんは言葉で説明するよりも実際にやってみた方がわかりやすいので、とりえあえず、実際にLVMを使ってみましょう。

LVMパーティションを作成する



ディスクに対してLVMのユーティリティを実行するには、事前に各ディスク上にLVMパーティションを作成する必要があります。なので、まずfdiskを使って各ディスク上にLVMパーティションを作成します。各ディスクのパーティションは以下の通りです。

# fdisk -l /dev/sda
Disk /dev/sda: 2000.3 GB, 2000398934016 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          13      104391   83  Linux
/dev/sda2              14        6387    51199155   83  Linux
/dev/sda3            6388        6518     1052257+  82  Linux swap / Solaris
# fdisk -l /dev/sdb
Disk /dev/sdb: 2000.3 GB, 2000398934016 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
 
   Device Boot      Start         End      Blocks   Id  System
# fdisk -l /dev/sdc
Disk /dev/sdc: 2000.3 GB, 2000398934016 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
 
   Device Boot      Start         End      Blocks   Id  System
# fdisk -l /dev/sdd
Disk /dev/sdd: 2000.3 GB, 2000398934016 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
 
   Device Boot      Start         End      Blocks   Id  System
# fdisk -l /dev/sde
Disk /dev/sde: 2000.3 GB, 2000398934016 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
 
   Device Boot      Start         End      Blocks   Id  System
# fdisk -l /dev/sdf
Disk /dev/sdf: 2000.3 GB, 2000398934016 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
 
   Device Boot      Start         End      Blocks   Id  System
#


/dev/sdaにはブートやルート、およびスワップパーティションが存在しますが、残りのディスクは基本的に同じです。まず、以下のように/dev/sda上にLVMパーティションを作成します。

# fdisk /dev/sda
The number of cylinders for this disk is set to 243201.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)
 
Command (m for help): n
Command action
  e   extended
  p   primary partition (1-4)
p
Selected partition 4
First cylinder (6519-243201, default 6519):
Using default value 6519
Last cylinder or +size or +sizeM or +sizeK (6519-243201, default 243201):
Using default value 243201
 
Command (m for help): t
Partition number (1-4): 4
Hex code (type L to list codes): 8e
Changed system type of partition 4 to 8e (Linux LVM)
 
Command (m for help): p
 
Disk /dev/sda: 2000.3 GB, 2000398934016 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
 
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          13      104391   83  Linux
/dev/sda2              14        6387    51199155   83  Linux
/dev/sda3            6388        6518     1052257+  82  Linux swap / Solaris
/dev/sda4            6519      243201  1901156197+  8e  Linux LVM
 
Command (m for help): w
The partition table has been altered!
 
Calling ioctl() to re-read partition table.
 
WARNING: Re-reading the partition table failed with error 16: Device or resource busy.
The kernel still uses the old table.
The new table will be used at the next reboot.
Syncing disks
#


続いて/dev/sdb上にLVMパーティションを作成します。

# fdisk /dev/sdb
The number of cylinders for this disk is set to 243201.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)
 
Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-243201, default 1):
Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-243201, default 243201):
Using default value 243201
 
Command (m for help): p
 
Disk /dev/sdb: 2000.3 GB, 2000398934016 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
 
    Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1      243201  1953512001   83  Linux
 
Command (m for help): t
Selected partition 1
Hex code (type L to list codes): 8e
Changed system type of partition 1 to 8e (Linux LVM)
 
Command (m for help): p
 
Disk /dev/sdb: 2000.3 GB, 2000398934016 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1      243201  1953512001   8e  Linux LVM
 
Command (m for help): w
The partition table has been altered!
 
Calling ioctl() to re-read partition table.
Syncing disks.
#


ほかのディスクでも同じようにLVMパーティションを作成していきます。

# fdisk /dev/sdc
(略)
# fdisk /dev/sdd
(略)
# fdisk /dev/sde
(略)
# fdisk /dev/sdf
(略)
#


全部のデバイスでLVMパーティションを作成し終わったら実行結果を反映させるため、再起動します。ちなみにfdiskは2TB以下のディスク領域しか扱うことができないので、将来に備えてpartedのことを勉強しておくのもいいかもしれません。

PVを作成する



再起動したら次は各ディスクのLVMパーティションを使ってPVを作成します。まずは/dev/sda4から。

# pvcreate /dev/sda4
  Physical volume "/dev/sda4" successfully created
#


作成したPVの一覧を確認するにはpvdisplayコマンドを使います。

# pvdisplay
"/dev/sda4" is a new physical volume of "1.77 TB"
  --- NEW Physical volume ---
  PV Name               /dev/sda4
  VG Name
  PV Size               1.77 TB
  Allocatable           NO
  PE Size (KByte)       0
  Total PE              0
  Free PE               0
  Allocated PE          0
  PV UUID               7zZFwx-ccj9-CONj-F1fi-YD2Q-hdBT-m0rU5p
 
#					       


指定した/dev/sda4がPVとして登録されているのが確認できます。ほかのLVMパーティションに対しても同じようにPVを作成していきます。

# pvcreate /dev/sdb1
  Physical volume "/dev/sdb1" successfully created
# pvcreate /dev/sdc1
  Physical volume "/dev/sdc1" successfully created
# pvcreate /dev/sdd1
  Physical volume "/dev/sdd1" successfully created
# pvcreate /dev/sde1
  Physical volume "/dev/sde1" successfully created
# pvcreate /dev/sdf1
  Physical volume "/dev/sdf1" successfully created
# 


VGを作成する



次に、VolGroup00というVGを作成してそのVGに/dev/sda4のPVを追加します。

# vgcreate VolGroup00 /dev/sda4
  Volume group "VolGroup00" successfully created
#


VGの情報はvgdisplayで確認することができます。

# vgdisplay
  --- Volume group ---
  VG Name               VolGroup00
  System ID
  Format                lvm2
  Metadata Areas        1
  Metadata Sequence No  1
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                1
  Act PV                1
  VG Size               1.77 TB
  PE Size               32.00 MB
  Total PE              464149
  Alloc PE / Size       0 / 0
  Free  PE / Size       464149 / 1.77 TB
  VG UUID               tSSUzh-8aQQ-aZd7-SivA-dBiz-dQZp-tzgzvd
 
#					  


VGを拡張する



さきほど作成したVGにほかのPVを追加して容量を増やしてみます。

# vgextend VolGroup00 /dev/sdb1
  Volume group "VolGroup00" successfully extended
# 


追加できたかどうかvgdisplayで確認します。(VG Sizeに注目)

# vgdisplay
  --- Volume group ---
  VG Name               VolGroup00
  System ID
  Format                lvm2
  Metadata Areas        2
  Metadata Sequence No  2
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                2
  Act PV                2
  VG Size               3.59 TB
  PE Size               32.00 MB
  Total PE              941080
  Alloc PE / Size       0 / 0
  Free  PE / Size       941080 / 3.59 TB
  VG UUID               tSSUzh-8aQQ-aZd7-SivA-dBiz-dQZp-tzgzvd
# 


無事、追加できたようです。同じ要領で残りのディスクも追加していきます。

# vgextend VolGroup00 /dev/sdc1
  Volume group "VolGroup00" successfully extended
# vgextend VolGroup00 /dev/sdd1
  Volume group "VolGroup00" successfully extended
# vgextend VolGroup00 /dev/sde1
  Volume group "VolGroup00" successfully extended
# vgextend VolGroup00 /dev/sdf1
  Volume group "VolGroup00" successfully extended
#


もう一度lvdisplayで状態を確認してみましょう。

# vgdisplay
  --- Volume group ---
  VG Name               VolGroup00
  System ID
  Format                lvm2
  Metadata Areas        6
  Metadata Sequence No  13
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                1
  Open LV               1
  Max PV                0
  Cur PV                6
  Act PV                6
  VG Size               10.87 TB
  PE Size               32.00 MB
  Total PE              356098
  Alloc PE / Size       354878 / 10.83 TB
  Free  PE / Size       1220 / 38.12 GB
  VG UUID               KctZmn-mBM5-ZfNn-LyBH-iT0B-E8fW-Yn2rp1
#


これで10.87TBのVGができました。

LVを作成する



VGが作成できたら、その作成したVG内に実際に扱うことのできる論理的なディスク領域、LVを作成します。

# lvcreate -L10.8T -n lvol0 VolGroup00 # VolGroup00内にlvol0という名前で容量が10.8TBのLVを作成する
  Rounding up size to full physical extent 10.80 TB
  Logical volume "lvol0" created
#


作成できたかどうかはlvdisplayで確認します。

# lvdisplay
  --- Logical volume ---
  LV Name                /dev/VolGroup00/lvol0
  VG Name                VolGroup00
  LV UUID                YY3Mah-Yk2h-Sy06-9FVs-9bLx-vFp8-AyWheH
  LV Write Access        read/write
  LV Status              available
  # open                 0
  LV Size                10.80 TB
  Current LE             353895
  Segments               6
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:0
#			      


なお、初期のLVMではPE(物理エクステント)の最大数やカーネルの実装による制限で、LVの最大容量が(具体的な数字は忘れましたが、確か1TBか2TBぐらいに)制限されていましたが、
現在のLVM2はこの制限が大幅に緩和されています。(ただし、それでもファイルシステム等による制限は付きまといます)

また↑の方法とこのマシンだけだと冗長性が確保できないので、実際には何かしらの方法(例えばDRBD)で冗長化する必要があるのでしょう。

仕上げ



あとはこのLVをmkfs系のコマンドでフォーマットすれば大容量マシンの出来上がりです。