UEFI のための PXE (grub2) ブート環境を作成する (Rocky 8 / x86-64)

RockyLinux 8 で tftp を使用して、pxeboot システムを設定する方法について記しています。
BIOSブートとUEFIブート、どちらのファームウェアインターフェースにも対応できるようにします。

## 構築概要

- OSは、RockyLinux 8.10 x86-64 (最小限のインストール + 標準)
- tftp-server、dhcpd-server、httpd を使用
- grub2 メニューを使用
- 構築を簡略化するため、SELinux と Firewalld は無効にし、rootユーザーで実施
- 配布するディストリビューションは、RHEL系
- 対象のアーキテクチャは、x86-64

> **grub2メニュー**
grub2メニューを使用することで、BISOブートとFEIブートの間ですべての設定の一貫性を保つことが容易になります。
また、他のアーキテクチャをサポートする予定がある場合は、そのアーキテクチャを使用して、`grub2-mknetdir` コマンドを実行し、それらをtftpサーバに持ち込むことが簡単になります。

## TFTP/DHCPサーバーのセットアップ

### TFTP

tftp-server パッケージと追加の grub パッケージをインストールします。

- tftp-server
- grub2-efi-x64-modules
- grub2-pc-modules
- grub2-tools-extra <- インストール済みになっているはずです
- shim-x64 <- インストール済みになっているはずです
- shim-ia32

::: note info
他のアーキテクチャが必要な場合は、当該ディストリビューションのBaseOSまたはそのアーキテクチャの同等のリポジトリから grub2 モジュールパッケージを取得し、手動でインストールしてください。（ここでは割愛します。）
:::

``` console
# dnf install tftp-server \
  grub2-efi-x64-modules \
  grub2-pc-modules \
  grub2-tools-extra \
  shim-x64 \
  shim-ia32
```

ネットディレクトリを作成します。

``` console
# grub2-mknetdir --net-directory /var/lib/tftpboot/
Netboot directory for i386-pc created. Configure your DHCP server to point to /srv/tftp/boot/grub2/i386-pc/core.0
Netboot directory for x86_64-efi created. Configure your DHCP server to point to /srv/tftp/boot/grub2/x86_64-efi/core.efi
```

tftp ソケットを起動します。

``` console
# systemctl start tftp.socket
```

### DHCP

dhcp-server パッケージをインストールします。

``` console
# dnf install dhcp-server
```

DHCPサーバー `/etc/dhcp/dhcpd.conf` を設定します。

```shell:dhcpd.conf (設定例)
option space pxelinux;
option architecture-type code 93 = unsigned integer 16;
option pxelinux.mtftp-ip    code 1 = ip-address;
option pxelinux.mtftp-cport code 2 = unsigned integer 16;
option pxelinux.mtftp-sport code 3 = unsigned integer 16;
option pxelinux.mtftp-tmout code 4 = unsigned integer 8;
option pxelinux.mtftp-delay code 5 = unsigned integer 8;

subnet 10.100.0.0 netmask 255.255.255.0 {
        option routers          10.100.0.1;
        range           10.100.0.110 10.100.0.199;
        next-server     10.100.0.1;
        ## EFI Client Catch
        class "pxeclients" {
                match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
                if option architecture-type = 00:07 {
                        filename "boot/grub2/x86_64-efi/core.efi";
                } else if option architecture-type = 00:08 {
                        filename "boot/grub2/x86_64-efi/core.efi";
                } else if option architecture-type = 00:09 {
                        filename "boot/grub2/x86_64-efi/core.efi";
                } else {
                        filename "boot/grub2/i386-pc/core.0";
                }
        }
} 
```

::: note warn
IP アドレスは適宜環境に合わせてください。
:::

dhcp サービスを起動します。

```console
# systemctl start dhcpd
```

## ウェブサーバー（httpd）のセットアップ

### HTTPD

httpd パッケージをインストールします。
単純なWebサーバーを立ち上げることをお勧めします。特別な構成は必要ありません。デフォルトの `/var/www/html` パスを使用します。

::: note info
`/srv/www` のような別のホストを使用したい場合は、仮想ホストをセットアップする必要がありますが、ここでは割愛します。
:::

```console
# dnf install httpd
```

サービスを起動します。

```console
# systemctl enable --now httpd
```

使用したいディストリビューションを保存するためのディレクトリを作成します。

```console
# mkdir -p /var/www/html/os/rocky
```

### GRUB

`grub2-mknetdir` コマンドを実行した際に、`core.*` のファイルセットが作成されています。`grub.cfg` はそれらと同一ディレクトリに置かなければいけませんが、作業の重複を避けるため、すべてのgrub設定を `/var/lib/tftpboot` に作成し、それらを `core.*` を含む各ディレクトリ内にシンボリックリンクすることで簡略化します。

::: note warn
付属の `grub.cfg` が各ディレクトリあります。シンボリックリンクを作成する際にエラーの原因になるため、あらかじめリネームや削除をしておきます。
:::

基本となるgrub.cfgを作成します。

```console
tee /var/lib/tftpboot/grub.cfg << 'EOF'
set default=0
set timeout=60

menuentry 'EFI Firmware System Setup' $menuentry_id_option 'uefi-firmware' {
  fwsetup
}
menuentry 'Reboot' {
  reboot
}
menuentry 'Shutdown' {
  halt
}
EOF
```

シンボリックリンクを作成します。

```console
# cd /var/lib/tftpboot/boot/grub2/x86_64-efi
# ln -s ../../../grub.cfg
# cd /var/lib/tftpboot/boot/grub2/i386-pc
# ln -s ../../../grub.cfg
```

## ディストリビューションの追加

grubができたので、ディストリビューションを追加します。ここでは **Rocky Linux** を例にします。

### Rocky Linux

Rocky Linux 8 と Rocky Linux 9 の両方をダウンロードして、メニューをセットアップします。

```console
# cd /var/tmp

## Rocky Linux 8
# wget https://dl.rockylinux.org/pub/rocky/8/isos/x86_64/Rocky-8-latest-x86_64-dvd.iso

## Rocky Linux 9
# wget https://dl.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9-latest-x86_64-dvd.iso
```

::: note info
オンライン環境で最新バージョンを取得する設定になっています。
特定のバージョンが必要な場合は適宜URLを修正してください。
オフライン環境の場合は、インターネットに接続できるPC等でisoをダウンロードし、適宜転送してください。
:::

必要なデータを必要なディレクトリにコピーします。
pxeboot関連のイメージは `/var/lib/tftpboot/rocky-X-ARCH`(Xはバージョン、ARCHはアーキテクチャ)に置きます。
DVDのローカルミラーは `/var/www/html/os/rocky/X/ARCH` に置きます。

```console
## Rocky 8
# mount -o loop,ro Rocky-8-latest-x86_64-dvd.iso /mnt
# mkdir -p /var/www/html/os/rocky/8/x86_64
# rsync -vrlptDSH --delete /mnt/ /var/www/html/os/rocky/8/x86_64
# umount /mnt

## Rocky 9
# mount -o loop,ro Rocky-9-latest-x86_64-dvd.iso /mnt
# mkdir -p /var/www/html/os/rocky/9/x86_64
# rsync -vrlptDSH --delete /mnt/ /var/www/html/os/rocky/9/x86_64
# umount /mnt

## Copy the appropriate files over for the kernels
# mkdir -p /var/lib/tftpboot/rocky-{8,9}-x86_64
# cp /var/www/html/os/rocky/8/x86_64/images/pxeboot/* /var/lib/tftpboot/rocky-8-x86_64
# cp /var/www/html/os/rocky/9/x86_64/images/pxeboot/* /var/lib/tftpboot/rocky-9-x86_64
```

`grub.cfg` に追加の設定をします。
この設定では、キックスタートは使用していません。
GUIインストーラーが起動します。

```shell:grub.cfg
. . .
# Rocky 8
menuentry 'Install Rocky Linux 8 (No KS) (UEFI)' --class fedora --class gnu-linux --class gnu --class os {
  echo "Loading Rocky Linux 8 kernel..."
  linux rocky-8-x86_64/vmlinuz inst.repo=http://10.100.0.1/os/rocky/8/x86_64 inst.stage2=http://10.100.0.1/os/rocky/8/x86_64 ip=dhcp
  initrd rocky-8-x86_64/initrd.img
}
menuentry 'Install Rocky Linux 8 (No KS) (BIOS)' --class fedora --class gnu-linux --class gnu --class os {
  echo "Loading Rocky Linux 8 kernel..."
  linux16 rocky-8-x86_64/vmlinuz inst.repo=http://10.100.0.1/os/rocky/8/x86_64 inst.stage2=http://10.100.0.1/os/rocky/8/x86_64 ip=dhcp
  initrd16 rocky-8-x86_64/initrd.img
}
```

```shell:grub.cfg
. . .
# Rocky 9
menuentry 'Install Rocky Linux 9 (No KS) (UEFI)' --class fedora --class gnu-linux --class gnu --class os {
  echo "Loading Rocky Linux 9 kernel..."
  linux rocky-9-x86_64/vmlinuz inst.repo=http://10.100.0.1/os/rocky/9/x86_64 inst.stage2=http://10.100.0.1/os/rocky/9/x86_64 ip=dhcp
  initrd rocky-9-x86_64/initrd.img
}
menuentry 'Install Rocky Linux 9 (No KS) (BIOS)' --class fedora --class gnu-linux --class gnu --class os {
  echo "Loading Rocky Linux 9 kernel..."
  linux16 rocky-9-x86_64/vmlinuz inst.repo=http://10.100.0.1/os/rocky/9/x86_64 inst.stage2=http://10.100.0.1/os/rocky/9/x86_64 ip=dhcp
  initrd16 rocky-9-x86_64/initrd.img
}
```

以上で、Rocky Linux のインストールが可能になりました。

この設定では、オーソドックスなgrub2メニューにしていますが、サブメニューを追加して階層化することも可能です。

これらの方法については、また機会があれば掲載したいと思います。