はじめに

dm-iobandは、LinuxでディスクI/Oの帯域制御を行うdevice-mapperのデバイスドライバです。パーティション、ユーザ、プロセス、仮想マシン等、様々な単位で帯域を制御することができます。本文書では、このdm-iobandを使ってRed Hat Enterprise Linux 5(以下、RHEL5)および CentOS 5の論理ボリュームの帯域制御を行う方法を紹介します。

dm-iobandの仕組み

dm-iobandは既存のブロックデバイスの上にdevice-mapperのデバイスをスタックして利用します。このデバイスに対して出されたI/O要求は、dm-iobandで帯域制御されてから下位のデバイスに渡されます。
帯域制御の方式は三種類あり、それぞれ、weightポリシー、weight-iosizeポリシー、range-bwポリシーと呼びます。ユーザはこの中から任意のポリシーを撰択して帯域制御を行います。各ポリシーの特徴は以下の通りです。

weightポリシー

パーティションやプロセスといった帯域を制御する単位毎にウエイトを割り当て、ウエイトの比率に従って帯域を分配します。例えばデバイスsda上に二つの論理ボリュームLogVol00とLogVol01があり、それぞれにデバイスioband00とioband01をスタックしてウエイトを60:40で割り当てた場合には、ioband00にはsdaの帯域の60%、ioband01にはsdaの帯域の40%が割り当てられることになります。

この時のdm-iobandの動作を説明します。 まず最初にdm-iobandは設定されたウエイトの比率に従って各デバイスにトークンを配ります。ここではioband00に60個、ioband01に40個のトークンを配ることとします。各デバイスはI/O要求を受け取ると、トークンが有る場合にはトークンを一つ減らして下位のデバイスにI/O要求を渡し、無い場合にはそのI/O要求をdm-ioband内に保留します。そして、

 

  • 両方のデバイスのトークンが0になった。
  • 一方のデバイスのトークンが0になり、かつもう一方のデバイスはI/O要求を受け取っていない。

という状態になると、dm-iobandは各デバイスにトークンを補充し、保留されていたI/O要求は下位のデバイスに渡されます。ioband00とioband01に対して常にI/O要求が出されている状態では、トークンが補充される間隔でioband00が60回、ioband01が40回のI/Oを行うことになります。従って、sdaの帯域がウエイトの比率に従って分配されることになります。

 

weight-iosizeポリシー

weightポリシーと同様にウエイトの比率に従って帯域を分配しますが、weightポリシーがI/O回数に基づいて帯域を分配するのに対して、weight-iosizeポリシーではI/Oサイズ(セクタ数)に基づいて帯域を分配します。dm-iobandの動作でweightポリシーと異なるのは、下位のデバイスにI/O要求を渡す時に、I/Oするセクタの数だけトークンを減らす点です。前述したweightポリシーの例に当てはめると、トークンが補充される間隔でioband00が60セクタ、ioband01が40セクタ分のI/Oを行うことになります。

range-bw ポリシー

パーティションやプロセスといった帯域を制御する単位毎に、帯域の保証値と制限値をKbytes/secで指定します。帯域は制限値を超えない範囲で保証値以上のスループットが維持できるように制御されます。

dm-iobandを動かす

それでは、実際にdm-iobandを使って論理ボリュームの帯域制御を行う方法を説明します。

パッケージのダウンロードとインストール

以下の二つのRPMパッケージを入手します。

(1)dm-iobandパッケージ
帯域制御を行うデバイスドライバが含まれています。以下からお使いのアーキテクチャやカーネルの種類に適合するパッケージを選んでダウンロードして下さい。

i386(i686)用

x86_64用

(2)dm-ioband-configパッケージ
LVMを使ったシステムで簡単にdm-iobandを利用するためのスクリプトや設定ファイルが含まれています。以下からダウンロードして下さい。
http://sourceforge.net/projects/ioband/files/dm-ioband-config/dm-ioband-config-1.0.0-1.noarch.rpm/download

ダウンロードが済みましたら、rpmコマンドを使ってインストールします。

# rpm -ivh kmod-dm-ioband-1.12.0-1.x86_64.rpm
# rpm -ivh dm-ioband-config-1.0.0.-1.noarch.rpm

設定ファイル

dm-iobandの設定は、/etc/ioband.confに記述します。以下に設定ファイルの例を示します。

# Configuration file for dm-ioband
#
# device ID thrott limit policy weight token
/dev/VolGroup01/LogVol00 vg1 0 0 weight 20 0
/dev/VolGroup01/LogVol01 vg1 0 0 weight 10 0
#
# device ID N/A limit policy bandwidth N/A
/dev/VolGroup02/LogVol00 vg2 0 0 range-bw 100:200 0
/dev/VolGroup02/LogVol01 vg2 0 0 rangw-bw 100:500 0

設定ファイルには帯域を制御する論理ボリュームを一行ずつ記述します。先頭が#で始まる行はコメントとして無視されます。行の各フィールドの意味は撰択したポリシーによって異なります。

weightおよびweight-iosizeポリシー撰択時の各フィールドの解説

  • 1番目のフィールド(デバイス)
    論理ボリュームを指すデバイスファイルのパスを記述します。
  • 2番目のフィールド(ID)
    論理ボリュームに付けるIDを指定します。帯域制御は同じIDを持つ論理ボリュームの間で行われ、通常、同じハードディスク上に作られた論理ボリュームには同じIDを指定します。以降の説明では、この同じIDを持つ論理ボリュームの集まりを「帯域共有グループ」と呼びます。【図1】を設定ファイルにすると、下記のとおりとなります。
# Configuration file for dm-ioband
#
# device ID thrott limit policy weight token
/dev/VolGroup00/LogVol00 sda 0 0 weight 20 0
/dev/VolGroup00/LogVol01 sda 0 0 weight 10 0
/dev/VolGroup00/LogVol02 sdb 0 0 weight 20 0
/dev/VolGroup00/LogVol03 sdb 0 0 weight 10 0
  • 3番目のフィールド(I/O スロットル)
    I/Oをしていなかった論理ボリュームに対してI/Oを始めた時に、dm-iobandが受け付けたI/O要求の数がI/Oスロットルに設定した値以上になったならば、dm-iobandはその論理ボリュームのトークンが無くなるまでI/O要求を優先的に下位デバイスに渡します。0を指定するとデフォルト値として4が設定されます。
  • 4番目のフィールド(I/O リミット)
    帯域共有グループが同時に受け取ることのできるI/O要求の数を指定します。通常、帯域制御するデバイスのnr_requests(ブロックレイヤーで割り当てることのできるI/O要求の数の上限)を1.5倍した値を指定します。1.5倍する理由ですが、ブロックレイヤーではI/O負荷の高い時に、一時的にnr_requestsを1.5倍した数までI/O要求を受け付ける場合があるからです。
    0を指定するとデフォルト値として192が設定されます。帯域制御するデバイスのnr_requests(I/O要求キューの最大長)が128以外だった場合には、nr_requestsを1.5倍した値を指定します。

例)/dev/sda上に論理ボリュームが作られている場合

1)/dev/sdaのnr_requestsを調べる

 

# cat /sys/block/sda/queue/nr_requests
64

2)1.5倍した値(64 * 1.5 = 96)を記述する

# device ID thrott limit policy weight token
/dev/VolGroup00/LogVol00 sda 0 96 weight 20 0
/dev/VolGroup00/LogVol01 sda 0 96 weight 10 0
  • 5番目のフィールド(ポリシー)
    “weight”もしくは”weight-iosize”を指定します。
  • 6番目のフィールド(ウエイト)
    各論理ボリュームのウエイトを指定します。以下にウエイトと帯域の関係を示します。
  ID ウエイト 各論理ボリュームの帯域
LogVol00 sda 80 80/(80+20)*100=80% sdaの帯域の80%
LogVol01 sda 20 20/(80+20)*100=20% sdaの帯域の20%
LogVol02 sdb 30 30/(30+60)*100=33% sdbの帯域の33%
LogVol03 sdb 60 60/(30+60)*100=67% sdbの帯域の67%

 

  • 7番目のフィールド(トークン)
    帯域共有グループに割り当てるトークンの数を指定します。0を指定するとweightポリシーの時はI/Oリミットの4倍の値が、weight-iosizeポリシーの場合は32倍の値が設定されます。前節【dm-iobandの仕組み】にて解説しましたように、トークンはI/O中に消費と補充を繰り返します。トークンの数がデバイスの速度に対して少な過ぎるとトークンの補充が頻繁に起きることになるので、そのオーバーヘッドがスループットの低下を招きます。また、トークンの数がデバイスの速度に対して多過ぎると、トークンの補充間隔が長くなるので帯域制御の粒度が粗くなります。補充のオーバーヘッドがスループットに与える影響を小さくするには、トークンの消費時間を少なくとも100ms以上にする必要があると思われます。
    以下では具体的にトークンの設定値を計算する方法を説明します。

1)1000IOPSの性能を持つディスク上に三つの論理ボリュームがあり、weightポリシーで帯域制御を行うこととします。

# device ID thrott limit policy weight token
/dev/VolGroup00/LogVol00 sda 0 0 weight 70 0
/dev/VolGroup00/LogVol01 sda 0 0 weight 20 0
/dev/VolGroup00/LogVol02 sda 0 0 weight 10 0

2)各論理ボリュームに個別にI/Oをした場合、最もウエイトの小さい論理ボリュームが最も短時間でトークンを消費することになるので、その時間が100msecになるようにトークンの数を求めます。

LogVol02のトークンの数 = トークンの消費時間[s] * IOPS
100           = 0.1          * 1000

3)ウエイトの比率に従って、他の論理ボリュームのトークンの数を求めます。

     トークンの数 = LogVol02のトークンの数 * LogVol02に対するウエイトの比率
LogVol00   700    = 100           * (70/10)
LogVol01   200    = 100           * (20/10)

4)各論理ボリュームのトークンの数を合計して、設定ファイルに記述します。

トークンの合計     = LogVol00 + LogVol01 + LogVol02
   1000       =  700   +  200   +  100

# device ID thrott limit policy weight token
/dev/VolGroup00/LogVol00 sda 0 0 weight 70 1000
/dev/VolGroup00/LogVol01 sda 0 0 weight 20 1000
/dev/VolGroup00/LogVol02 sda 0 0 weight 10 1000

range-bwポリシー撰択時の各フィールドの解説

  • 1番目のフィールド(デバイス)
    weightポリシーと同じです。
  • 2番目のフィールド(ID)
    weightポリシーと同じです。
  • 3番目のフィールド(I/O スロットル)
    range-bwポリシーでは使用しません。0を指定して下さい。
  • 4番目のフィールド(I/O リミット)
    weightポリシーと同じです。
  • 5番目のフィールド(ポリシー)
    “range-bw”を指定します。
  • 6番目のフィールド(帯域)
    各論理ボリュームの帯域の保証値/制限値を指定します。例えば保証値100KB/s、制限値200KB/sの場合には、”100:200″ と指定します。
# device                 ID  thrott limit policy   bandwidth token
/dev/VolGroup02/LogVol00 vg2      0     0 range-bw 100:200       0
/dev/VolGroup02/LogVol01 vg2      0     0 rangw-bw 100:500       0
  • 7番目のフィールド(トークン)
    range-bwポリシーでは使用しません。0を指定して下さい。

設定上の注意

3番目のフィールド(I/O スロットル)、4番目のフィールド(I/O リミット)、5番目のフィールド(ポリシー)、7番目のフィールド(トークン)の設定は、帯域共有グループ全体に適用されます。同じIDを持つ論理ボリュームには同じ値を指定するようにして下さい。

帯域制御の開始と停止

帯域制御の開始と停止はserviceコマンドを使って行います。

# service ioband start
dm-ioband を起動中:                                       [成功]
# service ioband stop
dm-ioband を停止中:                                       [成功]

開始や停止に失敗した場合には、/var/log/messages にエラーの内容が出力されます。

# service ioband start
dm-ioband を起動中:                                       [失敗]
# tail -1 /var/log/messages
Feb 18 16:05:54 ume dm-ioband: /dev/hda1: not a dm-device

service statusコマンドを使うとdm-iobandを使用しているデバイスの一覧が表示されます。

# service ioband status
VolGroup00-LogVol00: 0 134217728 ioband 253:3 vg1 4 192 none weight 768 :20
VolGroup00-LogVol01: 0 134217728 ioband 253:4 vg1 4 192 none weight 768 :10

【使用上の注意】
LVMの設定変更は、帯域制御を停止させた状態で行って下さい。動作中にLVMの設定を変更した場合には、帯域制御機能が無効になる場合があります。

動作確認

ベンチマークツールfioを使って帯域制御が行われていることを確認してみます。fioはFlexible IO Testerの略で、Jens Axboe氏の作成したツールです。以下が公式サイトです。

http://freshmeat.net/projects/fio/

RHEL5 x86_64用のRPMパッケージは以下からダウンロードすることができます。

ftp://ftp.riken.go.jp/pub/Linux/dag/redhat/el5/en/x86_64/dag/RPMS/fio-1.31-1.el5.rf.x86_64.rpm

以降の解説では、二つの論理ボリュームLogVol00とLogVol01が/mnt0と/mnt1にそれぞれext3でマウントされていることとします。

# mount
/dev/mapper/VolGroup01-LogVol00 on /mnt0 type ext3 (rw)
/dev/mapper/VolGroup01-LogVol01 on /mnt1 type ext3 (rw)

ベンチマークの準備として以下のコマンドを実行します。fioは/mnt0と/mnt1ディレクトリに1GBytesのファイルを作ります。

# fio --name=LogVol00 --directory=/mnt0 --size=1024m --time_based --runtime=1
# fio --name=LogVol01 --directory=/mnt1 --size=1024m --time_based --runtime=1

帯域制御の設定をします。weightポリシーを使用し、各論理ボリュームのウエイトはLogVol00:LogVol01 = 20:10とします。

# Configuration file for dm-ioband
#
# device                 ID  thrott limit policy weight token
/dev/VolGroup01/LogVol00 vg1      0     0 weight     20     0
/dev/VolGroup01/LogVol01 vg1      0     0 weight     10     0

帯域制御を開始します。

# service ioband start

以下のスクリプトを実行して、各論理ボリュームのスループットを測定します。

#!/bin/sh
arg="--time_based --runtime=60 --ioengine=libaio --iodepth=50 
        --direct=1 --norandommap --rw=randrw --size=1024m"
fio $arg --name=LogVol00 --directory=/mnt0 --output=LogVol00.log &
fio $arg --name=LogVol01 --directory=/mnt1 --output=LogVol01.log &
wait

このスクリプトは、各論理ボリュームに対して以下の条件でI/Oを行います。

 

  • ランダムリード/ライト
  • 実行時間60秒
  • 非同期I/O
  • I/O並列度(同時に発行するI/Oの数)50
  • ダイレクトI/O

結果はLogVol00.logとLogVol01.logに記録されます。以下は弊社にあるディスクアレイを使って測定した結果です。

LogVol00.logの抜粋

Run status group 0 (all jobs):
READ: io=60,864KiB, aggrb=1,033KiB/s, minb=1,033KiB/s, ...
WRITE: io=60,160KiB, aggrb=1,021KiB/s, minb=1,021KiB/s, ...

LogVol01.logの抜粋

Run status group 0 (all jobs):
READ: io=30,264KiB, aggrb=514KiB/s, minb=514KiB/s, ...
WRITE: io=30,128KiB, aggrb=511KiB/s, minb=511KiB/s, ...

項目aggrbがスループットを表しています。LogVol00のスループットがLogVol01の約2倍になっており、ウエイトの比率に従って帯域が分配されていることが分ります。

 

さいごに

dm-iobandは今回紹介した論理ボリュームの帯域制御だけでなく、仮想マシンのディスク帯域制御等、様々な応用が可能です。dm-iobandのWebサイトには設定例やベンチマーク結果等が掲載されていますので、是非アクセスしてみて下さい。

http://sourceforge.net/apps/trac/ioband/wiki/dm-ioband