iPXE経由でAPU/APU2にUbuntu 18.04をインストールする

# はじめに
[PC Engines社製のAPU, APU2](http://pcengines.ch/apu.htm)はVGA出力がなく、通常はルーター・サーバーなどの用途で使用するためキーボード・マウスも接続せずに、必要な場合はシリアルコンソール経由でアクセスしています。

いろいろ試す都度、OSを再インストールするのは大変なので、APU/APU2のiPXE機能を利用して作業を半自動化しています。
これまでUbuntu 16.04を利用していましたが、18.04がリリースされてしばらく経つので、構成を変更することにしました。

同社製のALIXを利用していた時にはdebootstrapを使ってCFカードにOSをインストールしていましたが、APU/APU2ではmSATAを装着して、今回作成するtftpサーバーに接続し、OSをインストールしています。

# 機器の構成

今回構築するインストールイメージを提供するtftpサーバーは以下のような構成です。

* Hardware: ALIX (CPU: AMD Geode LX800, Mem: 256MB, Disk: 8GB, NIC: 100Mbps x3)
* OS: Debian Jessie 8.11
* Software:
  * TFTP, DHCPサーバー (dnsmasq)
  * Webサーバー (nginx or apache2)

ALIXは100MbpsのEthernetポートを3つ持ち、dnsmasqによりDHCPdが稼動するため、スイッチを経由してIPを配布し、インターネットへの接続なしにAPUにUbuntu 18.04を導入することができます。

ALIXを利用しているのは、たまたま手元にあったからです。現在であればRaspberry PiのようなLinuxマシンでも良いでしょう。

![20181002_apu_installer_alix.png](https://qiita-image-store.s3.amazonaws.com/0/78296/94d660ea-e746-cef7-7ad8-26e266acb7c5.png)


## APU/APU2の基本的な構成

SDカードは使用せずに、mSATA接続でSSDを使用しています。

## 機器構成の注意事項
2018/10/01時点では、dnsmasqを導入する際には、*dns-root-data*パッケージをアンインストールする必要があります。

バグリポートは既に上がっていますが、dnsmasqパッケージで対応するべきかもしれません。

* [bugs.debian.org - dns-root-data: Added entry in /usr/share/dns/root.ds breaks dnsmasq startup](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=902963)

また、Jessieでは問題ありませんが、Ubuntuにdnsmasqを導入する際には、systemd-resolved.serviceが稼動していると53番ポートを占有されてdnsmasqが起動しない可能性があります。

サービスを一時的に停止するには次のように"systemctl stop"を使い、恒久的に起動しないようにするためには"systemctl disable"を使います。

```bash:ubuntuにdnsmasqを導入する際のワークアラウンド
$ sudo systemctl stop systemd-resolved.service
$ sudo systemctl disable systemd-resolved.service
```

# 必要なパッケージの導入

今回はWebサーバーとしてnginxを利用しています。

```bash:debianでのdnsmasqとnginxの導入
$ sudo apt-get install dnsmasq nginx
$ sudo apt-get remove dns-root-data
```

## サーバー稼動の確認
あらかじめ必要なサービスが稼動することを確認しておきます。

```bash:サービスの起動
$ sudo systemctl restart dnsmasq.service
$ sudo systemctl restart nginx.service
```

エラーが出力されなければ、この段階では問題ありません。
53番ポートが占有されているなどのメッセージが出た場合には前述のように関連サービスをアンインストールしたり、無効化するなどの対応をとってください。

# ファイルの配置

ALIXにOSをインストールする場合には、CFカードを既に稼動しているLinuxマシンに接続するなどし、debootstrapでイメージをインストールします。

ここではALIXへのOSのインストールなどは省略し、既にシリアル接続や、SSH経由でTFTPサーバーとなるマシン(ALIX)にログインできる状態であるものとして進めます。ALIXではなくRaspberry Piなどを利用している場合は適宜、"ALIX"と書かれている箇所を読み替えてください。

まず、ALIX上にServer ISOと、このISOイメージから必要なファイルをコピーし、変更を加えます。

## ISOイメージの取得とマウント

国内にミラーされているServer LIVE ISOイメージではPreseedが稼動しないため、[Alternative downloads](https://www.ubuntu.com/download/alternative-downloads)からServer ISOイメージをダウンロードします。

ISOイメージは作業時点での18.04の最新版を取得してください。

> 【2020年11月追記】Ubuntu 20.04ではpreseedによるインストールは行なえなくなり、AutoInstallを利用することになります。AutoInstallによるOSインストールは別の記事にまとめていますが、移行にはそれなりの難しさがあるためiPXEを利用する場合は18.04をお勧めします。

また、ここではISOイメージをホームディレクトリに配置し、/mnt/isoにマウントするものとします。

```bash
$ wget http://cdimage.ubuntu.com/releases/18.04/release/ubuntu-18.04.5-server-amd64.iso 
$ sudo mkdir /mnt/iso 
$ sudo mount –o loop,ro ubuntu-18.04.5-server-amd64.iso /mnt/iso 
```

## TFTPサーバー用ファイルの配置

今回は **/app/tftp_root/** をTFTPサーバーのトップディレクトリとします。
変更する場合は適宜変更し、以下の指示を読み替えてください。

### ベースファイルの展開
ISOイメージからnetboot用のファイルを展開します。

```bash
$ sudo mkdir -p /app/tftp_root
$ cd /app/tftp_root
$ sudo rsync -av /mnt/iso/install/netboot/. .
$ sudo ln -s ubuntu-installer/amd64/linux . 
```

### defaultファイルの編集

自動的にインストールが進行するように、最下行でタイムアウト値(timeout)に"1"を設定します。

```bash:/app/tftp_rootで以下のコマンドを実行
$ vi pxelinux.cfg/default
$ grep timeout pxelinux.cfg/default 
## 次のような1行が出力される事を確認する
timeout 1
```

### txt.cfgファイルの編集

checksumを使っているので、後からsedで置換するための変数として```PRESEEDCHECKSUM```を埋め込んでいます。

```bash
$ vi ubuntu-installer/amd64/boot-screens/txt.cfg
```

append行は少し長いですが、1行で入力してください。

```text:ubuntu-installer/amd64/boot-screens/txt.cfg
default install 
label install 
        menu label ^Install 
        menu default 
        kernel linux 
        append vga=none initrd=ubuntu-installer/amd64/initrd.gz auto=true hostname=ubuntu locale=en_US.UTF-8 console-setup/charmap=UTF-8 console-setup/layoutcode=us console-setup/ask_detect=false pkgsel/language-pack-patterns=pkgsel/install-language-support=false netcfg/choose_interface=auto preseed/url=http://192.168.100.1/ubuntu/preseed.cfg preseed/url/checksum=PRESEEDCHECKSUM --- quiet console=ttyS0,115200n8
```
## Webサーバー用のファイルを準備する

ここではnginxを使っていますが、特別な設定は行なっていないため、apache2でも稼動します。
トップディレクトリにubuntu/を作成し、ISOイメージから4つのディレクトリをコピーして使用します。

```bash
$ sudo mkdir /var/www/html/ubuntu
$ cd /var/www/html/ubuntu
$ sudo rsync -av /mnt/iso/{dists,install,pool,.disk} . 
```

このdists,installなどのディレクトリと同じ場所に、preseed.cfgを作成します。
**/var/www/html/ubuntu以下で編集するファイルは、このpressed.cfgのみです。**

1. ユーザーID,パスワードは適宜変更してください。
2. mirror/http/hostnameにはALIXの内部向けポートに設定されているIPアドレスを設定してください。既にインターネットに接続するために内部ネットワークを192.168.100.0/24で構築している場合は、適切なIPアドレスに変更してください。（機器の構成にある図を参照）
3. (2.)のIPアドレスを変更した場合には、preseed/late_commandに書かれているIPアドレスも同時に変更すること

```text:/var/www/html/ubuntu/preseed.cfg
d-i debian-installer/add-kernel-opts string video=off elevator=deadline console=ttyS0,115200n8 
d-i debian-installer/locale string en_US 
d-i debian-installer/keymap string us 
d-i keyboard-configuration/xkb-keymap select us 
d-i keyboard-configuration/toggle select No toggling 
d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string unassigned-hostname 
d-i netcfg/get_domain string unassigned-domain 
d-i netcfg/wireless_wep string 
d-i mirror/protocol string http 
d-i mirror/country string manual 
d-i mirror/http/hostname string 192.168.100.1 
d-i mirror/http/directory string /ubuntu 
d-i mirror/http/proxy string 
d-i mirror/suite string bionic 
d-i passwd/user-fullname string Debian User 
d-i passwd/username string debian 
d-i passwd/user-password password temppwd 
d-i passwd/user-password-again password temppwd 
d-i user-setup/allow-password-weak boolean true 
d-i user-setup/encrypt-home boolean false 
d-i clock-setup/utc boolean false
d-i time/zone string Asia/Tokyo 
d-i clock-setup/ntp boolean true
d-i clock-setup/ntp-server string ntp.nict.jp 
d-i partman-auto/method string regular 
d-i partman-lvm/device_remove_lvm boolean true 
d-i partman-md/device_remove_md boolean true 
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true 
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true 
d-i partman/choose_partition select finish 
d-i partman/confirm boolean true 
d-i partman/confirm_nooverwrite boolean true 
d-i partman-md/confirm boolean true 
d-i partman-partitioning/confirm_write_new_label boolean true 
d-i partman/choose_partition select finish 
d-i partman/confirm boolean true 
d-i partman/confirm_nooverwrite boolean true 
d-i partman/mount_style select uuid 
d-i live-installer/net-image string /ubuntu/install/filesystem.squashfs 
d-i base-installer/install-recommends boolean true 
d-i base-installer/kernel/image string linux-generic 
d-i apt-setup/use_mirror boolean false 
d-i apt-setup/restricted boolean true 
d-i apt-setup/universe boolean true 
d-i apt-setup/backports boolean true 
d-i apt-setup/services-select multiselect security 
d-i apt-setup/security_host string security.ubuntu.com 
d-i apt-setup/security_path string /ubuntu 
d-i debian-installer/allow_unauthenticated boolean true 
tasksel tasksel/first multiselect none 
d-i pkgsel/include string openssh-server 
d-i pkgsel/upgrade select full-upgrade 
popularity-contest popularity-contest/participate boolean false 
d-i pkgsel/update-policy select none
d-i pkgsel/install-language-support boolean true
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true 
d-i grub-installer/bootdev  string default
d-i finish-install/reboot_in_progress note 
d-i preseed/late_command string \ 
        in-target /bin/sed -ie 's/192.168.100.1/jp.archive.ubuntu.com/g' /etc/apt/sources.list 
```

この構成は最低限にしています。Ansibleを利用したい場合は、late_commandで、authorized_keysを配置して、SSHのパスワードログインを拒否する設定にするなどの変更をすると便利でしょう。

18.04から ```d-i live-installer/net-image string /ubuntu/install/filesystem.squashfs```の設定を加えています。これは、Webサーバーでサブディレクトリにファイルを展開したために必要になりました。

また、一番最後ではmirrorに設定したWebサーバーのIPアドレスを国内のミラーサイトに変更しています。
違うミラーサイトを指定する場合には、ディレクトリ構造の違いも吸収する必要があるかもしれません。

## preseed.cfg と txt.cfg ファイルの関係性

txt.cfgにchecksumを埋め込んでいるため、/var/www/html/ubuntu/preseed.cfgを編集する度にtxt.cfgを書き換える必要があります。

```bash
$ i="$(md5sum /var/www/html/ubuntu/preseed.cfg|cut -d' ' -f1)"
$ sudo sed -e "s/checksum=[0-z]\+/checksum=${i}/" /app/tftp_root/ubuntu-installer/amd64/boot-screens/txt.cfg
```

定型的な作業はMakefileなどでタスクにまとめておくと便利でしょう。

# dnsmasqの基本的な構成

参考までに設定ファイルを転機しておきます。

## dnsmasq

APU/APU2にIPを振るためにdhcpdをeth1で稼動させています。

```text:/etc/dnsmasq.d/eth1.conf
log-queries
no-negcache

domain-needed
bogus-priv
expand-hosts
domain=ipxe.example.com

interface=eth1
no-dhcp-interface=eth0
dhcp-range=192.168.100.200,192.168.100.240,2m
dhcp-leasefile=/var/tmp/dnsmasq.leases
```

APU/Apu2にインストールイメージを配布するために、TFTPサーバーを構成している例です。

```text:/etc/dnsmasq.d/tftp.conf
enable-tftp
tftp-root=/app/tftp_root
dhcp-boot=pxelinux.0
```

## プロセスの再起動
ファイルを配置したらdnsmasqをリスタートします。

```bash
$ suso systemctl restart dnsmasq.service
```

# APU/APU2でのiPXEによるインストール

端末側で速度などを調整してから、APUにシリアルケーブルを接続し、電源を接続するとBIOSが起動します。
ここで入力待ちになるため、```C-b (Controlキーと'b'キーの同時押し)```を入力するとiPXEブートのプロンプトが表示されます。

```bash
iPXE> dhcp net0
iPXE> chain tftp://192.168.100.1/pxelinux.0
```
ここから先の処理はpreseed.cfgの記述に問題がなければ自動的にインストールが進み、再起動され、Ubuntu 18.04のログインプロンプトが表示されて完了します。

# デバッグ作業について

preseed.cfgの記述は成功するまで、止まった理由を確認し、書き直して、また実行という作業になります。
シリアル端末側で一切の入力ができないまま停止する場合もあれば、APU/APU2側でShellが実行できる場合もあります。

## TFTP, Webサーバー側での確認事項
/var/log/syslog, /var/log/nginx/access.log を確認し、”not found", " 404 " などで検索をします。

```bash:確認作業の例
$ grep 'not found' /var/log/syslog
Oct  1 13:32:24 localhost dnsmasq-tftp[2948]: file /app/tftp_root/linux not found

$ grep ' 404 ' /var/log/nginx/access.log
192.168.100.201 - - [01/Oct/2018:16:17:18 +0900] "GET /ubuntu/dists/bionic/InRelease HTTP/1.1" 404 168 "-" "Debian APT-HTTP/1.3 (1.6.3)"
```

## APU/APU2側での確認事項
インストーラーが何かしらエラーを出力し、処理をマニュアルで進められるようであれば、一度Shellを実行し、/var/log/syslog の内容を確認します。

# 遭遇した課題

squashfsイメージを取得するところの事は前述したので、それ以外で気になった点について残しておきます。

* 使用するISOイメージのタイミングによって、構成するデバイス名が /dev/sda だったり、/dev/sdb になったりする点
* 前回のUbuntu 16.04(build:20101020ubuntu451)では /dev/sda でしたが、今回は内部ドライブは /dev/sdb に見えている点

対応のため、partmanでディスクの指定を削除し、bootdevをdefaultに設定しています。
APU/APU2のfirmwareのバージョンによっても挙動が異なるかもしれず、これらの現象に遭遇しない可能性もあります。

以上