PXEでiSCSI boot

iSCSI SAN bootはHBAが高い、使用できるハードウェアが限られる等あるので
PXE経由でiSCSI BOOT出来るように頑張ってみた。
InitiatorとしてはLinux-iSCSIはもうメンテされてないし、色々面倒なので
Open-iSCSIを使用。なお、CentOS5にはiscsi-initiator-utilsとして入っている。

iSCSI Target設定

iSCSI TargetはiSCSI Enterprise Target等を使って適当にでっち上げる。
モジュールをビルド、インストールはCentOS5等だと特に問題なく可能。
最終的には専用のストレージ使うにしろテストにはこれで十分。


設定は以下の様に /etc/ietd.conf に追記。
最低限の設定はIQNとブロックデバイスの割り当てを記述するだけ。
ブロックデバイスの代わりにファイルも使えたりもする。

Target iqn.sample.target0
	Lun 0 Path=/dev/sdaXX
Target iqn.sample.target1
	Lun 0 Path=/dev/sdaYY

で以下の通り起動、至ってシンプル。

 # /etc/init.d/iscsi-target start

これでTargetは準備完了。


確認は以下の様な感じで可能。
loginした時点で/dev/sdXのブロックデバイスとして見えるはず。

 # iscsid
 # iscsiadm -m discovery -t sendtargets -p <target ip>
 # iscsiadm -m node -T <target iqn> -p <target ip> --login

この領域にOSを入れる必要があるが、CentOS5はiSCSI領域に直接
標準のインストーラからインストール可能。面倒な場合は
他のホストでmountしてrestoreしたりそのままddするのも良い。

iSCSI Initiator設定

こっちがメイン。
方針としてパッケージの再コンパイル等は行わずにディストリビューション(この場合CentOS)で
配布されているバイナリだけを使用する。(sharedバイナリのまま使用)
システムは作ったら終わりというわけでは無いので、メンテナンスに掛かるコストは
少なければ少ないほど良いし。


ということで起動用initrdの準備。昔はext2だったが最近はcpioで固めた物がgzipされている。
全部最初から作るよりは、既に入っているinitrdを展開して追加、変更した後
再packするのが楽。要はmkinitrdのやっていることを代わりにやるだけ。


また、手の込んだことをやる場合はbusybox等を使っている人も居るようだが、
最近はそこまで領域の節約をする必要が無いのと、上記理由でupdateに追従
し易くするため、必要なライブラリ、バイナリを直接コピーして使うことにした。
必要なライブラリは使いたいバイナリをlddして特定。(LKMはlsmod等)
巨大なlibc.soが間違いなくinitrdに入ることになるが気にしない。


最低限必要なバイナリは以下(ライブラリは適宜必要な物を追加)。
debugしたい場合はstraceやらbashなども入れておくと便利。

ip
 インターフェイス操作用
dhclient
 IPアドレス取得用
 実際にはdhclient-scriptで設定
iscsid
 NETLINKソケット経由でiSCSI制御用LKMと通信する。(嵌り所多いので以下にmemo)
  以下のディレクトリが必要
   /var/lock/iscsi /var/run /var/lib/iscsi/{ifaces,isns,nodes,send_targets,slp,static}
  nss関連のライブラリも必要かも(間接的に呼ばれてる)
  /etc/passwdが必要(rootエントリだけで良い)
  /etc/iscsi/{iscsid.conf,initiatorname.iscsi} も必要
iscsiadm
 iscsidとの通信用
LKM(Ether用)
 e1000.ko,e100.ko 等々使ってるEther次第(ここではe1000を使用)
LKM(iSCSI用)
 依存関係によってinsmodに転けるのでロードする順番に注意
  sd_mod.ko
  scsi_mod.ko
  scsi_transport_iscsi.ko
  libiscsi.ko
  iscsi_tcp.ko
LKM(FS用)
 とりあえずext3で使いたい場合は以下
  jbd.ko
  ext3.ko

initrd内部のinitは以下のような感じ。モジュールをロードし始める所より下を
以下のスクリプトに置き換える。(echo "Loading xxxxx module"のあたり)
直接IPを記述するようにしているが、dhcpのオプションで色々なパラメータは渡せるので
適当に使わなそうな物を使ってIQNやTargetのIPを渡すと、複数のホストで共通のinitrdが使用できる。

 ##Network初期化
 insmod /lib/e1000.ko
 ip -o link set dev eth0 up
 dhclient -lf /var/run/dhcpc.lease -pf /var/run/dhcpc.pid
 ##iSCSI+FS初期化
 insmod /lib/jbd.ko
 insmod /lib/ext3.ko
 insmod /lib/scsi_mod.ko
 insmod /lib/sd_mod.ko
 insmod /lib/scsi_transport_iscsi.ko
 insmod /lib/libiscsi.ko
 insmod /lib/iscsi_tcp.ko
 iscsid
 iscsiadm -m discovery -t sendtargets -p <target ip>
 iscsiadm -m node -T <target iqn> -p <target ip> --login
 ##Rootマウント
 stabilized --hash --interval 250 /proc/scsi/scsi
 mkblkdevs
 mkrootdev -t ext3 -o defaults,ro /dev/sda1
 mount /sysroot
 setuproot
 switchroot

/dev/sdaに見えてる通り以降はローカルディスクから起動したのと何ら変わりなく使用可能。
ブロックサイズの性質上大きめのパケットが流れるので、Jumbo Frameが
使える場合は有効にしておくとppsが格段に下がって良い感じになる。


DHCPPXEの設定については色々な所で見かけるので割愛。
なおNFSROOTでのPXE BOOTも同様の方法で可能。iSCSIの代わりにNFSモジュールを
ロードしてmount.nfsでそのままmountするだけ。
カーネル組み込みのNFSROOTはあまりメンテされていないようでよく問題が
起きてるし、updateのコスト考えるとこっちの方が楽な感じ。