オフィシャルサイト: tgt project (Linux SCSI target framework), Open-iSCSI

iSCSI

ひとことで言えば、SCSI-over-TCP/IP。SCSI プロトコルを TCP/IP 上で使用する規格のことだ。iSCSI では、ストレージを提供する方つまりストレージサーバ側を「ターゲット」(Target)、利用する側を「イニシエータ」(Initiator) と呼ぶ。ソフトウェアの実装は幾つかの団体が行っているようだが、RedHat系では、ターゲットソフトウェアは tgt project (※)、イニシエータソフトウェアは Open-iSCSI のものを採用している。iSCSI 管理コマンドは使い方が幾分複雑だ。その点、当ページはコマンド実例集としてもお役に立てるだろう。当内容の検証は主に RedHat Enterprise Linux 5.5 と CentOS 5.5 上で行った。

tgt と並んで有名な iSCSI ターゲット実装に IET (iSCSI Enterprise Target) があり、ubuntu など幾つかのディストリビューションで採用されているようだ。また、tgt と統合して Linux カーネル 2.6.37 からアップストリームに入るとされている LIO (Linux iSCSI/linux-iscsi.org) の動きも注視しておくべきかもしれない。

Table of Contents

必要なパッケージ

scsi-target-utils
ターゲット側ソフトウェア。デーモン tgtd と、tgtadm, tgt-admin など管理ユーティリティを提供する。RHEL では、「クラスターストレージ」レポジトリに含まれている。
iscsi-initiator-utils
イニシエータ側ソフトウェア。イニシエータデーモン iscsid と、iscsiadm など管理ユーティリティを提供する。

ターゲットの設定

管理コマンドは tgtadmscsi-target-utils では、tgtadm をラップして使いやすくする tgt-admin という Perl スクリプトも付属しており、場面に応じて使い分けるとよい。

後で述べるイニシエータ側の iscsiadm とは異なり、tgtadm による設定は動的なものであって、マシンを再起動したりすると設定はさっぱり忘れ去られる。設定を恒久的に保持したい場合 (たいていはそうだろう) には、tgt-admin の設定ファイルである /etc/tgt/targets.conf に定義を書いておく。tgtd デーモンの rcスクリプトは、中で tgt-admin を利用しており、デーモン起動時に targets.conf を読み込んでターゲットをアクティブにしてくれる。

作業に入る前に、何はなくとも、まず tgtd デーモンを起動させなければならない。

root# service tgtd start

ちゃんと起動するようであれば、`chkconfig tgtd on' で自動起動サービスに登録する。

以下の説明では、ターゲットマシン (ストレージサーバ) のホストFQDN を alpha.hoge.com と仮定する。

設定を恒久的に保持するやり方 (targets.conf)

先に述べたように、設定が消えないようにするには tgt-admin の設定ファイルである /etc/tgt/targets.conf に定義を書く。tgt-admin が Perl スクリプトなだけに書き方はやや特殊。どうやら、tgt-admin スクリプトは汎用 Perl モジュール Config::General を使ってこのファイルをパースしているようだ。`#' で始まる行はコメントとして扱われる。

targets.conf の中身は、XML式に <target ...> タグで囲まれた "ローカル" な設定部分 (その iSCSI ターゲット固有の設定) と、何のタグにも囲まれていないグローバルな設定とに大別される。ファイル自体は、設定によってはパスワードを含むことになるので、ファイルパーミションは root:root 600 にするのが望ましい。

既に tgtadm を使ってターゲットを動的に構成済みならば、tgt-admin を使って現在の設定を targets.conf に則ったフォーマットで吐き出すこともできる;

root# tgt-admin --dump >OUTPUT_FILE

ただし、現在のところ、`tgt-admin --dump' では write-cache 設定など詳細パラメータはアウトプットされず、また、後で述べる理由から、吐き出されるコンフィグは最適でない場合がある。そのため、targets.conf そのものへ上書きリダイレクトするのは避け、あくまでも叩き台作りだと思って使った方がよい。

グローバルセクション

default-driver iscsi
tgt-admin の中で tgtadm--lld オプションの引数としてデフォルトで渡されるドライバ名。
include /etc/tgt/conf.d/*.conf
include 句を使えば、他の設定ファイル片を読み込むことができる。複数の設定ファイルを読み込みたい場合は、例のようにワイルドカードを使うか、include 句を複数回書いておけばよい (1行でひとつずつ)。デフォルトではコメントアウトされている。targtes.conf と同じく、インクルード先のファイルも root:root 600 のパーミションにするのが望ましい。

ローカルセクション (ターゲットの定義)

例として、ターゲット iqn.2010-09.com.hoge.alpha:testdevsLVMLV "LV_test_1" (LUN 1) と "LV_test_2" (LUN 2) をぶら下げてイニシエータアドレス 192.168.1.0/24 と 192.168.100.1 からのアクセスを許可する場合を示す。用語の説明は次項「動的更新コマンド」の項を参照していただきたい。

<target iqn.2010-09.com.hoge.alpha:testdevs>
    <backing-store /dev/VolGroup00/LV_test_1>
        lun 1
    </backing-store>
    <backing-store /dev/VolGroup00/LV_test_2>
        lun 2
    </backing-store>
    initiator-address 192.168.1.0/24
    initiator-address 192.168.100.1
    #write-cache off
</target>

scsi-target-utils 付属の targets.conf のサンプルに書かれているコメントによると、backing-store どうしなら書いてある順番どおりに LUN 序数が振られるとされているが、RHEL/CentOS 5.5 (scsi-target-utils-0.0-6.20091205snap) でやってみたところ結果は必ずしもそうならず、LV_test_1LV_test_2LUN 順序が逆になってしまうような現象が見られた。それを避けるため、上記のように <backing-store ...> をタグとして書いて lun を明記するほうがよい。なお、タグの中でも、コメントアウト記号は XML式でなく `#' を使えばよい。

TIDの固定について

<target ...> 定義が複数ある時、各ターゲットのターゲットID (tid) の採番はどうなのか。実験によると、tgt-admin ではどうやらターゲット名(`:' の後)で文字列ソートしてから tid をその順で振っているようだ。例えば、

となる。tgt-admin に自動で採番させるなら、 ターゲット名を上記のように数字で始まるものにするといいだろう。

しかし、tid の調整はこれだけでは済まない場合がある。例えば、VMware ESX(i) に複数の iSCSI サーバを吊す場合だ。ESX(i) の持つイニシエータ(=ソフトウェアiSCSIアダプタ) は、各iSCSI Target を SCSI-ID および SCSI-SN (SCSI Serial) で区別している。tgtd で実装した複数の Linux iSCSI サーバの Target を ESX(i) にぶら下げようとすると、各 Linux iSCSI サーバの最初の Target のコントローラ(LUN 0) の SCSI-ID はどれも 00010000、LUN 1は 00010001 なので、ESX(i) ホストはそれらが区別ができず、同一の Target が代替IPで公開されたものだと思い込んでしまうのだ。こうした問題を解決するため、targets.confcontroller_tid 句が追加装備された(メーリングリストアーカイブのこのスレッドに経緯がある)(※)。パッチされた tgt-admin が実装されたのは tgt-1.0 の途中で、RHEL/CentOS だと 6 からだが、RHEL 5.5 の tgt-admin (tgt-0.0) にその 4行のコードを手で書き加えたところ、やはりきちんと機能してくれた。

※ Target の中のディスクLUN毎に SCSI-ID と SCSI-Serial を指定する scsi_id 及び scsi_sn 句もあるが、それらでは LUN 0 であるコントローラの ID とシリアルは指定も変更もできない。

controller_tid 句の使用例を以下に示す。Linux iSCSI サーバA と B があるとすると、

iSCSIサーバA

前述のターゲット名によるソートの法則により、testdevs => vmdevs の順で tid が決定されるので、vmdevs ターゲットの controller_tid 指定は必須ではなく、指定しなくても前のTargetに 1 インクリメントされて同様の結果が得られる。また、ひねくれて testdevs を 10011、vmdevs を 10010 と入れ替えても、ちゃんと SCSI-IDは逆になり、プログラムから文句は出なかった。つまり、全ての Target に各々 controller_tid を指定しておいた方が間違いないということだ。

<target iqn.2010-09.com.hoge.alpha:testdevs>
    controller_tid 10010
    <backing-store /dev/VolGroup00/LV_test_1>
        lun 1
    </backing-store>
    <backing-store /dev/VolGroup00/LV_test_2>
        lun 2
    </backing-store>
    initiator-address 192.168.1.0/24
    initiator-address 192.168.100.1
    #write-cache off
</target>
<target iqn.2010-09.com.hoge.alpha:vmdevs>
    controller_tid 10011
    <backing-store /dev/VolGroup00/LV_devel_1>
...

iSCSIサーバB

<target iqn.2010-09.com.hoge.alpha:develdevs>
    controller_tid 10020
    <backing-store /dev/VolGroup00/LV_devel_1>
        lun 1
    </backing-store>
    initiator-address 192.168.1.0/24
    initiator-address 192.168.100.1
    #write-cache off
</target>
<target iqn.2010-09.com.hoge.alpha:sharedevs>
    controller_tid 10021
    <backing-store /dev/VolGroup00/LV_share_1>
...

この状態で tgt-admin --show をかけると、各SCSI-IDとシリアルは以下のようになっているのが分かる(サーバAの抜粋)。

Target 10010: iqn.2010-09.com.hoge.alpha:testdevs
    LUN information:
        LUN: 0
            Type: controller
            SCSI ID: IET     271A0000
            SCSI SN: beaf100100
        LUN: 1
            Type: disk
            SCSI ID: IET     271A0001
            SCSI SN: beaf100101
        LUN: 2
            Type: disk
            SCSI ID: IET     271A0002
            SCSI SN: beaf100102
Target 10011: iqn.2010-09.com.hoge.alpha:vmdevs
    LUN information:
        LUN: 0
            Type: controller
            SCSI ID: IET     271B0000
            SCSI SN: beaf100110
        LUN: 1
            Type: disk
            SCSI ID: IET     271B0001
            SCSI SN: beaf100111

どういう決まりかは深堀りしていないが、SCSI-ID は、tid の 16進表現 + ゼロパディングした 4桁のLUN序数、SCSI-SN は tid の 10進そのまま + LUN序数 1桁 となることが分かる。その他、

という観察から、controller_tid に指定する数値は 4桁~6桁が適当と思われる。数値であるから最上位桁が 0 では桁数が不定になる、整理しやすいよう iSCSIサーバのIPを取り入れたい、各iSCSIサーバ上の Target 序数を示す桁も付けたい、と考えると、無難且つ実用的なのは 5桁か 6桁ではないだろうか。

設定ファイルの反映

一番簡単なのは、

root# service tgtd reload

あるいは、上記で行われることを直接コマンドしてもいい;

root# tgt-admin --update ALL

設定ファイルのデバグ

tgt-admin-v オプションを付けてやると、tgt-admin の発行している tgtadm のコマンドを見ることができる。

root# tgt-admin --update ALL -c /dev/null  <- 一旦設定をクリア
root# tgt-admin -e -v  <- 設定ファイルをロード (`-c /etc/tgt/targets.conf' はデフォルトなので省略)

コマンドによる構築と操作

tgtadmtgt-admin コマンドでターゲットを動的に構築する手順や、コマンドによる操作について述べる。ただし、コマンドで構築したターゲットは、マシンをリブートしたり tgtd を再起動したりすると設定がチャラになってしまうことを忘れずに。恒久的に設定しておきたい場合は前述の「設定を恒久的に保持するやり方」を参照のこと。

ターゲットの定義作成

SCSI で言えばホストアダプタをひとつ新たに作ることに相当する。

root# tgtadm --lld iscsi --op new --mode target --tid 1 \
  --targetname iqn.2010-09.com.hoge.alpha:testdevs

--op は短いオプション -o--mode-m--tid-t--targetname-T で代用できる。

ターゲットID (tid) は 1オリジン。ターゲット名 (targetname) は RFC3720 によって下記のフォーマットが定められている。`iqn' は `iSCSI Qualified Name' を表す。ターゲット名は世界で一意なものでなければならないことになっている。

iqn.{`yyyy-mm' date code}.{ドメイン名もしくは組織名}:{製品名/シリアルNo./ホストの識別標など任意}

通例では、{ドメインもしくは...} はターゲットホストの FQDN を逆さにしたもの。末尾の {製品名/...など任意} はこのターゲットの用途を表すような文字列を割り振ることが多く、ドットなどを含んでもよい。

ターゲットを削除するコマンドは下記の要領。ターゲットがイニシエータによって利用中 (login済み) の時には削除されない。強制的に削除したい場合は --force を加える;

root# tgt-admin --delete iqn.2010-09.com.hoge.alpha:testdevs

あるいは、tid 指定で削除することもできる;

root# tgt-admin --delete tid=1

tgtadm コマンドを直接使うなら下記の要領。既に LUN がぶら下げてあっても削除できる;

root# tgtadm --lld iscsi --op delete --mode target --tid 1

ターゲットへのLUNの追加

SCSI ホストアダプタの下に論理ユニット (ディスク) をぶら下げる。下のコマンドは LVMLV を使用する例。

root# tgtadm --lld iscsi --op new --mode logicalunit --tid 1 \
  --lun 1 --backing-store /dev/VolGroup00/LV_test_1

--backing-store は短いオプションなら -b。LUN=0 はターゲットそのもの (いわばホストアダプタ) に割り当てられるので、lun は 1 から始める。

LUN を削除するには、

root# tgtadm --lld iscsi --op delete --mode logicalunit --tid 1 \
  --lun 1

ターゲットへのアクセスリスト登録

ターゲットに対して、イニシエータIPアドレスからのアクセスを許可するための ACL を設定する。何も設定しないとどこからもアクセスできない。アドレスは `192.168.1.1' のような単一アドレスも `192.168.1.0/24' といったネットワークアドレスも指定できる。全てのクライアント IP からアクセスを許す場合は特別なアドレスキーワード `ALL' を与える。アドレス句は一度に 1回且つ 1個しか使用できないので、複数の IP やネットワークからのアクセスを許可したい場合は、同様のコマンドを必要な分だけ繰り返す必要がある。

root# tgtadm --lld iscsi --op bind --mode target --tid 1 \
  --initiator-address 192.168.1.0/24

許可を取り消すには `--op bind' を `--op unbind' に変えてコマンドする。

その他のオペレーション

設定の確認

root# tgt-admin --show

--show-s に短縮してもいい。tgtadm を直接使っても同じことができるがコマンドが長くなる;

root# tgtadm --lld iscsi --mode target --op show

全てのターゲットを一気に無効にするには

tgt-admin に設定ファイルとしてヌルを渡してやれば全てのターゲットが消滅する (targets.conf から消えるわけではない);

root# tgt-admin --update ALL -c /dev/null

イニシエーターからの新たなログインを阻止しておきたい場合、ターゲットをオフラインにするという手がある。既に login しているイニシエータは影響を受けない。tid 1 のターゲットをオフラインにする場合;

root# tgt-admin --offline tid=1

または、

root# tgtadm --mode target --op update --tid 1 \
  --name=State --value=offline

オフラインにしたターゲットを再度オンラインにするには;

root# tgt-admin --ready tid=1

または、

root# tgtadm --mode target --op update --tid 1 \
  --name=State --value=ready

全ターゲットをオフラインにすることもできる;

root# tgtadm --mode sys --op update --name=State --value=offline

再びオンラインにするには;

root# tgtadm --mode sys --op update --name=State --value=ready

イニシエータの設定

node 登録を行うと /var/lib/iscsi/nodes/ 及び send_targets/ 下 (以下 "データベース" と呼ぶ) に設定が記録されるので、次回からは login するだけでそれらターゲットを利用できる。

実は、イニシエータ関連では /etc/init.d/ に 2つの起動スクリプトが存在する。iscsidiscsi だ。iscsid は、イニシエータ機能を動作させるために幾つかのカーネルモジュールをロード (stop 時はアンロード) して iSCSI 制御パスを有効にする。iscsi のほうは、データベースに記録されたターゲットに自動 login して使用可能にする、いわばオートマウンターのような役目 (実際に mount するわけではないが) だ。後者の iscsistart すると、iscsid が起動していなければ iscsidstart され、stop すると iscsidstop される。つまり、iscsi を自動起動サービスに登録するなら iscsid は必ずしも自動起動にする必要はない。逆に、iscsi 起動スクリプトによる自動 login を使用せずに、例えば libvirt のストレージプール経由でターゲットを使用したり必要時だけ iscsiadm コマンドで login する運用の時は、iscsid だけを自動起動にすべき。

以下の作業を始めるためには、とりあえず、iscsid を開始しておかなければならない。

root# service iscsid start

ブート時に自動的に起動させたければ、`chkconfig iscsid on' でスタートアップに登録。

以下の説明では、ターゲットマシン (ストレージサーバ) の FQDN が alpha.hoge.com で、DNS あるいは hosts ファイルで名前解決できるものと仮定する。

ターゲットノードの発見/登録

ターゲットマシンの提供している全ターゲットを自動発見して登録する場合

root# iscsiadm --mode discovery --type sendtargets \
  --portal alpha.hoge.com

--mode-m--type-tsendtargetsst--portal-p と短縮してもOK。

データベースから削除する場合、`--mode discovery' で登録したターゲットに限っては下記コマンドで登録削除できる。次項のように `--mode node' で決め打ち登録したものは discovery では削除できない;

root# iscsiadm --mode discovery --op delete \
  --portal alpha.hoge.com

特定のターゲットだけを決め打ちで登録する場合

root# iscsiadm --mode node --op new --portal alpha.hoge.com \
  --targetname iqn.2010-09.com.hoge.alpha:testdevs

--op-o--targetname-T と短縮してもOK。

逆に、ターゲットをデータベースから削除するには;

root# iscsiadm --mode node --op delete \
  --targetname iqn.2010-09.com.hoge.alpha:testdevs

確認

root# iscsiadm --mode node --op show

パラメータの変更

iscsi サービスを start、つまりこの後に述べる `service iscsi start' をした時、node.startup パラメータが automatic になっているターゲットだけが自動的にログイン処理される。node 登録をした時に automatic になるか manual になるかの規定値は、/etc/iscsi/iscsid.confnode.startup={automatic|manual} で決定される(既定はautomatic)。自動ログインさせたくないターゲットは、下記のようにして node.startup 値を manual に変えておこう。

root# iscsiadm --mode node --op update \
  --targetname iqn.2010-09.com.hoge.alpha:testdevs \
  --name=node.startup --value=manual

逆に、同一のターゲットの同一のLUNに複数のイニシエータマシンからログインさせようとした折(※)、2番目以降に登録したマシンでは、iscsid.confnode.startup=automatic になっているにもかかわらず、ブート時に自動 login されなかった。`--op show' で調べてみるとターゲットの node.startup 値が onboot になっていた。そういった場合は上記のコマンドで --valueautomatic にしてやる必要がある。

※ 何の排他制御もなしに複数のマシンから同一の iSCSI LUN を読み書きするのは危険だ。この時は Oracle RAC で共有制御される RAW (ASM) ボリュームだったことをお断りしておく。

ターゲットの使用開始 (ログイン)

これを行うことによって、対応するブロックデバイスがイニシエータマシンの /dev/ 配下にできて使用準備が整う。ちなにみ、libvirt のストレージプールに登録する場合は libvirt がこうしたことを代わりに行うので作業不要で、`service iscsi start' は機能がバッティングするのでしてはいけない。`service iscsi stop' をすると、iscsid デーモンも停止されるので注意が必要だ。

一気:
root# iscsiadm --mode node --loginall all

--loginall は短いオプション -L で代用可能。

ターゲットの node.starup パラメータが automaticmanual かに関わらず (ただし onboot のものを除く)、データベースに登録されている全てのターゲットにログインが行われる。automatic 設定のターゲットのみにログインするには、`--loginall all' を `--loginall automatic' に変えてコマンドする。

全てのターゲットからログアウトするには;

root# iscsiadm --mode node --logoutall all

--logoutall の短いオプションは -U

もっと一気:
root# service iscsi start

これは、node.startup パラメータが automatic なターゲット全てにログインが実行される。もちろん、chkconfig で自動起動にしておくこともできる (ただし注意事項あり - イニシエータ解説冒頭の説明を参照のこと)。

特定のターゲットにのみログインする場合:
root# iscsiadm --mode node \
  --targetname iqn.2010-09.com.hoge.alpha:testdevs --login

--targetname-T--login-l で代用可能。

ログアウトは;

root# iscsiadm --mode node \
  --targetname iqn.2010-09.com.hoge.alpha:testdevs --logout

--logout-u で代用可能。

ターゲット側でLUNを追加/削除した後の反映:
root# iscsiadm --mode node \
  -targetname iqn.2010-09.com.hoge.alpha:testdevs --rescan

--rescan-R で代用可能。

確認:
root# iscsiadm --mode session --op show

もっと詳細な iSCSI パラメータまで見たければ、

root# iscsiadm --mode session --op show --print 3

--print は出力の詳細レベル (0~3)。短いオプションでは -P

ターゲットが正常に認識されたかを確認するには、/dev/disk/by-path/ を見ると分かりやすい;

root# ls -l /dev/disk/by-path

問題点と調整

シャットダウン/リブート時のSCSI cache エラー

イニシエータとして iSCSI デバイスを使用している RHEL/CentOS 5.5 で、マシンのシャットダウンまたはリブート時に、"Synchronizing SCSI cache for disk ***" や "sd *:*:*:*: timing out command, waited ***s" が表示されてシャットダウンやリブートがいつまで経っても完了しない現象に遭遇している。これは、シャットダウン行程の最後にカーネルが SCSI キャッシュをフラッシュしようとするが、その段階では既にネットワークが停止されているため、iSCSI ディスクにアクセスできないからだ。そのうち対策されるだろうが、急場しのぎとして、以下のいずれかまたは複数を施しておけば、この現象を避けることができる。Red Hat Bugzilla – Bug 583218 から情報を得て検証した。

対策1 - ターゲットのライトキャッシュを無効にする

ターゲット側 (ディスクサーバ側) で当該ターゲットの write-cache パラメータを off にしておくと、この問題は発生しないようだ。これは targets.conf のターゲットブロック内に `write-cache off' を書いておくことで設定できる。記述例は、前述「設定を恒久的に保持するやり方」の「ローカルセクション(ターゲットの定義)」でコメントアウトしてある。例のように <target ...> タグ直下に書いてデバグしてみると、そのターゲットの各LUN に対して SCSI mode_page が設定されるのが分かる。ただし、ライトキャッシュを無効にした場合の I/Oパフォーマンスやデータの安全性については検討/検証が必要だ。

対策2 - ネットワークの停止をやめる

イニシエータ側での対策。ランレベル 0, 6, 1 のいずれかに落ちる場合に、ターゲットにログインしていたらネットワークを停止させないようにする。この動作を得るために、/etc/init.d/network スクリプトに修正を加える。

root# patch -d /etc/init.d <network_iscsi.patch

対策3 - iscsi 起動スクリプトの修正

イニシエータ側の iscsi 起動スクリプトは、stop 引数で呼ばれたとき全てのターゲットからログアウトする働きをするが、中身を見てみると、ランレベル 0, 6, 1 のいずれかに落ちる際に限っては、ログアウト処理が成功したフリをして何もせずに終わるようになっている。その部分をコメントアウトする。

root# patch -d /etc/init.d <iscsi_init.patch

iscsi 起動スクリプトはもともと、ルートファイルシステム (/) がネットワークデバイス (_netdev) である場合には処理をやめるようにできているので大丈夫だとは思うが、イニシエータマシンを iSCSI ディスクブートにしている場合は注意が必要かもしれない。

ターゲットとイニシエータを同一マシン上で動作させる方法

本来 iSCSI ターゲットデーモンとイニシエータデーモンは同一マシン上で稼働させる造りにはなっていない。しかし、当方のように低予算の研究目的で組む場合など、1台のマシン上に同居させたい場合がある。そのためには、tgtdiscsi 起動スクリプトの開始/停止順を修正してやる必要がある。マシンのブート時には tgtd -> iscsi の順、シャットダウン時には iscsi -> tgtd の順でなければならないが、ノーマルでは順序が逆なのだ。簡単な編集だがパッチにした。

root# chkconfig --del iscsi
root# patch -d /etc/init.d <iscsi_init_order.patch
root# chkconfig --add iscsi
root# chkconfig iscsi on
root# chkconfig --del tgtd
root# patch -d /etc/init.d <tgtd_init_order.patch
root# chkconfig --add tgtd
root# chkconfig tgtd on