技術的雑談-iSCSIお試し
環境
- CentOS5.2
- uname -a → Linux hogehoge 2.6.18-92.1.6.el5.centos.plusxen #1 SMP Thu Jun 26 13:43:14 EDT 2008 i686 i686 i386 GNU/Linux
目的・範囲
- iSCSIのtargetとinitiatorをセットアップし、iSCSIのデバイスを公開して使ってみる。
- マルチパスとかはやらない
- ファイルシステム自体はext3にしたので、同時マウントには対応しない。(多分ファイルシステムがブッ壊れる。)
前提
SCSIの世界では
- target
- :SCSIによってコントロールされるもの(例:HDDとか)
- initiator
- :SCSIで命令する側(例:OS)
- SCSIバス
- :SCSIは(概念的には)バス構成なので、1本のヒモにつかまって複数のデバイスがぶら下がって通信する。そのヒモのこと。(ただし、iSCSIはネットワーク経由なのでヒモじゃないっていえばヒモじゃない…。昔の名残です。)
- デバイス
- :SCSIバスに接続されているもの。targetもinitiatorもデバイスの一つ。target/initiatorは単に「呼びかけを始める人」「呼びかけられる人」という役割でしかない。ただし、一般的にDiskがPCに呼びかけることは無いので事実上は半固定的。かも。
と、いう。
また、targetには「target ID(tid)」という番号が振られる。これは単一SCSIバス内でユニーク(じゃないとマズい)。また、今回使用するiSCSI実装ではtarget:0は指定できないらしい。(なんでだろ?initiatorがtid:0って事なのかな?イミフ)
古き良きケーブル接続SCSI外付けHDDとか使ったことがある場合、本体後ろにあるダイアルとかで設定していた「番号」がtidです。
さらに、「単一デバイス内に複数の論理ボリュームがある」とされていて、それのことを「Logical unit」といい、それに番号を振って「Logical unit unmber = lun」という。
lunは0から始まる。
lunはそれの属するtarget内でユニーク。(当然)
なので、単一SCSIバス上ではtidとlunが指定されるとユニークな領域が特定される。
iSCSIはファイルシステムよりも下のレイヤーのものなので、ファイルシステムとかLVMとかは関係ない。
外付けSCSIディスクのケーブルがネットワークケーブルになっただけ。
(NFSとかFTPとかとは違うので注意。)
target側の設定
まずはtarget(ディスク側)の設定。
Install
yumを使いました。
# yum install scsi-target-utils.i386
で、インストールされたファイル一覧。
/etc/rc.d/init.d/tgtd /usr/sbin/tgtadm /usr/sbin/tgtd /usr/share/doc/scsi-target-utils-0.0 /usr/share/doc/scsi-target-utils-0.0/README /usr/share/doc/scsi-target-utils-0.0/README.iscsi /usr/share/man/man8/tgtadm.8.gz
サービス有効化
iSCSIのターゲットを稼動させるには「tgtd」というデーモンが動いている必要がある。
# chkconfig --add tgtd # chkconfig --list tgtd tgtd 0:off 1:off 2:off 3:off 4:off 5:off 6:off # chkconfig tgtd on # chkconfig --list tgtd tgtd 0:off 1:off 2:on 3:on 4:on 5:on 6:off # service tgtd start Starting SCSI target daemon: [ OK ]
サービスの稼動確認
ターゲットの操作はターゲットが稼動しているマシン上で「tgtadm」コマンドで行います。
# tgtadm --lld iscsi --mode target --op show # (→何も表示されないのが正解)
tgtdが起動していない場合はエラーが出る。
tgtadm: can't connect to the tgt daemon, Connection refused tgtadm: can't send the request to the tgt daemon, Transport endpoint is not connected
ターゲット・バックエンドの作成
iSCSI経由で公開するDiskを作ります。
NFSとかFTPとかとの違いを明確にする為にあえてファイルシステムを作成しない(mkfsしない)でターゲットを公開してみます。
ここでは公開するディスク(バックエンド)を仮想ディスク(loop device)として作成してみます。
→参照:Linuxでのディスクイメージファイルのマウント
# mkdir -p /var/loop # dd if=/dev/zero of=/var/loop/loop0.img bs=1048576 count=100 (とりあえず100Mbyteのディスクイメージ) 100+0 records in 100+0 records out 104857600 bytes (105 MB) copied, 0.367437 seconds, 285 MB/s # losetup /dev/loop0 /var/loop/loop0.img # ls -la /dev/loop0 brw-r----- 1 root disk 7, 0 7月 9 10:08 /dev/loop0
これで/dev/loop0として仮想デバイスができた。
(もちろん、mkfs -t ext3 /dev/loop0 とかやると/var/loop/loop0.imgファイルの中身でext3fsができあがる。)
※これをやった後にバックエンドは別に「/dev/*」じゃなくても平ファイルでもいけることが発覚。その場合はlosetup以降を省略して、tgtadmに/var/loop/loop0.imgを直接指定する。
SCSIターゲットの登録
今作った/dev/loop0を外部にiSCSIターゲットとして公開する。
まずは--helpを一覧。
# tgtadm --help Usage: tgtadm [OPTION] Linux SCSI Target Framework Administration Utility. --lld [driver] --mode target --op new --tid=[id] --targetname [name] add a new target with [id] and [name]. [id] must not be zero. --lld [driver] --mode target --op delete --tid=[id] delete the specific target with [id]. The target must have no activity. --lld [driver] --mode target --op show show all the targets. --lld [driver] --mode target --op show --tid=[id] show the specific target's parameters. --lld [driver] --mode target --op update --tid=[id] --name=[param] --value=[value] change the target parameters of the specific target with [id]. --lld [driver] --mode target --op bind --tid=[id] --initiator-address=[src] enable the target to accept the specific initiators. --lld [driver] --mode target --op unbind --tid=[id] --initiator-address=[src] disable the specific permitted initiators. --lld [driver] --mode logicalunit --op new --tid=[id] --lun=[lun] --backing-store=[path] add a new logical unit with [lun] to the specific target with [id]. The logical unit is offered to the initiators. [path] must be block device files (including LVM and RAID devices) or regular files. --lld [driver] --mode logicalunit --op delete --tid=[id] --lun=[lun] delete the specific logical unit with [lun] that the target with [id] has. --lld [driver] --mode account --op new --user=[name] --password=[pass] add a new account with [name] and [pass]. --lld [driver] --mode account --op delete --user=[name] delete the specific account having [name]. --lld [driver] --mode account --op bind --tid=[id] --user=[name] [--outgoing] add the specific account having [name] to the specific target with [id]. [user] could be [IncomingUser] or [OutgoingUser]. If you use --outgoing option, the account will be added as an outgoing account. --lld [driver] --mode account --op unbind --tid=[id] --user=[name] delete the specific account having [name] from specific target. --help display this help and exit Report bugs to <stgt-devel@lists.berlios.de>.
まずtarget(tid)を作成する。
# tgtadm --lld iscsi --op new --mode=target --tid=1 --targetname testtarget1 → tid(target ID)=1、target name = testtarget1としてtargetを作成する。
確認。
# tgtadm --lld iscsi --op show --mode=target Target 1: testtarget1 System information: Driver: iscsi Status: running I_T nexus information: LUN information: LUN: 0 Type: controller SCSI ID: deadbeaf1:0 SCSI SN: beaf10 Size: 0 Backing store: No backing store Account information: ACL information:
勝手にlun 0が作られるらしい。(Type : controler)なんだろ?
続けて先ほどの/dev/loop0をlun:1として登録する。
# tgtadm --lld iscsi --op new --mode=logicalunit --tid=1 --lun=1 -b /dev/loop0
確認。
# tgtadm --lld iscsi --op show --mode=target Target 1: testtarget1 System information: Driver: iscsi Status: running I_T nexus information: LUN information: LUN: 0 Type: controller SCSI ID: deadbeaf1:0 SCSI SN: beaf10 Size: 0 Backing store: No backing store LUN: 1 Type: disk SCSI ID: deadbeaf1:1 SCSI SN: beaf11 Size: 100M Backing store: /dev/loop0 Account information: ACL information:
このままだと接続が許されたAccountがない。
まずはアカウントの作成。
# tgtadm --lld iscsi --op new --mode account --user user1 --password 12345678
ちなみに、当然ながらこのuserIDとpasswordはUnix的なuser/passwordとは一切関係が無い。
後でイニシエータ側からtargetへ接続しに来るときに必要なのでuser/passwordは忘れないこと。
確認。
# tgtadm --lld iscsi --op show --mode=account Account list: user1
このアカウントをtid1に関連付ける。
# tgtadm --lld iscsi --op bind --mode account --tid=1 --user=user1
確認。
# tgtadm --lld iscsi --op show --mode=target Target 1: testtarget1 System information: Driver: iscsi Status: running I_T nexus information: LUN information: LUN: 0 Type: controller SCSI ID: deadbeaf1:0 SCSI SN: beaf10 Size: 0 Backing store: No backing store LUN: 1 Type: disk SCSI ID: deadbeaf1:1 SCSI SN: beaf11 Size: 100M Backing store: /dev/loop0 Account information: user1 ACL information:
tid1にuser1が関連付けられていることがわかる。
次にこのユーザがこのtargetに対してできることを割り当てる。
今回はベタで「全ての権限」を付与している。
# tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
確認。
# tgtadm --lld iscsi --op show --mode=target Target 1: testtarget1 System information: Driver: iscsi Status: running I_T nexus information: LUN information: LUN: 0 Type: controller SCSI ID: deadbeaf1:0 SCSI SN: beaf10 Size: 0 Backing store: No backing store LUN: 1 Type: disk SCSI ID: deadbeaf1:1 SCSI SN: beaf11 Size: 100M Backing store: /dev/loop0 Account information: user1 ACL information: ALL
ACL informationに「ALL」が記述されているのがわかる。
(逆に「どんな権限があるのか」はmanにもinfoにも--helpにも記載なし…。困った…。)
で、http://ken-etsu-tech.blogspot.com/2008/03/rhcs-iscsi-dm-mp-clvm-and-gfs3iscsi.htmlによると、これらのtgtadmで行った設定はリブートすると消えるそうだ(!!マジデ!!)
なので、これらの動作をshellか何かにして起動時に実行させる必要がある。
(今回はメンドイので省略…。ちなみに/etc/init.d/tgtdの中身を見ても「起動時に/etc/xxx/xxxにtgtadmに食わせるscript置いておくと自動実行されるよ〜」なんてものは無かった…orz)
これで一応ネットワーク上にiSCSIのtargetデバイスが公開されたはず。
イニシエータ側の設定
参照:http://ken-etsu-tech.blogspot.com/2008/03/rhcs-iscsi-dm-mp-clvm-and-gfs4iscsi.html
必要物のインストール
今回はtargetとイニシエータは別マシンで行っている。
以降の操作は基本的にイニシエータのマシン上で行うこと。
なので、イニシエータ用のパッケージが必要。
CentOS5.2の場合、インストール時に「iSCSI」を含めておくか、後から「yum install iscsi-initiator-util」でイニシエータに必要なソフトをインストールしておく。
(今回はインストール時に含めたから何もしてないけど、後からyumとかで入れた場合はmodprobeとかinsmodとかでカーネルモジュールをロードしないとダメかも。わからなければrebootでも多分OK。)
イニシエータの設定
イニシエータ名として重複しない名前を設定する必要がある。
# cp /etc/iscsi/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi.org # vi /etc/iscsi/initiatorname.iscsi InitiatorName=iqn.1994-05.com.redhat:4be3de93ab84
そのままでも何かのUIDみたいなものが設定されていてユニークっぽいが…。
今回はとりあえずhost名に変えておいてみる。(いいのか?)
アクセスするユーザ名/パスワードを設定する。
# cp /etc/iscsi/iscsid.conf /etc/iscsi/iscsid.conf.org # vi /etc/iscsi/iscsid.conf node.session.auth.authmethod = CHAP を追加 node.session.auth.username = user1 を追加 node.session.auth.password = 12345678 を追加
usernameとpasswordは先にtarget側で設定したものに合わせること(当然)。
接続設定はiscsiadmコマンドを使って行う。
# iscsiadm --help iscsiadm -m discovery [ -hV ] [ -d debug_level ] [-P printlevel] [ -t type -p ip:port -I ifaceN ... [ -l ] ] | [ -p ip:port ] [ -o operation ] [ -n name ] [ -v value ] iscsiadm -m node [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port -I ifaceN ] [ -l | -u | -R | -s] ] [ [ -o operation ] [ -n name ] [ -v value ] ] iscsiadm -m session [ -hV ] [ -d debug_level ] [ -P printlevel] [ -r sessionid | sysfsdir [ -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ] iscsiadm -m iface [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -I ifacename ] [ [ -o operation ] [ -n name ] [ -v value ] ] iscsiadm -m fw [ -l ] iscsiadm -k priority
(これもmanも--helpもinfoも情報少ない…。)
ディスカバリー
まずはtargetデバイスを発見してやる必要がある。
# iscsiadm --mode=discovery --type=sendtargets -p 192.168.0.1 192.168.0.1:3260,1 testtarget1 ↑ 先ほど設定したものが発見できること
この時点で/var/lib/iscsi以下にゴチャゴチャとファイルが作成される。
それらのファイルはiscsiadmコマンドで操作できるので基本的にはいじらない。
ファイルに書かれているって事は、イニシエータが発見したtarget情報は揮発しない。(rebootしても残る)
……target設定はrebootしたらきれいさっぱり忘れるくせに…(--#)
当然、target側の構成を変えたりtargetやlunを追加したときはディスカバーをやり直す必要がある。
(今回は試していないけど。)
ログイン
iSCSIはSCSIと違ってネットワーク上のものなので、旧SCSIで言えばケーブルのコネクタの所に鍵がかかっている。
ログイン操作をすることによってそのtargetに対してイニシエータはアクセスが可能になる。
(先ほどのターゲットは192.168.0.1だったとする。) # iscsiadm --mode=node --login Logging in to [iface: default, target: testtarget1, portal: 192.168.0.1,3260] Login to [iface: default, target: testtarget1, portal: 192.168.0.1,3260]: successful ↑ ログインできたっぽい
成功すると/dev/sd*というデバイスができる。(*はa,b,c,d…)
# ls -l /dev/sd* brw-r----- 1 root disk 8, 0 7? 9 13:33 /dev/sda
iSCSIのセッションが張られていることを確認する。
# iscsiadm -m session tcp: [1] 192.168.20.52:3260,1 testtarget1
/var/lib/iscsi/以下にtargetの情報が登録されている。
# tree -aps /var/lib/iscsi/* /var/lib/iscsi/ifaces /var/lib/iscsi/isns /var/lib/iscsi/nodes `-- [drw------- 4096] testtarget1 `-- [drw------- 4096] 192.168.0.1,3260,1 `-- [-rw------- 1611] default /var/lib/iscsi/send_targets `-- [drw------- 4096] 192.168.0.1,3260 |-- [-rw------- 420] st_config `-- [lrwxrwxrwx 53] testtarget1,192.168.0.1,3260,1,default -> /var/lib/iscsi/nodes/testtarget1/192.168.0.1,3260,1 /var/lib/iscsi/slp /var/lib/iscsi/static 4 directories, 2 files # cat /var/lib/iscsi/nodes/testtarget1/192.168.0.1,3260,1/default node.name = testtarget1 node.tpgt = 1 node.startup = automatic iface.hwaddress = default iface.iscsi_ifacename = default iface.net_ifacename = default iface.transport_name = tcp node.discovery_address = 192.168.0.1 node.discovery_port = 3260 node.discovery_type = send_targets node.session.initial_cmdsn = 0 node.session.initial_login_retry_max = 4 node.session.cmds_max = 128 node.session.queue_depth = 32 node.session.auth.authmethod = CHAP node.session.auth.username = user1 node.session.auth.password = 12345678 node.session.timeo.replacement_timeout = 120 node.session.err_timeo.abort_timeout = 15 node.session.err_timeo.lu_reset_timeout = 30 node.session.err_timeo.host_reset_timeout = 60 node.session.iscsi.FastAbort = Yes node.session.iscsi.InitialR2T = No node.session.iscsi.ImmediateData = Yes node.session.iscsi.FirstBurstLength = 262144 node.session.iscsi.MaxBurstLength = 16776192 node.session.iscsi.DefaultTime2Retain = 0 node.session.iscsi.DefaultTime2Wait = 2 node.session.iscsi.MaxConnections = 1 node.session.iscsi.MaxOutstandingR2T = 1 node.session.iscsi.ERL = 0 node.conn[0].address = 192.168.0.1 node.conn[0].port = 3260 node.conn[0].startup = manual node.conn[0].tcp.window_size = 524288 node.conn[0].tcp.type_of_service = 0 node.conn[0].timeo.logout_timeout = 15 node.conn[0].timeo.login_timeout = 15 node.conn[0].timeo.auth_timeout = 45 node.conn[0].timeo.noop_out_interval = 5 node.conn[0].timeo.noop_out_timeout = 5 node.conn[0].iscsi.MaxRecvDataSegmentLength = 131072 node.conn[0].iscsi.HeaderDigest = None,CRC32C node.conn[0].iscsi.IFMarker = No node.conn[0].iscsi.OFMarker = No
iSCSIデバイスの使用
後は普通のdiskと同じように使える。
使用例としてイニシエータ側からtarget(内のlun)にmkfsしてファイルシステムを作ってマウントしてみる。
# mkfs -V -t ext3 /dev/sda mkfs (util-linux 2.13-pre7) mkfs.ext3 /dev/sda mke2fs 1.39 (29-May-2006) /dev/sda is entire device, not just one partition! Proceed anyway? (y,n) y Filesystem label= OS type: Linux Block size=1024 (log=0) Fragment size=1024 (log=0) 25688 inodes, 102400 blocks 5120 blocks (5.00%) reserved for the super user First data block=1 Maximum filesystem blocks=67371008 13 block groups 8192 blocks per group, 8192 fragments per group 1976 inodes per group Superblock backups stored on blocks: 8193, 24577, 40961, 57345, 73729 Writing inode tables: done Creating journal (4096 blocks): done Writing superblocks and filesystem accounting information: done This filesystem will be automatically checked every 23 mounts or 180 days, whichever comes first. Use tune2fs -c or -i to override. # mount /dev/sda /mnt # mount /mnt mount: /dev/sda は マウント済か /mnt が使用中です mount: mtab によると、/dev/sda は /mnt にマウント済です
OKっぽい。
この状態で/mnt以下にファイルやディレクトリを作成すると、それはネットワーク経由でtargetマシンに保存される。
ちなみにtarget側から接続状況を確認するとこうなっている。
(これは先のtargetマシン上で実行する。) # tgtadm --lld iscsi --mode target --op show Target 1: testtarget1 System information: Driver: iscsi Status: running I_T nexus information: I_T nexus: 1 Initiator: hogehoge.initiator01 Connection: 0 IP Address: 192.168.0.2 ← このへん LUN information: LUN: 0 Type: controller SCSI ID: deadbeaf1:0 SCSI SN: beaf10 Size: 0 Backing store: No backing store LUN: 1 Type: disk SCSI ID: deadbeaf1:1 SCSI SN: beaf11 Size: 100M Backing store: /dev/loop0 Account information: user1 ACL information: ALL
後始末
誰かがiSCSI targetを使用中だとtargetのtgtdは普通にshutdownできないので、イニシエータは後始末をしてターゲットを手放す必要がある。
(いきなりイニシエータ側をrebootしたときの弊害については下記参照。)
まずイニシエータ側でumountする。
# umount /mnt # iscsiadm -m session --op show tcp: [1] 192.168.0.1:3260,1 testtarget1 ← まだiSCSIとしてのSessionは張られたまま # iscsiadm -m session -r 1 --logout ← 先の「tcp: [1]」の「1」がSession ID(らしい) Logging out of session [sid: 1, target: testtarget1, portal: 192.168.0.1,3260] Logout of [sid: 1, target: testtarget1, portal: 192.168.0.1,3260]: successful
確認。
# iscsiadm -m session --op show iscsiadm: No active sessions. ← Sessionがなくなった
イニシエータ側の再起動に関する注意
どうも、そのままではイニシエータ側でOSを再起動した場合、OS再起動時にiSCSI SessionをLogoutしないようになっている為、再起動後に同じtargetに対してiSCSI Sessionが張れなくなるという問題があるらしい。(/etc/init.d/iscsiにわざわざそのようなロジックが作りこんである。バグというわけではなさそうだ。)
OSのブートディスクでiSCSIによるRAIDなどが使われる可能性もあるので、init.dが完了した後にカーネルから強制KILLされるのを待つようにしてあるらしい。まあ、正しいか…。
そのかわり、iSCSIのターゲットを使った場合、rebootかshutdownする前にiscsiadm logoutする必要がある。メンドイ…気もする。
回避する為にはイニシエータ側の/etc/init.d/iscsiとiscsidを書き換える必要があるらしい。
中に「if [ "$RUNLEVEL" = "6"云々」と書いてあるif節があるので、そこを全てコメントアウトする。
ただし、これをしてしまうとブートデバイスにiSCSIを使用していると問題が発生すると思われる。トレードオフ。(未検証。)
# でも、iscsiadmで「autostart」とか指定すると/etc/init.d/iscsiの中で勝手にloginしてくれちゃうんだよなぁ…それって設計思想矛盾してない?
考察
負荷試験とか障害試験などはナシで「とりあえずお試し」なのでなんとも言えませんが、仮想環境(XenとかVMWare)でクラスタ環境を作る場合にはいいかもしれないですね。
(参考にさせていただいたサイトでも大体そのような使い方しているし。)
Xenの場合はDom0の平ファイルとかデバイスファイルとかでは共有ディスク作れないし、VMWareの場合はできるっぽいんだけどマルチパスとかは当然できないわけで、クラスタ乗っけようとするとわけわかめになって挫折した事ありなので、ブロックデバイスレベルで論理結線可能なiSCSIは試験環境構築では非常に魅力的ではないでしょうか。
参照させていただいたサイト
- http://docs.sun.com/app/docs/doc/819-0386/fmvcd?a=view
- LinuxじゃないけどiSCSI全体がちゃんと説明されている
- http://www.atmarkit.co.jp/flinux/rensai/xen202/xen202a.html
- open-iscsiってCentOSのiSCSIと同じものなのかな?CLVMの使い方も書いてある。
- http://ken-etsu-tech.blogspot.com/2008/02/red-hat-cluster-centos-51rhcs1.html
- RHCS環境を作っています。スゴイです。内容的にはほとんど丸パクリさせていただきました。m(_ _)m(ちゃんと手前側でも実機検証してますよ^^;)
履歴
2008/07/09 -- 初版
2010/04/06 -- 追試。現段階でも記述内容で問題なくお試しできる事を確認済み。CentOS5.4。
技術的雑談へ戻る