Kickstart on XCP-ng: Five Silent Traps
2026年にもなってIDCでKickstartをやっている。
Kickstartとは
OSの自動インストールの仕組み。設定ファイル1つでパーティション、ネットワーク、ユーザー、パッケージをすべて定義し、人間の介入なしにインストールを完走させる。Red HatがRed Hat Linux 5.2(1998年)で導入した。同時期のSolarisにはJumpStartがあり、UNIXの世界ではOS自動インストールという概念自体は珍しいものではなかった。Kickstartは四半世紀を超えてなお、RHELとその派生ディストリビューション(AlmaLinux、Rocky Linux)の標準的なプロビジョニング手段として現役だ。
クラウドではない。物理サーバーにXCP-ngを載せて、その上にHVM仮想マシンを並べている。GUIの管理ツールはある。XenCenter(Windows)やXen Orchestra(Web)を使えばマウスでポチポチVMを作れる。使わない。Macしかないというのもあるが、それ以上に妙な矜持がGUIを許さなかった。CLIならスクリプトにできる。再現できる。SSHとxeコマンドだけでVMを作る。AlmaLinuxのminimal ISOをKickstartで自動インストールして、テンプレートVMを焼く。それだけのことに、丸一日かかった。
XCP-ngとは
Citrix XenServerのオープンソースフォーク。2018年にXenServerのフリー版が機能制限されたことを受けて、Vates社が完全なOSS代替として立ち上げた。ハイパーバイザーにXenを使い、dom0(管理用の特権VM)からxeコマンドでゲストVMを操作する。商用のVMware ESXiやHyper-Vに対するOSSの選択肢だ。
5つの罠を踏んだ。どれも「静かに失敗する」タイプで、エラーメッセージが出ない。
罠1: CD VBDが静かに失敗する
VMを作ってISOを挿入して起動した。40秒でhaltした。ディスクは空のまま。
VBD(Virtual Block Device)とは
Xenにおける仮想ディスクデバイス。物理マシンのSATAポートやCDドライブに相当する仮想的な接続点で、ここにVDI(Virtual Disk Image、実際のディスクイメージ)をマウントする。CD VBDは仮想CDドライブにあたる。
XCP-ngのxe vm-installで作ったVMにはCDドライブがない。xe vm-cd-insertはCD VBDが存在しないと、エラーも出さずに何もしない。ISOを挿入したつもりが、何も起きていなかった。
# これは静かに失敗する(CD VBDがないため)
xe vm-cd-insert vm=my-vm cd-name=AlmaLinux-minimal.iso
# 明示的にCD VBDを作成してからマウントする
ISO_VDI=$(xe vdi-list sr-uuid=${ISO_SR} name-label="AlmaLinux-minimal.iso" params=uuid --minimal)
xe vbd-create vm-uuid=${VM_UUID} vdi-uuid=${ISO_VDI} device=3 type=CD mode=RO bootable=truevm-cd-insertを信用しない。vbd-createで明示的にCD VBDを作る。これが最初の教訓だった。
罠2: HVM VMにブートパラメータを渡せない
Kickstartを使うには、カーネルパラメータにinst.ks=を渡す必要がある。PV-argsやxenstore-writeで設定しようとした。無視された。
HVMとPV——Xenの二つの仮想化方式
Xenには歴史的に二つの仮想化モードがある。PV(Paravirtualization、準仮想化) はゲストOSのカーネルを改変してXenと直接やりとりする方式で、Xen初期(2003年〜)の中心的な技術だった。PVではXenがカーネルを直接ロードするので、PV-argsでカーネルパラメータを自由に渡せる。HVM(Hardware Virtual Machine) はCPUのハードウェア仮想化支援(Intel VT-x / AMD-V)を使い、未改変のOSをそのまま動かす方式。HVMではBIOS/UEFIからGRUBを経てカーネルが起動するので、Xen側からパラメータを差し込む余地がない。現在のXCP-ngではHVMが標準で、PVはレガシーな位置づけだ。
HVM VMはBIOS/GRUBで起動する。Xenのパラ仮想化パラメータはHVMモードでは効かない。GRUBの設定ファイルを直接書き換えたカスタムISOを作るしかない。
ここで問題になるのがISO作成ツールだ。genisoimage(mkisofs)では--grub2-boot-infoオプションが使えない。GRUB2ブートに必要なフラグだ。xorriso一択になる。
genisoimageからxorrisoへ
mkisofsはISO 9660イメージを作成するツールとして長く使われてきた。そのフォークであるgenisoimageがDebianやRHEL系に同梱されている。だが、GRUB2のBIOSブートに必要な--grub2-boot-infoフラグをサポートしていない。xorrisoはGNU xorrisoプロジェクトが開発したISO操作ツールで、GRUB2を含むモダンなブート構成に完全に対応する。RHEL系のカスタムISOを作るなら、現時点ではxorriso一択だ。
xorrisoのコマンドは長い。でもこの方法でしかHVM VMにKickstartパラメータを渡せない。
# ISO展開
xorriso -osirrox on -indev ${ISO_SRC} -extract / ${WORKDIR}/
# GRUB cfgにカーネルパラメータを追加
KERNEL_ARGS="console=tty0 console=ttyS0,115200n8 earlycon=uart8250,io,0x3F8,115200n8 inst.text inst.headless"
sed -i "s|inst.stage2=hd:LABEL=AlmaLinux quiet|inst.stage2=hd:LABEL=AlmaLinux ${KERNEL_ARGS}|g" \
${WORKDIR}/boot/grub2/grub.cfg
# ISO再作成(--grub2-mbr と --grub2-boot-info が必須)
xorriso -as mkisofs \
-V "AlmaLinux" \
--grub2-mbr --interval:local_fs:0s-15s:zero_mbrpt,zero_gpt:${ISO_SRC} \
--protective-msdos-label \
-partition_cyl_align off -partition_offset 0 \
-partition_hd_cyl 92 -partition_sec_hd 32 \
--boot-catalog-hide \
-b images/eltorito.img -no-emul-boot -boot-load-size 4 \
-boot-info-table --grub2-boot-info \
-o ${ISO_DST} ${WORKDIR}/罠3: メディアチェックで12時間止まる
カスタムISOで起動した。画面に何も出ない。5分待った。10分待った。進まない。
原因はgrub.cfgのset default="1"だった。元のAlmaLinux ISOでは、メニューの0番が「Install」、1番が「Test this media & install」になっている。デフォルトが1——つまりメディアチェックが自動で走る。
xorrisoで再作成したISOにはチェックサム情報がない。メディアチェックは当然失敗する。そしてMedia check failed! System will halt in 12 hoursと表示されて、12時間待つ。
# default を 0 に変更("Install" を自動選択)
sed -i 's/set default="1"/set default="0"/' ${WORKDIR}/boot/grub2/grub.cfg一行の修正だ。これを忘れると12時間待ちぼうけになる。しかもシリアルコンソールを設定する前にこの罠を踏むと、メッセージすら見えない。ただ何も起きないまま時間だけが過ぎる。最も気づきにくいトラップだった。
罠4: シリアルコンソールが映らない
GUIなし環境ではxl consoleでシリアル接続するしかない。これが映らないとブラインド作業になる。罠3で12時間待つ羽目になったのも、そもそもコンソールが映っていなかったからだ。
シリアルコンソールには4つの設定が必要で、1つでも欠けると何も表示されない。
| 設定 | 場所 | 役割 |
|---|---|---|
serial --speed=115200 ... + terminal_input/output serial | GRUB cfg 先頭 | GRUBメニューをシリアルに出力 |
earlycon=uart8250,io,0x3F8,115200n8 | カーネルパラメータ | カーネル初期段階の出力 |
inst.headless | カーネルパラメータ | AnacondaのVGA初期化を抑制 |
platform:hvm_serial=pty | XCP-ng VM設定 | dom0側のPTY割り当て |
earlyconとは
Linuxカーネルの起動初期段階で、通常のコンソールドライバが初期化される前にシリアルポートへ出力するための仕組み。earlycon=uart8250,io,0x3F8,115200n8は「I/Oポート0x3F8にある8250互換UARTを115200bpsで使え」という指定。物理マシンのCOM1に相当する。Xen HVMではdom0側がUARTをエミュレートしており、この指定がないとカーネルがエミュレートされたUARTを正しく認識できない。
GRUBの設定はカスタムISOのgrub.cfg先頭に追加する。
serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1
terminal_input serial console
terminal_output serial consoleVM側の設定はxeコマンドで。
xe vm-param-set uuid=${VM_UUID} platform:hvm_serial=ptyearlyconがないとXen HVMのUARTエミュレーションをカーネルが初期化できない。GRUBメニューは出るのにカーネル起動後に何も出なくなる、という中途半端な状態になる。
罠5: AlmaLinuxのKickstartが世代間で互換でない
前の世代で動いていたKickstartをそのまま持っていった。パースエラーの嵐だった。
AlmaLinuxとは
CentOS 8のEOLを受けて2021年に誕生したRHELの1:1互換ディストリビューション。Red Hatが2020年にCentOS Streamへの転換を発表した(いわゆるCentOS終了事件)とき、CloudLinux社がフォークとして立ち上げた。Rocky Linuxと並んでCentOSの後継として広く採用されている。RHELのメジャーバージョンアップに伴い、Kickstartの構文にも破壊的変更が入る。
| 項目 | AlmaLinux 8(RHEL 8) | AlmaLinux 9(RHEL 9) |
|---|---|---|
| EULA | eula --agreed | 廃止。書くとエラー |
| ブートローダ | bootloader --location=mbr | bootloader --boot-drive=xvda |
| NTP | timezone --ntpservers=... | timesource --ntp-server=... に分離 |
| パーティション | /boot + swap + / | + biosboot 1MB(GPT+BIOSで必須) |
| network行継続 | network ... \(次行へ) | 行継続不可。1行で書く |
biosbootパーティションとは
GPT(GUID Partition Table)ディスクからBIOSでブートするために必要な小さなパーティション。MBRの場合、GRUBのステージ1.5コードはMBRとパーティションの隙間(MBR gap)に書き込まれる。GPTではこの隙間が存在しないため、代わりにbiosbootパーティション(通常1MB)を用意してそこにGRUBのコアイメージを配置する。UEFI環境では不要だが、XCP-ngのHVM VMはBIOSブートなので必須になる。
GPTがデフォルトになったことで、BIOSブートにはbiosbootパーティションが必須になった。これがないとGRUBがインストールされず、OSが起動しない。
part biosboot --fstype=biosboot --size=1
part /boot --fstype=ext4 --size=1024
part swap --size=4096
part / --fstype=xfs --grow --size=1networkコマンドの行継続が使えなくなったのも地味に痛い。静的IPだと1行が非常に長くなる。
network --bootproto=static --device=link --onboot=yes --activate --ip=192.168.1.200 --netmask=255.255.255.0 --gateway=192.168.1.1 --nameserver=8.8.8.8,1.1.1.1 --hostname=tmpl-alma10読みにくいが、こう書くしかない。
最終形
IDCにDHCPはない。Kickstartにも静的IPを直書きする。テンプレート用に192.168.1.200を1つ予約して、すべてのインストールでこのIPを使う構成にした。KickstartファイルとGRUB設定を分離する方法もあるが、今回はカスタムISOにすべて埋め込んだ。ISOを1つ作れば、あとはxeコマンドだけでVMが立ち上がる。
最終的なKickstartの構成はこうなっている。
#--- 基本設定 ---
text # テキストモードインストール
firstboot --disable
reboot # 完了後に自動再起動
#--- ロケール ---
lang en_US.UTF-8
keyboard --xlayouts='jp'
timezone Asia/Tokyo --utc
timesource --ntp-server=ntp.nict.jp # ← timezone から分離
timesource --ntp-server=ntp.jst.mfeed.ad.jp
#--- ネットワーク(テンプレート用固定IP、1行で書く) ---
network --bootproto=static --device=link --onboot=yes --activate --ip=192.168.1.200 --netmask=255.255.255.0 --gateway=192.168.1.1 --nameserver=8.8.8.8,1.1.1.1 --hostname=tmpl-alma10
#--- ディスク ---
ignoredisk --only-use=xvda
clearpart --all --initlabel --drives=xvda
zerombr
bootloader --boot-drive=xvda # ← --location=mbr は廃止
part biosboot --fstype=biosboot --size=1 # ← GPT+BIOSで必須
part /boot --fstype=ext4 --size=1024
part swap --size=4096
part / --fstype=xfs --grow --size=1
#--- ユーザー ---
rootpw --iscrypted $6$...
user --name=libraz --shell=/bin/bash ...
#--- パッケージ ---
%packages --ignoremissing
@minimal-environment
%end
#--- Post-install ---
%post --log=/root/ks-post.log
# SSH鍵配置、sudoers設定、sshd_config、シェル環境...
# 追加パッケージのdnf installはここではやらない
%endクローン後にIPを変更する運用だ。Kickstartのネットワーク設定を毎回書き換えるのは手間なので、テンプレートを1つ作ってxe vm-cloneで複製する方が現実的だった。
5つの罠に共通すること
どれもエラーメッセージが出ない。CDが挿さらない。パラメータが無視される。メディアチェックが黙って走る。コンソールに何も映らない。構文が黙って変わっている。
Kickstartの設計思想は「人間の介入なしに完走する」だ。だから途中で止まって聞いてこない。それは正しい設計だ。ただし、設定を間違えたときも聞いてこない。
シリアルコンソールを最初に確保すること。これがすべての起点になる。コンソールさえ映れば、何が起きているかはわかる。何が起きているかがわかれば、直せる。