金融データ分析基盤の構築 - PXEブート編

前回からかなり時間が空いてしまった。
主な要因はPXEブートをした経験がないこと、Linuxのブートプロセスへの理解が浅かったこと、お盆休みで旅行に行っていたことだった。(3つ目が特に大きい)


今回の内容

  • PXEブート環境の構築

  • cloud-init(cloud-config)を利用したインストール自動化

  • クライアント機1台でのOSインストールの検証

今回の内容は一言で言うと、OSの完全自動インストール環境を構築した。
これからKubernetesやHadoopなどのクラスタを組んでいく中で、複数のマシンのOSインストールを行うことになるが、それぞれで手動で行うと単純に手間がかかるだけでなくミスも多くなる。そのため、PXEブートを使用して完全に自動化したOSインストールを行えるようにした。

PXEブート環境の構築

ネットワーク構成図

ネットワーク構成図は非常にシンプルで、一般的な家庭での構成にPXEブート用のサーバとクライアントを追加したのみとなる。下の図ではPXEブート用サーバがProxy DHCP、TFTPサーバ、NFSサーバ、Webサーバをすべてになっているが、これらを分けるような構成も考えられる。PXEブート用サーバ内のそれぞれの役割は以下のようになっている。

  1. Proxy DHCP:ブートファイル名、TFTPサーバIPの伝達

  2. TFTPサーバ:ブートファイル、initrd、カーネルの配布

  3. NFSサーバ:ISOイメージファイルへのネットワーク越しのマウント

  4. Webサーバ:自動インストール用設定ファイルの配布

2, 3, 4はOSインストールの手順となるが、1(Proxy DHCP)のみはPXEブート特有の仕組みとなっている。一般に、PXEブートではDHCPサーバがIPの払い出しとともにブートファイル名とTFTPサーバのIPを伝達する場合が多いと思う。しかし、PXEブートに対応していないルータの場合はルータを変えるしかなくなってしまうので、Proxy DHCPという方法でブートファイル名とTFTPサーバのIP配布のみを実施することができる。

PXEブート用サーバ、クライアントのOSはともにUbuntu server 24.04(noble numbat)となっている。

ネットワーク構成図

Proxy DHCP

前述したとおり、PXEブートではDHCP要求が来たらIP払い出しとともにブートファイル名とTFTPサーバのIPを渡してあげる必要がある。しかし、自宅のモデム(ルータ一体型)はPXEブートの設定ができないため、今回は同一LAN内にProxy DHCPサーバを立てる。

使用するのはdnsmasqというツールで、これ一つでDNS、DHCP、TFTPの機能がすべて入っている。今回はDNSは使わないので、DHCPとTFTPの設定のみをしている。

dnsmasqは以下のコマンドでインストール可能で、設定ファイルは/etc/dnsmasq.confに存在する。

sudo apt install dnsmasq

設定するのは以下の内容で、主にやりたいこととしてはDHCP要求があった場合にIPの払い出しはモデムに任せてブートファイル名(pxelinux.0, syslinux.efi)とTFTPサーバのIPを配布するというもの。いろいろ動かしながら試行錯誤して、正常に動く設定が以下だった(うまいことドキュメントを探せなくて苦労していた)。

TFTPサーバのIPは、dhcp-bootのオプションでIPを指定しなければ自身のIPを自動的に設定するようになっている。

interface=enp1s0
bind-interfaces
dhcp-range=192.168.1.0,proxy
dhcp-match=set:efi-x86_64,option:client-arch,7 # EFI bytecode
dhcp-match=set:efi-x86_64,option:client-arch,9 # EFI x86-64
dhcp-match=set:bios,option:client-arch,0  # Intel x86PC
dhcp-boot=tag:efi-x86_64,"syslinux.efi"
dhcp-boot=tag:bios,"pxelinux.0"
pxe-service=x86PC, "Install Linux", pxelinux

ちなみに、dhcp-matchとdhcp-bootを使ってレガシーBIOSかUEFIかによってブートファイルを分けるというようなこともできる。上ではレガシーBIOSでpxelinux.0、UEFIでsyslinux.efiを渡すようにしている。

設定を模索する際には同一LAN内の管理用ノパソでtcpdumpというコマンドを使用して、特定ポート(67, 68, 69)を監視することでどこまで正常に動いているかを確認していた。実際に、初めはDHCP要求とIP払い出ししか観測できなくて、設定をいろいろ試している間にProxy DHCPの通信も確認できた。

TFTPサーバ(ブートローダ、initrd、vmlinuz配布用)

ブートファイル名とTFTPサーバのIPをクライアントに渡せるようになったので、次はTFTPサーバの構築とファイルの配置を行う。といってもdnsmasqで以下の設定を書くだけでTFTPサーバを構築できる。

enable-tftp
tftp-root=/tftpboot

あとは、ブートローダとinitrd、vmlinuzをそれぞれ/tftpboot配下に配置しておく。ブートローダは以下のコマンドでそれぞれインストールできるはず。あとブートローダは単体だけで動かない場合があるので、その場合は依存ファイル(ldlinux.c32、ldlinux.e64)も同じ階層に配置する。(各ファイルは適当にファイル名でfindして見つけた)

sudo apt install syslinux

initrd(初期RAMディスク)、vmlinuz(カーネル)はそれぞれインストールするOSのISOファイルをマウントして中から抽出してくる。今回はUbuntu server 24.04を使っていて、それを適当なところにマウントしてcasperの中にあるファイルをコピーしてきた。

さらに、ブートローダには設定ファイルが必要となっていて、デフォルトではブートローダのファイルと同じ階層にpxelinux.cfg、syslinux.cfgというディレクトリを探しに行くようになっている。そのディレクトリの中で、まず、"01-<クライアント機のMACアドレス>"のファイル名を探し、なかった場合に"default"になる。今回はまだクライアントごとに設定を変えるつもりはないので、"default"のみ作っておく。pxelinux.cfg, syslinux.cfgどちらも同じファイル。

default linux
prompt 1
timeout 30
label linux
        kernel /images/noble/vmlinuz
        ipappend 1
        append initrd=/images/noble/initrd netboot=nfs nfsroot=192.168.1.10:/mnt/ubuntu_iso autoinstall ds=nocloud-net;s=http://192.168.1.10/autoinstall/

最終的なTFTPサーバの配布するディレクトリはこんな感じになる。

/tftpboot/
├── images
│   └── noble
│       ├── initrd
│       └── vmlinuz
├── ldlinux.c32
├── ldlinux.e64
├── pxelinux.0
├── pxelinux.cfg
│   └── default
├── syslinux.cfg
│   └── default
└── syslinux.efi

NFSサーバ(OSイメージのマウント用)

最後にNFSサーバの構築をする。実はこのNFSサーバの構築はPXEブートではおそらく一般的ではないとと思うが、この構成にしているのは理由がある。

まず、一般的にはWebサーバでISOファイルをそのまま配布するというのが多いと思う。しかし、今回OSのインストール対象にしているのはかなり低スペックなマシンのため、なんとISOファイルがRAMに乗り切らないのだ。OSインストール時はinitrd/initramfsにてRAM上にファイルシステムを構築し、一時的にファイルをそこに置く。そのため、Ubuntu serverの2GBくらいあるISOファイルが乗り切らないの途中でエラーになってしまう。

そこで、NFSサーバによってネットワーク越しにOSイメージをマウントすることで、ISOファイルのダウンロードをせずにOSのインストールができるようになる。(OSインストールを行わないネットワークブートではなく、ネットワーク越しにマウントしたうえでローカルストレージにOSインストールを行う)

NFSサーバはnfs-kernel-serverというパッケージをインストールして構築し、ISOファイルをマウントしたディレクトリを公開した。設定は/etc/exportsに以下のようにマウントしたディレクトリを別の場所にコピーし、それを記載した(マウントした場所を直接だとアンマウントによって公開できなくなる可能性がある。)

/mnt/external_hdd1/nfs/ubuntu_iso 192.168.1.0/24(ro,sync,no_subtree_check,insecure)

cloud-initによるインストール自動化

次に、PXEブート後のOSインストール時の対話的な動作を排除し完全自動化を行う。

cloud-configファイル

Ubuntuではcloud-initによってOSインストールの際の設定を自動化できる。あまり深く理解はできていないのだが、cloud-initではブートローダ設定のappendにautoinstallをつけるとユーザ名やパスワードなどの設定を事前記載できるらしい(パッケージなども入れられるようなので、その辺りも今後使えそう)。

設定ファイルは以下の通りで、現状では非常に単純な設定のみとしている。
(外部にさらしていないので、パスワードは"password"とする)

#cloud-config
autoinstall:
  version: 1
  identity:
    hostname: ubuntu-server
    username: ubuntu
    password: $6$CgGPb2bK8YGaY7WN$u7YrrOdwq5JJwaTp0rouxcKETG0oMTbVEZR6/M4jE5fjbtQGv5deDazJC/0BV8quYNV57nlU7OimhpY7oeCqU0
  ssh:
    install-server: yes

Webサーバ

先ほどのファイルを配布するためのWebサーバをnginxで立てる。
nginxは単純にaptでインストールし、そのあと/etc/nginx/sites-available/defaultにautoinstallというパスを公開するように設定する。

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /mnt/external_hdd1/www/;

        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                try_files $uri $uri/ =404;
        }

        location /autoinstall {
                autoindex on;
        }
...

これまでの設定でPXEブートとインストール自動化の準備が完了した。

実機での検証

実際に実機でOSインストールの検証を行ってみる。
部屋が汚いので写真などはとっていないが、簡単に手順だけ記載する。

手順といってもやることは本当になく、インストールはほぼ完全に自動化されている。

  1. クライアント機にLANケーブルをつなぐ

  2. クライアント機の電源を入れる

上記の操作のみで指定したOS(Ubuntu server 24.04)のインストールが始まる。BIOS/UEFIはHDDやUSBストレージなど決まった順番でブートローダを探しに行く。その中にPXEブートがサポートされていれば自動的にそれが試されるため、BIOSの優先順位をいじることもなく完了する。

次にやること

今回で、クライアント機のOSインストールができたところなので、ここからAnsibleによってクラスタ(Kubernetes、Hadoop)の環境構築などをしようかなと思ってる。

参考文献

  1. https://gihyo.jp/admin/serial/01/ubuntu-recipe/0787

  2. https://frsyuki.hatenablog.com/entry/20080720/p2

  3. https://cloud-init.io/

この記事が気に入ったらサポートをしてみませんか?