iPXE導入でサーバ機種にあわせたネットブートを
今回は、syslinuxのmenu.c32を利用した起動メニューの利用と機種情報によって動的にメニュー設定を変更する方法を紹介していきたいと思います。
(タイトルはそれらしいのにかえてみました(^^;;)
起動メニューを利用するには
DSASでは、PXELINUX+menu.c32を利用して起動時に使用するカーネルやイメージを選択できるようにしており、OSの起動中や起動後に使用する変数などをカーネルパラメータに記述したりしています。
状況によっては、起動時にパラメータを追加したり変更して起動したいケースがあるのですが、前回も書いたように、iPXEに用意されているメニューコマンドを使って作成したメニューでは、起動時にメニューを手動で変更することは現時点では難しいようです。
ですが、menu.c32ファイルやpxelinux.0をiPXEでロードして利用することができます。このことを利用すれば、既存のPXELINUXのメニュー設定の内容をほぼそのまま流用することもできそうですね。
menu.c32を直接ロードする方法とpxelinux.0をロードしてからmenu.c32を利用する2つの方法があるのですが、ここでは、menu.c32を直接ロードする方法を紹介します。
menu.c32を直接ロードする方法ですが、使用するmenu.c32のバージョンは3.86以下という制限があります。(こちらから取得できます。ここでは、syslinux-3.86に同梱されているファイルを使用しています。)
また、デフォルトのundionly.kpxeではCOMBOOTが無効なので、有効にする必要があります。さらに、カーネルパラメータの引き渡しをしてくれないので、修正をする必要があります。具体的には以下のようにしてから、ビルドします。(ソースの取得方法やビルドに関する詳細はiPXEのサイトをご覧ください。)
・COMBOOTを有効にする
# src/config/local/general.h に以下の行を追加 #define IMAGE_COMBOOT /* SYSLINUX COMBOOT image support */
・カーネルパラメータの引き渡し
# src/arch/i386/interface/syslinux/comboot_call.c に以下を追加 $ diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/i386/interface/syslinux/comb index 7ee5f61..5effa72 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/i386/interface/syslinux/comboot_call.c @@ -206,6 +206,12 @@ static int comboot_fetch_kernel ( char *kernel_file, char *cmdline ) { return rc; } + if ( ( rc = image_set_cmdline ( kernel, cmdline ) ) != 0 ) { + DBG ( "COMBOOT: could not set kernel command line: %s\n", + strerror ( rc ) ); + return rc; + } + /* Replace comboot image with kernel */ if ( ( rc = image_replace ( kernel ) ) != 0 ) { DBG ( "COMBOOT: could not replace with kernel: %s\n",
MACアドレスや機種情報に応じてメニューを生成
undionly.kpxeを用意したら、前回配置したものと入れ替えればOKです。
ここでは、他にもいくつかのファイルを用意するのですが、それらの目的とファイルの配置は、以下のようになります。
・特定のMACアドレスをもつサーバの場合には、指定した起動メニューを引き渡し、それ以外のサーバにはデフォルトの起動メニューを引き渡す
・機種毎に異なるIPMIのSOL対応シリアルポートにあわせて起動メニューの内容を変更する
/tftp/cgi-bin/ |-- boot_ipxe.cgi `-- menu.cgi /tftp/iPXE/ |-- undionly.kpxe |-- menu.c32 |-- boot.ipxe `-- menu.ipxe /tftp/iPXE/menu/ |-- default `-- xx-xx-xx-xx-xx-xx
スクリプトやCGIの中身は以下のようになります。
・boot.ipxe(前回と同じ)
#!ipxe imgfree chain http://${next-server}/cgi-bin/boot_ipxe.cgi?type=${product:uristring}&mac=${mac}
・boot_ipxe.cgi(サーバによって渡す内容を変更するCGI)
#!/bin/bash # クエリ文字列は、$QUERY_STRINGに格納されている # 変数type,macをクエリ文字列から生成する for q in $(echo ${QUERY_STRING} | tr "&" " "); do eval $q done # "%3A(:)"のままだと扱いにくいので"-"に変更 mac=$(echo ${mac} | sed -e 's/%3A/-/g') # 機種情報によって、シリアルポートの情報を変更 case $type in *Express*) port=1 rate=115200 ;; *) # デフォルト port=2 rate=19200 ;; esac echo "Content-type: text/plain" echo "Connection: close" echo "Content-Length: $(. /tftp/iPXE/menu.ipxe | wc -c)" echo "" . /tftp/iPXE/menu.ipxe
・menu.ipxe(menu.c32をチェーンロードし、機種情報によって設定されたパラメータに応じたメニューを取得するipxeスクリプト)
cat << EOF #!ipxe imgfree chain /iPXE/menu.c32 /cgi-bin/menu.cgi?port=${port}&rate=${rate}&mac=${mac} EOF
・menu.cgi(渡されたパラメータを元にメニューを生成するCGI)
#!/bin/bash for q in $(echo ${QUERY_STRING} | tr "&" " "); do eval $q done if [ -h "/tftp/iPXE/menu/${mac}" ]; then # MACアドレスのファイルがある場合 file=$mac else # デフォルト file=default fi echo "Content-type: text/plain" echo "Connection: close" echo "Content-Length: $(. /tftp/iPXE/menu/${file} | wc -c)" echo "" . /tftp/iPXE/menu/${file}
・default(デフォルトメニュー)
serial ${port} ${rate} timeout 100 prompt 0 menu title Default Boot Menu default web label web kernel root/web/vmlinuz append BOOT_IMAGE=root/web/vmlinuz initrd=pxeinit.gz dsas=web panic=10 console=tty0 console=ttyS${port},${rate}n8 label db kernel root/db/vmlinuz append BOOT_IMAGE=root/db/vmlinuz initrd=pxeinit.gz dsas=db panic=10 console=tty0 console=ttyS${port},${rate}n8 ID=
・xx-xx-xx-xx-xx-xx(MACアドレスがxx-xx-xx-xx-xx-xxのサーバ用メニュー)
serial ${port} ${rate} timeout 100 prompt 0 menu title Assigned Boot Menu default db label db kernel root/db/vmlinuz append BOOT_IMAGE=root/db/vmlinuz initrd=pxeinit.gz dsas=db panic=10 console=tty0 console=ttyS${port},${rate}n8 ID=101
httpを叩いて、確認してみる
上記によって、MACアドレスがxx-xx-xx-xx-xx-xxのサーバには専用のメニューを表示し、それ以外のサーバにはデフォルトのメニューが表示されます。またメニューの内容は、機種情報にしたがったシリアルポート設定となります。
では、実際の起動の流れを擬似的にhttpアクセスして追ってみましょう。
# iPXEが起動して実行するブート用のiPXEスクリプトを取得 $ wget -q -O - "http://192.168.0.1/iPXE/boot.ipxe" #!ipxe imgfree chain http://${next-server}/cgi-bin/boot_ipxe.cgi?type=${product:uristring}&mac=${mac}↓
#上記のスクリプト実行時に変数代入された状態でcgiを叩く # MACアドレスがxx-xx-xx-xx-xx-xxで、機種情報がExpressの場合 $ wget -q -O - "http://192.168.0.1/cgi-bin/boo_ipxe.cgi?type=Express&mac=xx%3Axx%3Axx%3Axx%3Axx%3Axx" #!ipxe imgfree chain /iPXE/menu.c32 /cgi-bin/menu.cgi?port=1&rate=115200&mac=xx-xx-xx-xx-xx-xx # MACアドレスがxx-xx-xx-xx-xx-xx以外で、機種情報がx3550の場合 $ wget -q -O - "http://192.168.0.1/cgi-bin/boo_ipxe.cgi?type=x3550&mac=yy%3Ayy%3Ayy%3Ayy%3Ayy%3Ayy" #!ipxe imgfree chain /iPXE/menu.c32 /cgi-bin/menu.cgi?port=2&rate=19200&mac=yy-yy-yy-yy-yy-yy↓
# menu.cgiを叩いて、メニューの内容を確認 # MACアドレスがxx-xx-xx-xx-xx-xxで、機種情報がExpressの場合 $ wget -q -O - "http://192.168.0.1/cgi-bin/menu.cgi?port=1&rate=115200&mac=xx-xx-xx-xx-xx-xx" serial 1 115200 timeout 100 prompt 0 menu title Assigned Boot Menu default db label db kernel root/db/vmlinuz append BOOT_IMAGE=root/db/vmlinuz initrd=pxeinit.gz dsas=db panic=10 console=tty0 console=ttyS1,115200n8 ID=101 # MACアドレスがxx-xx-xx-xx-xx-xx以外で、機種情報がx3550の場合 $ wget -q -O - "http://192.168.0.1/cgi-bin/menu.cgi?port=2&rate=19200&mac=yy-yy-yy-yy-yy-yy" serial 2 19200 timeout 100 prompt 0 menu title Default Boot Menu default web label web kernel root/web/vmlinuz append BOOT_IMAGE=root/web/vmlinuz initrd=pxeinit.gz dsas=web panic=10 console=tty0 console=ttyS2,19200n8 label db kernel root/db/vmlinuz append BOOT_IMAGE=root/db/vmlinuz initrd=pxeinit.gz dsas=db panic=10 console=tty0 console=ttyS2,19200n8 ID=
機種情報やMACアドレスに基づいて、メニューが動的に変更されているのが確認できました。
あとは、実際に起動して確認することになりますが、wget等で簡単に確認できるのもいいですね。
まとめ
さて、前回の最後に少し書きましたが、DSASでは、IPMIのSOLをよく利用していて、IPMIのSOL対応シリアルポートの情報を起動メニューのパラメータに記述しています。
SOL対応シリアルポートは機種毎に違っていて、ボーレートが速すぎると不安定な機種もあったりします。シリアルポートの設定が機種に応じた設定ではないために、SOL経由でOSの起動メッセージが見えなかったり、文字化けしたりで、機種による違いをどうにかしたいと思っていたことが、iPXE導入のきっかけとなります。
iPXE導入により、起動するサーバの機種にあわせてメニュー設定を書き換える必要がなくなりました。また、新しい機種を導入した場合でも、基本的に一つのCGIファイルを1回編集するだけですみます。
他にも、DMIのAsset Tag情報をサーバの識別IDとして利用して、ipxeスクリプトから${asset:uristring} で取得したりすると、いろいろ便利に使えるかもしれませんね。