DnsmasqとiPXEで構築するLinux PXEブートサーバー

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

サーバーラックを行き来しながら大量のUSBメモリを持ち歩く作業は、すぐにうんざりするものです。5台や10台もの物理ノードを管理する場合、ISOイメージを1本ずつ書き込んでいると膨大な時間が無駄になります。また、USBメモリは紛失しやすく、繰り返しの抜き差しによる劣化やデータ破損のリスクも伴います。

ネットワークブートを活用すれば、そうした煩わしさを一掃できます。Preboot Execution Environment(PXE)サーバーを使用すると、ローカルネットワーク(LAN)経由でベアメタル環境にOSインストーラーを直接ストリーミング配信できます。軽量なDHCP/TFTPデーモンであるDnsmasqと、HTTP/HTTPSに対応したオープンソースのブートローダーであるiPXEを組み合わせることで、既存のメインルーターを置き換えることなく、高速な集中プロビジョニングサーバーを実現できます。

クイックスタート(5分)

本手順ではDebian 12またはUbuntu 22.04/24.04 LTSを使用します。ここではDnsmasqをProxyDHCPサーバーとして構成します。このモードは、IPアドレスの割り当て管理を既存ルーターに完全に任せたまま、ブロードキャストドメイン内にネットワークブート用の指示情報のみを挿入します。

1. Dnsmasqのインストール

sudo apt update
sudo apt install -y dnsmasq wget

2. TFTPルートディレクトリの作成とiPXEバイナリの取得

現代の運用環境では、レガシーBIOSノードと最新の64ビットUEFIファームウェアの両方をサポートする必要があります。

sudo mkdir -p /var/lib/tftpboot
cd /var/lib/tftpboot

# 公式ビルド済みのiPXEブートローダーをダウンロード
sudo wget http://boot.ipxe.org/undionly.kpxe
sudo wget http://boot.ipxe.org/ipxe.efi
sudo chmod -R 755 /var/lib/tftpboot

3. DnsmasqをProxyDHCPとして設定

以下の設定を/etc/dnsmasq.d/pxe.confに保存します。192.168.1.50はサーバーの固定IPに、192.168.1.0は使用環境のサブネットに合わせて適宜変更してください。

# /etc/dnsmasq.d/pxe.conf

# 53番ポートの競合を回避するためDNSサービスを無効化
port=0

# 内蔵TFTPサーバーを有効化
enable-tftp
tftp-root=/var/lib/tftpboot

# ローカルサブネット上でProxyDHCPモードでリッスン
dhcp-range=192.168.1.0,proxy

# DHCPオプション93(クライアントシステムアーキテクチャ)を識別
dhcp-match=set:bios,option:client-arch,0
dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-match=set:efi-x86_64,option:client-arch,9

# 検出されたファームウェアに応じて適切なブートローダーを提供
dhcp-boot=tag:bios,undionly.kpxe,,192.168.1.50
dhcp-boot=tag:efi-x86_64,ipxe.efi,,192.168.1.50

# PXEサービスをネットワーククライアントに通知
pxe-service=tag:bios,X86PC,"ネットワークブート (BIOS)",undionly.kpxe
pxe-service=tag:efi-x86_64,X86-64_EFI,"ネットワークブート (UEFI)",ipxe.efi

4. デーモンの再起動とテスト

sudo systemctl restart dnsmasq
sudo systemctl status dnsmasq

テスト対象のマシンを同じスイッチに接続します。マシンの起動メニュー(POST画面中にF12、F11、Delキーなど)を開き、オンボードNICを選択して、iPXEのシェルバナーが表示されるか確認してください。

仕組みの解説

プロトコルのやり取りの流れを理解しておくことで、ネットワーク初期化中にクライアントが停止した際のトラブルシューティングがスムーズになります。

ProxyDHCPの動作原理

標準的なPXEでは、メインゲートウェイ上でDHCPオプション66(次期サーバーIP)およびオプション67(ブートファイル名)の設定が必要です。しかし、市販のブロードバンドルーターや廉価なアンマネージドルーターでは、これらの項目を変更できないケースがほとんどです。

ProxyDHCPはこの制約を回避します。メインルーターは従来どおりUDPポート67で通常のIP設定情報をリースします。それと同時に、DnsmasqはUDPポート4011を監視し、PXEClientベンダークラスが付与されたDHCPDISCOVERパケットを検知します。DnsmasqはTFTPアドレスとブートファイルのポインタのみを含んだ補助パケットを返信し、クライアント側で両方の情報が透過的にマージされます。

チェーンロードが不可欠な理由

NICに内蔵されているファームウェアは非常にシンプルです。多くの標準ROMは基本的なTFTPにしか対応していません。TFTPは512バイトという小さなブロック単位で転送を行い、パケットごとにACK(応答確認)を待つため、転送速度は1〜2MB/s程度まで低下します。ギガビット回線であっても、2.4GBほどある最近のLinuxインストールISOをTFTPで転送すると25分以上かかることがあります。

チェーンロードはこの転送速度のボトルネックを解消します。

  1. クライアントNICが、TFTP経由で軽量(約350KB)なiPXEバイナリを1秒未満でダウンロードします。
  2. iPXEが初期化され、物理ネットワークインターフェースの制御を引き継ぎます。
  3. iPXEがOSカーネル、initrdアーカイブ、cloud-initイメージをHTTP経由で回線速度の上限(ギガビットイーサネットで約112MB/s)で取得します。これにより、同じ2.4GBのISOが25秒足らずでダウンロード完了します。

OSの自動インストールの配信

iPXEが稼働すれば、HTTPで配信されるインタラクティブなブートメニューをマシンに読み込ませ、完全自動化された無人インストールを開始できるようになります。

1. NginxのデプロイとiPXEスクリプトの作成

標準的なWebサーバーをインストールします。

sudo apt install -y nginx

/var/www/html/boot.ipxeにメニュー用スクリプトを作成します。

#!ipxe

set server_ip 192.168.1.50
set base_url http://${server_ip}

:menu
menu ネットワークブートメニュー
item --gap --             ---------------- ディストリビューション ----------------
item ubuntu24            Ubuntu 24.04 LTS (自動インストール)
item debian12            Debian 12 Bookworm (ネットブート)
item --gap --             ---------------- ユーティリティ -------------------
item shell               iPXEシェルに移行
item reboot              マシンを再起動
choose --timeout 10000 --default ubuntu24 target && goto ${target}

:ubuntu24
echo Ubuntu 24.04 インストーラーカーネルを読み込み中...
kernel ${base_url}/ubuntu/vmlinuz ip=dhcp url=${base_url}/ubuntu/noble-live-server-amd64.iso autoinstall ds=nocloud-net;s=${base_url}/ubuntu/
initrd ${base_url}/ubuntu/initrd
boot

:debian12
echo Debian 12 インストーラーカーネルを読み込み中...
kernel http://deb.debian.org/debian/dists/bookworm/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux initrd=initrd.gz
initrd http://deb.debian.org/debian/dists/bookworm/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz
boot

:shell
shell

:reboot
reboot

2. タグマッチングによるブートループの防止

iPXEが起動すると、自身で再度DHCPリクエストを発行します。適切にフィルタリングを行わないと、Dnsmasqは最初のTFTPバイナリを何度も提供し続け、ループに陥ってしまいます。/etc/dnsmasq.d/pxe.confを編集し、DnsmasqがiPXEを判別してHTTPスクリプトを渡すように設定します。

# クライアントがすでにiPXEを実行中かを検出
dhcp-userclass=set:ipxe,iPXE

# iPXEクライアントをWebメニューへリダイレクト
dhcp-boot=tag:ipxe,http://192.168.1.50/boot.ipxe

# ハードウェアからの初期リクエストにのみ生バイナリを配信
dhcp-boot=tag:!ipxe,tag:bios,undionly.kpxe,,192.168.1.50
dhcp-boot=tag:!ipxe,tag:efi-x86_64,ipxe.efi,,192.168.1.50

# すでにiPXE内で実行されているクライアントに対してはPXEメニュー通知を抑止
pxe-service=tag:!ipxe,tag:bios,X86PC,"ネットワークブート (BIOS)",undionly.kpxe
pxe-service=tag:!ipxe,tag:efi-x86_64,X86-64_EFI,"ネットワークブート (UEFI)",ipxe.efi

sudo systemctl restart dnsmasqでサービスを再読み込みします。クライアントマシンを再起動すると、ハードウェアPXEから直接Nginx上のメニューへと遷移します。

実践のヒントとトラブルシューティング

本番ネットワーク上で不適切なDHCP設定を動作させると、オフィス全体のサブネットに通信障害を引き起こす恐れがあります。本番機器を接続する前に、必ず隔離されたテスト環境で設定を検証してください。

  • ブリッジ接続されたQEMU仮想マシンで検証する: 実機を扱う前に、ローカル環境でDnsmasqの設定をテストします。
    # ブリッジインターフェース(br0)経由でx86_64ゲストをPXEブート
    qemu-system-x86_64 -m 2048 -boot n \
      -netdev bridge,id=net0,br=br0 \
      -device virtio-net-pci,netdev=net0
  • リアルタイムでネットワークパケットをトレースする: クライアントマシンが「Searching for DHCP server…」で停止してしまう場合は、サーバー側で受信パケットを監視します。
    sudo tcpdump -i eth0 -n "port 67 or port 68 or port 69 or port 4011"

    正常であれば、ポート67への初期ブロードキャスト、ポート4011での即時応答、続いてポート69でのTFTP読み込みリクエスト(RRQ)が確認できます。

  • ローカルファイアウォールの設定を確認する: ホストのファイアウォールでUDPポート67、69、4011、およびNginx用のTCPポート80が許可されていることを確認します。
    sudo ufw allow 67/udp
    sudo ufw allow 69/udp
    sudo ufw allow 4011/udp
    sudo ufw allow 80/tcp
  • UEFIセキュアブートに注意する: 公式でビルドされているipxe.efiバイナリは、一般的なMicrosoft UEFI CAキーで署名されていません。対象のマザーボードでセキュリティ違反エラーが発生した場合は、UEFI設定でセキュアブートを無効にするか、企業用のMachine Owner Key(MOK)を使用してバイナリに署名してください。
Share: