自作PXE(DHCP/TFTP/HTTP)サーバでOS自動インストール Ubuntu 24.04編

に公開

この記事について

この記事ではPXE(DHCP/TFTP/HTTP)サーバを使ってOSの自動インストールを実施するために必要な作業や知識について解説します
学習のために執筆しているためAIは使用しません

環境

使用したPXE(DHCP/TFTP/HTTP)サーバ(自作)
https://github.com/callus-corn/tao/tree/v1.4.0
使用したハードウェア(2台)

  • サーバ:intel NUC 12
  • クライアント:intel NUC 12

インストールするOS:Ubuntu 24.04.2

PXEによるOSインストール概要

ブート方法にPXEを選択している場合、コンピュータの電源を入れると起動に必要な情報を取得するためにNICからDHCP通信が開始されます
起動に必要な情報がそろったらコンピュータはTFTPでブートローダをダウンロードし、ブートローダはカーネルと初期RAMイメージを起動してOSのインストールを実施します

本記事におけるOSインストール


前述のOSインストールを今回の条件で書き下すと上記のようになります
それぞれ見ていきます

1. ブートローダの起動:
UEFI->NIC->bootx64.efi->grubx64.efi

1.1 UEFI Secure Boot


2025年現在のUEFIによる起動は主にUEFI Secure Bootと呼ばれる機能を使用します
詳細はUEFI仕様[1-1]を参照してください
近年のハードウェアは署名を持つ信頼できるブートローダを起動することが求められています
署名を検証するには信頼できる証明書が必要なため、ハードウェアは出荷時点で信頼できる証明書を内部に埋め込む必要があります
現実的には多くのハードウェアにはマイクロソフトの証明書が埋め込まれており、マイクロソフトがビルドしたバイナリが起動可能です
この方法にはデメリットがあり、ハードウェアに証明書を埋め込むことのできるマイクロソフト以外のベンダは実質的にブートローダを開発できなくなる可能性を含んでいます
これではRedHatもCanonicalも困ってしまうので対策として信頼できるベンダの証明書を含んだ軽量なブートローダであるshimが提供されています[1-3]
shimは各ベンダにより署名されたブートローダを取得し、grub${ARCH}.efiを起動します

1.2 PXE


PXEはintelによって策定されたネットワークブートの仕様です
詳細は仕様を参照してください[1-4]
PXEではDHCPサーバとBoot Serverが分離している環境を想定しています
クライアントはDHCPを使ってブート用設定を受け取りDHCPオプションに含まれる各種情報に従ってブートローダをダウンロードします
ただし、DHCPサーバの応答にPXE用のDHCPオプションが存在しない場合はRFCで定められたDHCP情報からブートします
今回はDHCPサーバとBoot Serverを同一の構成としてPXEのDHCPオプションは使用しません
そのため、今回は正確にはPXEではなくただのDHCPによるブートになります
DHCPに存在するfileという項目[1-5]を正しく渡せばブート可能です

1.3 設定方法

UEFI

コンピュータの起動時に適当なファンクションキーを押せばUEFIの設定変更プログラムが起動します
今回対象としたIntel NUCではF2キーです
Boot用の項目がどこかにあるはずなのでPXEで起動するように変更します

BMC(Baseboard Management Controller)などハードウェア管理用の制御機構がある場合はそちらを使用してください

DHCP

使用しているDHCPサーバの設定に合わせてfileを設定してください
今回の自作DHCPではソースコードにハードコーディングしています

TFTP

shim(bootx64.efi)とgrubはインストールしたいisoファイルから取得します
通常のgrubはPXE機能が入っていないのでPXE機能が入っているgrubnetを使用します

wget https://releases.ubuntu.com/noble/ubuntu-24.04.2-live-server-amd64.iso
mount -o loop,ro ubuntu-24.04.2-live-server-amd64.iso /mnt
ar vx /mnt/pool/main/g/grub2-signed/grub-efi-amd64-signed_1.202.2+2.12-1ubuntu7.1_amd64.deb
tar xvf data.tar.zst
mkdir -p /EFI/boot/
cp /mnt/EFI/boot/bootx64.efi /EFI/boot/bootx64.efi
cp ./usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed /EFI/boot/grubx64.efi
umount /mnt

1.4 不明点

  • shimでPXEブートをしようとすると一番最初にrevocations.efiというファイルのダウンロードを試みますが、このrevocations.efiの役割は不明です
    これは予想になってしまいますが、失効した証明書を処理する機能だと思います
  • PXEのブートローダにはSyslinux系統のブートローダが多く使われています。それらのブートローダの名前は大体の場合pxelinux.0になるのですが、pxelinux.0のファイル名をgrub${ARCH}.efiにした場合に正常に起動できるのかは不明です
    これは予想になってしまいますが、起動不可だと思います。署名済みのSyslinux系統のブートローダが必要なはずですが、見つかっていません

1.5 参考

[1-1] UEFI Specifications
[1-2] UEFI Secure Boot と MIRACLE LINUX をブートさせるまで
[1-3] shim
[1-4] PXE Specification Version 2.1
[1-5] RFC 2131

2. カーネルの起動:
grub64.efi->vmlinuz->initrd/init

2.1 GRUB

GRUBはOSを起動するための非常に多機能なブートローダです
Linuxに限らずWindowsも起動可能ですが、ここでは大雑把に説明します
詳しく知りたい方はマニュアルを参照してください[2-1]
GRUBではコマンドと呼ばれる単位で機能を設定します
今回の用途ではlinuxとinitrdだけ把握しておけば十分です

  • linux file params...
    linuxカーネルのファイル名とコマンドラインパラメータを指定する
  • initrd file
    初期RAMディスクのファイル名

上記以外にも*.lstという形式で設定を記述することができますが、今回は使用しません
GRUBを起動すると/grub/grub.cfgから設定を読み込んで指定されたファイルをメモリに配置し、OSを起動します

2.2 vmlinuz

linuxカーネルは歴史的にvmlinuzというファイルに保存されています
このvmlinuzはカーネルの実態[2-2]であるvmlinuxを圧縮したファイルとvmlinuxを展開するためのプログラム[2-3]から構成されます
ブートローダからvmlinuzに処理が移行すると展開プログラムが実行されlinuxカーネルが起動します
起動したカーネルはカーネルと関係ないコマンドラインパラメータを/proc/cmdlineに書き出してinitrdの/initを実行します[2-4]

2.3 設定方法

grub.cfg

grub.cfgにvmlinuzとinitrdを設定します

mkdir /grub
cat << EOF > /grub/grub.cfg
set timeout=0
menuentry "Ubuntu" {
  linux /casper/vmlinuz ip=dhcp url=http://<HTTPサーバ>/images/ubuntu-24.04.2-live-server-amd64.iso ds=nocloud-net\;s=http://<HTTPサーバ>/autoinstall/ autoinstall
  initrd /casper/initrd
}
EOF
説明
set timeout=0 ブートを開始する時間。未設定の場合はユーザ入力を待ち続ける。0の場合は即座に開始する
menuentry "Ubuntu" ブートの選択肢
ip=dhcp IPアドレス設定(/init用の設定)
url=... ISOイメージのURL(/init用の設定)
ds=...;s=... cloud-initのdatasourceと設定ファイルのURL(後述)
autoinstall Subiquit用の設定(後述)

vmlinuzとinitrd

vmlinuzとinitrdはインストールしたいISOから取得します

wget https://releases.ubuntu.com/noble/ubuntu-24.04.2-live-server-amd64.iso
mount -o loop,ro ubuntu-24.04.2-live-server-amd64.iso /mnt
mkdir /casper
cp /mnt/casper/vmlinuz /casper/vmlinuz
cp /mnt/casper/initrd /casper/initrd
umount /mnt

2.4 参考

[2-1] GNU GRUB Manual 2.12
[2-2] linux
[2-3] misc.c
[2-4] init/main.c

Ubuntuの起動:
initrd/init->systemd->cloud-init,Subiquity

3.1 initrd

initrdはOSの様々な機能を設定するために必要なファイルをまとめたcpioアーカイブです
詳細はドキュメントを参照してください[3-1]
カーネルはinitrdをメモリ上に展開して/initを実行します
具体的な中身が確認したい場合は以下で確認可能です

wget https://releases.ubuntu.com/noble/ubuntu-24.04.2-live-server-amd64.iso
sudo mount -o loop,ro ubuntu-24.04.2-live-server-amd64.iso /mnt
unmkinitramfs /mnt/casper/initrd initrd/

/initはシェルスクリプトになっています
Ubuntuのインストール用のinitrdはインストール済みのものとは異なり/confに設定ファイルを持っていて/scripts/casperを起動するようになっています
このcasperは/proc/cmdlineを参照してurl=...に記載されている場所からwgetコマンドでisoファイルをダウンロードします
そしてisoファイルからsquashfsファイルを探してマウントします
/initは必要な設定を一通り実行したらrun-initコマンド[3-2]でsystemdを起動します

3.2 systemd

systemdは様々なシステムを管理するためのソフトウェアです
systemdはユニットファイルを読み込んでそこに記載されている設定に従ってシステムを管理します
ユニットファイルには依存関係を設定可能でsystemdはそれらをうまく処理して各サービスを起動します
今回はSnapd、cloud-init、Subiuityにだけ注目します

3.3 Snapd

Snapdはディストリビューション非依存のパッケージ形式であるsnapパッケージの管理サービスです
ディストリビューション非依存なので古いUbuntuで新しいパッケージを利用することも可能です
この特性を利用してOSのインストーラであるSubiquityをインストールするためにSnapdが利用されています
今回のUbuntuのインストールでもsystemdが立ち上がると即座にSnapdが起動し、ubuntu-server-minimal.squashfsからSubiquityのインストールを実行しています[根拠不足]

3.4 cloud-init

cloud-initはOSの初期設定を自動化してくれるツールです
UbuntuのインストールにおいてはSubiquityに設定ファイルを渡すことだけに注目すれば十分です
cloud-initはカーネルのコマンドラインパラメータを読み取って必要な設定ファイルの場所(datasource)を把握します
このとき、datasourceにNoCloudを指定するとurlで直接設定ファイルの置き場所を指定できます[3-3]
cloud-initは設定ファイルから中身を取得すると/run/cloud-init/combined-cloud-config.jsonに中身を書き出します[3-4]
今回のUbuntuインストールで必要な機能はこれだけです
注意点として設定ファイルはcloud-initを使用して取得するためmeta-dataとuser-data(またはvendor-data)の形式で記載する必要がありますが、実際にインストール処理を行うのはSubiquityであってcloud-initはOSのインストールを行いません
更なる詳細はドキュメントを参照してください[3-5]

3.5 Subiquity


SubiquityはOSのインストーラです
Ubuntuをインストールしたことがある方であれば必ず見たことのあるインストール画面はSubiquityによるものです
Subiquityはコマンドラインパラメータにautoinstallの文字列が含まれている場合、設定された項目に従って自動でインストールを実行します
インストールが完了したらインストール先のストレージから起動するようにUEFIの設定を変更します
詳細な設定方法はドキュメントを参照してください[3-6]

3.6 設定方法

ISOイメージ

ISOイメージをコマンドラインパラメータで指定した場所に置きます

wget https://releases.ubuntu.com/noble/ubuntu-24.04.2-live-server-amd64.iso
mkdir /images
cp ubuntu-24.04.2-live-server-amd64.iso /images

cloud-init

ホスト名、ユーザ名、パスワードは全てubuntuにします
meta-dataは空ファイルで問題ありません
meta-data

touch /autoinstall/meta-data

user-data

cat << EOF > /autoinstall/meta-data
#cloud-config
autoinstall:
  version: 1
  identity:
    hostname: ubuntu
    username: ubuntu
    password: $6$/X6MRgjz4jCIK8.G$FhJbD17m9qTcMA8FHMPcFvp7v2wvgmRBdvdEdTiZXZRwuZFUVsear9.vYY01QcwPuYAMRMvSJoJKfUlaBviU3/
  ssh:
    install-server: yes
EOF
説明
#cloud-config 必須
autoinstall Subiquityで実行する項目。cloud-initでは実行不可

3.7 不明点

  • /initでどのファイルがどこにマウントされるかの正確な情報は不明です
  • Snapd、cloud-init、Subiquityはsystemdによって起動されており*.serviceファイルがどこかに存在するはずですが、どこに存在しているかは不明です

3.8 参考

[3-1] initrd
[3-2] run-init
[3-3] NoCloud
[3-4] Storage locations
[3-5] Boot stages
[3-6] Autoinstall configuration reference manual

デモ

https://youtu.be/USm5SgRnu6k

Discussion