Debian Installerの挙動について

この資料は[Debian Installer internals](http://ftp.gnome.org/pub/debian-meetings/2006/debconf6/slides/Debian_installer_workshop-Frans_Pop/paper/)にある*Running Debian Installer*の章を翻訳または要約して、説明を付け加えたものです。

元資料はDebian"etch"時点(2006年?)の資料のようなので、最新のものとは差異があるかもしれません。
("stretch"インストール時のsyslogを見る限りでは大きな違いはないように思います)

# Attribution

オリジナルの資料はGNU GPL v2の元、再配布や改変が許可されています。

> Copyright © 2006 © Frans Pop <fjp@debian.org>
> This article is free; you may redistribute it and/or modify it under the terms of version 2 of the GNU General Public License.

# Debian Installerって？

Debian InstallerはDebian系ディストリビューションでOSのインストールに使われているプログラムです。
公式からOSの[インストールイメージ](https://www.debian.org/distrib/index.ja.html)が配布されていますが、インストールイメージからマシンを起動した際に実行されるプログラムがDebian Installerです。

一方RedHat系OSでは[Anaconda](https://fedoraproject.org/wiki/Anaconda)というプログラムが使用されています。※ここでのAnacondaはPythonのディストリビューションの話ではありません。Pythonで書かれていますが。(紛らわしいですね)

# インストール処理の概要

インストール処理は5つのステージで構成されています。

1. ブートと初期化 : 追加コンポーネントをロードできるようにインストーラーをセットアップします
2. 追加コンポーネントのロード : 全機能が動作するようにインストーラーを拡張します
3. ネットワーク設定(ステージ1で行われていない場合)
4. パーティショニング
5. ターゲットシステムのインストール

最初の3ステップはインストール方法(CD-ROM/NETBOOT*1)によって大きく異なります。この3ステップで使用されるコンポーネント(udeb*2)はinitrd*3に含まれている必要があります。

下記テーブルは最初の3ステージに関連するコンポーネントをCD/DVDからブートした場合と、ネットワークからブートした場合に分けて記述しています。


<table>
<tr><td>ステージ</td><td>CD/DVD</td><td>ネットワークブート</td><td>コメント</td></tr>
<tr><td>-</td><td colspan="2">initrd-preseed</td><td>/preseed.cfgがある場合のみ</td></tr>
<tr><td>1</td><td colspan="2">localechooser</td><td>言語/国/ロケール選択</td></tr>
<tr><td>1</td><td colspan="2">kbd-chooser</td><td>キーボード選択</td></tr>
<tr><td>1</td><td>cdrom-detect</td><td>eth-detect</td><td>ハードウェアの検出とセットアップ</td></tr>
<tr><td>1</td><td></td><td>netcfg</td><td>ネットワーク設定</td></tr>
<tr><td>-</td><td>file-preseed</td><td>network-preseed</td><td>ブートプロンプトで選択されている場合</td></tr>
<tr><td>2</td><td></td><td>choose-mirror</td><td>ミラーの選択</td></tr>
<tr><td>2</td><td>load-cdrom (anna)</td><td>download-installer (anna)</td><td>追加コンポーネントの取得と展開</td></tr>
<tr><td>3</td><td>eth-detect</td><td></td><td>ハードウェアの検出とセットアップ</td></tr>
<tr><td>3</td><td>netcfg</td><td></td><td>ネットワーク設定</td></tr>
<tr><td>3</td><td>choose-mirror</td><td></td><td>ミラーの選択(ステージ4に必要な場合がある)</td></tr>
</table>

ステージ4と5で要求されるコンポーネントはインストール方法によらず同じです。

<table>
<tr><td>ステージ</td><td> </td><td>コメント</td></tr>
<tr><td>4</td><td>hw-detect</td><td>追加ハードウェアの検出</td></tr>
<tr><td>4</td><td>partman</td><td>パーティションの作成, ファイルシステムの作成, マウントポイントの選択</td></tr>
<tr><td>5</td><td>tzsetup</td><td>タイムゾーンの選択</td></tr>
<tr><td>5</td><td>clock-setup</td><td>ハードウェアクロックをUTCかローカルタイムゾーンに設定する</td></tr>
<tr><td>5</td><td>user-setup</td><td>ルートと通常ユーザーを作成する</td></tr>
<tr><td>5</td><td>base-installer</td><td>ベースシステム(debootstrap*5)とカーネルのインストール</td></tr>
<tr><td>5</td><td>apt-setup</td><td>ターゲットシステムにAPTの設定を行う(sources.list)</td></tr>
<tr><td>5</td><td>pkgsel</td><td>追加パッケージをインストールする(tasksel)</td></tr>
<tr><td>5</td><td>grub/lilo-installer; nobootloader</td><td>ブートローダーのインストール</td></tr>
<tr><td>5</td><td>finish-install</td><td>インストールの完了と再起動</td></tr>
</table>

**NOTE**

1. **NETBOOT** PXELINUXを使ってインストーラーをネットワークブートした際のことを指していると思います
2. **udeb** uはマイクロ(μ)を表しています。udebはDebianInstallerでのみ使用される小さなdebパッケージです。領域を無駄にしないようドキュメント等が省かれています。資料中の*コンポーネント*という用語はudebパッケージを指しているようです。Debian Installerは最小限のコンポーネントを初期RAMディスクに含めておき、その他必要なものを動的にロードするという挙動になるようです。
3. **initrd** 初期RAMディスクはLinuxシステム起動の際に使用される一時的なファイルシステムで、通常はルートファイルシステムをマウントするために使用されます。ISOインストーラーに含まれるisolinux.cfgを参照するとルートファイルシステムが指定されていません。つまりDebianのインストーラーはメモリ上に展開された初期RAMディスク上で動作するプログラムということになります。ちなみにRedHat系OSは若干挙動が異なって、初期RAMディスクをメモリに展開した後でインストーラーに含まれる LiveOS/squashfs.img をルートファイルシステムとしてマウントする、という挙動になるようです。*4
4. **ISOLINUX** ISOからシステムを起動するためのブートローダー。[Syslinuxプロジェクト](https://www.syslinux.org/wiki/index.php?title=The_Syslinux_Project)に含まれるコンポーネントの一つです。
5. **debootstrap** Debian系OSのベースシステムをインストールするために使用されるプログラム。Debian Installerでベースシステムの作成に使用されるほか、chrootで動作するDebian環境を作成するためにも使用される。

## インストーラーの起動／実行プロセス

インストーラーの起動プロセスは通常のシステム起動と似ています。ブートローダー(いつくかのケースではシステムのファームウェア)はKernelと初期RAMディスクをロードしてinitを開始します。ブートプロセスは`BOOT_DEBUG`パラメーターを追加することでデバッグすることが出来ます。

追加のカーネルパラメーターやブートパラメータを指定することが可能です。これは未適合のハードウェアをサポートするために必要になる場合があります。

次のリストは起動ステップの概要を説明しています。

1 - `/init`(initramfs)もしくは`/sbin/init`(initrd): 初期環境をセットアップする(/proc, /dev; `udev`を実行する)

2 - `busybox init`: /etc/inittabが読み込まれる。inittabには次の内容が含まれる:

- `::sysinit:/sbin/debian-installer-startup`
- `::respawn:/sbin/debian-installer`
- VT2 (busybox shell), VT3 (/var/log/messages), VT4 (/var/log/syslog)の初期化

3 - `/sbin/debian-installer-startup`

これは`/lib/debian-installer-startup.d`以下にあるスクリプトを実行したりソースとして読み込んだりする、モジュール実行者的なスクリプトです。実行される主な処理は

- 必要なら`devfs`と`sysfs`をマウントする
- `udev`/`hotplug`を実行する
- acpiモジュールをロードする
- syslogを開始する
- メモリをチェックして必要ならあらかじめ指定された制限に応じて`lowmem`モードを有効化する
- 初期RAMディスクの中に存在するdebconfテンプレートを読み込む
- ブートパラメーターを読み込んでdebconfパターン(`<component>/<template>=<value>`)を見つけ出し、debconfデータベースにセットする
- 使用されているコンソールタイプを検出する
- framebufferモジュールをロードする
- インストーラーがrescueモードで実行されるべきかを検出する

4 - `/sbin/debian-installer`

これは`/lib/debian-installer.d`以下にあるソーススクリプトを実行する、モジュール実行者的なスクリプトです。実行される主な処理は

- フレームバッファーデバイスが利用できるかどうか検出する
- コンソールを初期化する(空にする, UTF-8)
- どのdebconfフロントエンドが使用されるべきか検出する(text, newt, gtk)
- `main-menu`を開始する(詳細は次のセクション)
- システムを停止または再起動する(`main-menu`の終了後)

`/lib/debian-installer(-startup).d`以下のスクリプトはアーキテクチャに依存する場合があります。

## Debian Installer Menu

コンポーネント`main-menu`はインストールの残りの部分を実行します。このコンポーネントはメニューを動的に組み立てて、選択されたコンポーネントを実行します。メニューはdebconfのプライオリティが低または中の場合のみ表示されることに注意してください。プライオリティが高の場合もコンポーネントは存在していますが、ユーザーに表示することなく実行されます。

debconfのプライオリティは自動的に変更される場合があります。コンポーネントの実行が失敗した場合やユーザーが`<Go Back>`を選択してメニューに戻った場合、プライオリティは低く設定されます。このようなケースでは次のコンポーネントの実行に成功するとプライオリティは元の値に戻されます。

udebの重要な特徴の一つはpostinstスクリプトの実行が先送りされるということです。インストール時にはudebはファイルシステムへの展開のみ行われ、postinstスクリプトの実行はmain-menuによって行われます。実際にはあるコンポーネントが選択された際に実行されます。
言い換えればmain-menuはudebのコンフィギュレーションを行う責任を負っているとも表現できます。

メニューに含まれるコンポーネントは、dpkgステータスファイル(`/var/lib/dpkg/status`)に`Installer-Menu-Item`行を含んでいる必要があります。メニュー内のコンポーネントの順序は第一にコンポーネントの依存性で決まります。メニューアイテムナンバーはコンポーネントの順番が依存性だけでは決定できない際に使用されます。

次のサンプルのように、Providesは事実上他のコンポーネントが依存する状態を定義するために使用出来ます。(choose-mirrorはnetcfgのProvides: configured-networkに依存しています)

```
Package: netcfg
Status: install ok installed
Version: 1.23
Provides: configured-network
Depends: libc6 (>= 2.3.5-1), libdebconfclient0, libdebian-installer4 (>= 0.37),
         dhcp-client-udeb | dhcp3-client-udeb | pump-udeb, libiw28-udeb,
         cdebconf-udeb, ethernet-card-detection
Description: Configure the network
Installer-Menu-Item: 18

Package: choose-mirror
Status: install ok unpacked
Version: 1.19
Depends: libc6 (>= 2.3.5-1), libdebconfclient0, libdebian-installer4 (>= 0.38),
         configured-network
Description: Choose mirror to install from
Installer-Menu-Item: 23
```

また、このサンプルは`netcfg`の実行が成功し("installed")、かつ`choose-mirror`は未処理である("unpacked")ことを示しています。

いくつかのコンポーネントはメニューに含まれていますが、既定では実行されません。これらのコンポーネントは`finish-install`よりも大きなメニュー番号を持っています。例としてはdebconfのプライオリティを変えるもの、デバッグログの保存、CD-ROMの検査、インストールのキャンセルなどです。

## 追加の柔軟性を提供するフック

インストールの特定の段階で、コンポーネントはスクリプトの部品実行的な仕組みを提供しています。これにより、他のコンポーネントがスクリプトを所定のディレクトリに置いておくことで、postinstスクリプトがすでに実行されているか否かにかかわらず、インストールの特定の時点でコマンドを実行させることができます。

主なフックは:

`/usr/lib/base-installer.d`

 - base-installerによってdebootstrapの前に実行される

`/usr/lib/post-base-installer.d`

 - base-installerによってkernelの選択/インストールの直前に実行される

`/usr/lib/finish-install.d`

 - finish-installによって実行される

そのほか、特別な用途で使用される`/usr/lib/apt-setup/generators`, `/lib/main-menu.d`や`/lib/rescue.d`があります。

これら一般的なフックのほか、partmanは大きなフックのコレクションで構成されていて、すべてのスクリプトは機能拡張のためにフックとして配置されます。最も注目すべきフックはブートローダーがパーティショニングが要求を満たしているか検証するためのスクリプトを配置する`/lib/partman/finish.d`です。

## 特別なツール

インストーラーにはpostinstスクリプトやpreseedingスクリプトで使用される特別なコマンドが含まれています。

 - **anna-install** 追加の非標準d-iコンポーネント(udeb)をインストールするために使用される。このツールは`anna`がすでに実行されたかどうかチェックします。もしそうならばコンポーネントをすぐに展開し、そうでないならば`anna`が実行された際にインストールされるようスケジュールします。

 - **apt-install** 標準のパッケージ向けに(通常のaptと)同様の機能を提供し、ターゲットシステムにパッケージのインストールを行います。パッケージはbase-installerが実行済であれば即座にインストールされ、もしそうでなければ"extra"パッケージとしてbase-installerのpostinstスクリプトの終わり近くでインストールされるようスケジュールされます。

 - **log-output** コマンドを実行して出力(stdoutとstderrどちらかまたは両方)を`/var/log/syslog`に出力します。

 - **in-target** `/target`ディレクトリ*1にchrootしてコマンドを実行します。このコマンドはより要求の多いコマンドを実行できるようchroot環境をセットアップします。例えば`proc`や`sysfs`をマウントしたり`policy-rc.d`を作成します。このコマンドは当然ベースシステムがインストールされた後でなければ使用できません。

**NOTE**

1. **/target** インストーラーによって作成されたパーティションはこのディレクトリにマウントされます。

## preseedingを使用したインストールの自動化

現在3つの方法でpreconfigurationがサポートされています。

 - *initrd preseeding* `/preseed.cfg`は初期RAMディスク内に存在している必要があります。このファイルは**debian-installer-startup**処理で読み込まれます。
 - *file preseeding* **preseed/file**ブートパラメーターがある場合トリガーされます
 - *network preseeding* **preseed/url**ブートパラメーターがある場合トリガーされます

インストール方法によって利用できる設定が異なります。

preseedingの主要な目的はdebconfの質問に対して規定値を与えることです。ファイル／ネットワークpreseedingに対して、preconfigurationファイルが読み込まれる前の質問項目に対して規定値を与えても意味をなさないことに注意してください。

debconfの質問に規定値を与える一方で、preseedingは**preseed/early_command**と**preseed/late_command**を使って異なるタイミングでスクリプトを実行することが出来ます。**early_command**はpreconfigurationファイルが読み込まれた直後に実行されます(ファイル／ネットワークpreseedingのみ)。**late_command**は**finish-install**コンポーネントの一部として実行されます。

これらのスクリプトで複雑な処理を行うことが出来ます。一つの例としてはターゲットシステムにパッケージを追加でインストールすることが出来ます(`apt-install`を使って)。**early_command **で追加のd-iコンポーネントをインストールしたり(`anna-install`で)、もし特定のタイミングで実行されるべきスクリプトがあるのであれば、フックとして所定のディレクトリに配置することもできます。

## インストーラーのデバッグ

インストーラーの大部分はスクリプトなので、多くの問題は`set -x`を正しい位置にセットすることでかなり簡単にデバッグすることが出来ます。一番わかりやすい位置はデバッグしたいコンポーネントのpostinstスクリプトです。出力は`/var/log/syslog`で確認することが出来ます。ネットワークが開始された後であれば"Save debug logs"オプションを使って内部的なWebサーバーを起動することで簡単に出力を確認できます。

Cで書かれたコンポーネントのデバッグは少し大変です。オプションとしてはstrace udebを使用する方法(必要であればカスタムのイメージに追加する)や、ソースにデバッグコードが追加されたカスタムのコンポーネントを作成してそれを使用することです。

デバッグのためにコンポーネントが開始された際にコントロールを取得したいだろうと思います。インストーラーを`install debconf/priority=medium`とともに起動するのは良い方法です。これによりコンポーネントが開始される前にメニューが表示されます。

インストーラーがmidiumプライオリティで開始されると、オプションコンポーネント`open-ssh-client`をロードすることが出来ます。このコンポーネントをロードすることでログファイルを**scp**などで他のコンピューターにコピーすることが出来ます。udebやイメージを作り直すことなく新しいスクリプトやコマンドをテストする場合にも役に立つでしょう。netcatパッケージに含まれる**nc**コマンドはデフォルトで利用可能です。