Arch Linuxのインストール練習環境としてループバックデバイスを活用する

## 概要

最近、[Arch Linux](https://www.archlinux.jp/)を触り始めました。CentOSやUbuntuといったインストーラが提供されているLinuxは扱ったことがあるのですが、Arch LinuxはCLIからコマンドを打ち込んでインストール作業を進める感じなので、だいぶ硬派な感じです。

Arch LinuxはCLIから作業することもあり、インストール手順を間違えた際に手順を一からやり直すのがちょっと面倒なので、「インストールをミスっちゃた…&#x1f97a;」というガッカリ感があります。何回かインストールを繰り返すことで手順を憶えてゆくという考え方もありますが、できればインストールのトライ＆エラーのサイクルは小さくしたいものです。
(短い期間で手順を憶えられるなら、それに越したことはないですよね)

何か良い方法はないだろうか…と考えてみたら、Linuxのループバックデバイスを利用することで、Arch Linuxのインストール練習を効率的に行えそうな気がしてきました。調べてみたところ、思いのほか上手いことインストール練習用環境が構築できたので、一連の手順をメモしておこうと思います。

## ループバックデバイスを利用したArch Linuxインストール

Linuxにはループバックデバイスというものがあり、 `/dev/loop0` のようなデバイスとファイルを紐づけることができます。よくある例としてはISOイメージをmountするようなケースがあるかと思います。

```sh
$ sudo mkdir /cdrom
$ sudo mount -o loop archlinux-2021.05.01-x86_64.iso /cdrom
```

ディスクに見立てたファイルを `/dev/loop0` として扱うことでArch Linuxの一連のインストール手順の練習環境にできそうです。

### 今回の環境

今回試した環境は、Arch Linuxの上でディスクに見立てたファイルにArch Linuxをインストールし、QEMUで動かす(動作確認する)という形になっています。
そのため、あらかじめ「Arch Linuxがインストールされた環境が用意されていること」という鶏卵問題が存在しています…。

### 事前準備

CDブートしてArch Linuxをインストールする場合は必要なコマンドが揃っていますが、インストール後のArch Linux環境ではそれらのコマンドが用意されていません。そのため、あらかじめArch Linuxのインストールに必要なパッケージを用意します。

```sh
$ # Arch Linuxのインストールに必要なパッケージを用意する。
$ sudo pacman -S \
    extra/qemu \
    extra/edk2-ovmf \
    extra/gptfdisk \
    extra/gparted \
    arch-install-scripts \
    dosfstools
```

### ディスクに見立てたファイルを用意する

4GBほどのファイルを用意し、 `/dev/loop0` に紐づけます。

```sh
$ # 4GBのディスクに見立てたファイルを作成する。
$ touch disk.img
$ dd if=/dev/zero of=disk.img bs=1M count=$((1024*4))
$
$ # loop0(ループバックデバイス)にファイルを設定する。
$ sudo losetup /dev/loop0 disk.img
$ losetup -l
NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE                 DIO LOG-SEC
/dev/loop0         0      0         0  0 /home/arch/work/disk.img    0     512
```

### パーティションを設定する

`/dev/loop0` に対してArch Linuxインストール用のパーティションを設定します。
今回はLinuxインストール用のパーティションを設定していますが、様々なファイルシステム向けのパーティションを設定するといった実験にも応用できそうです。

```sh
$ # パーティションを設定する。
$ sudo gdisk /dev/loop0
```

[gdisk](https://wiki.archlinux.jp/index.php/Fdisk#gdisk)コマンドを使用してパーティションを設定します。

#### EFIシステムパーティションの作成

```sh
Command (? for help): n ★入力
Partition number (1-128, default 1):
First sector (34-8388574, default = 2048) or {+-}size{KMGTP}:
Last sector (2048-8388574, default = 8388574) or {+-}size{KMGTP}: 512M ★入力
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): ef00 ★入力
Changed type of partition to 'EFI system partition'
```

#### スワップパーティションの作成

スワップパーティションのサイズは512MBとします。

```sh
Command (? for help): n ★入力
Partition number (2-128, default 2):
First sector (34-8388574, default = 1050624) or {+-}size{KMGTP}:
Last sector (1050624-8388574, default = 8388574) or {+-}size{KMGTP}: 1024M ★入力
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8200 ★入力
Changed type of partition to 'Linux swap'
```

### Linuxパーティションの作成

最後にLinuxパーティションを作成します。ここはほぼEnterでデフォルト値のまま入力するだけでOKです。

```sh
Command (? for help): n ★入力
Partition number (3-128, default 3):
First sector (34-8388574, default = 2099200) or {+-}size{KMGTP}:
Last sector (2099200-8388574, default = 8388574) or {+-}size{KMGTP}:
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'
```

### パーティション情報の書き込み

作成されたパーティションは以下になります。

```sh
Command (? for help): p ★入力
Disk /dev/loop0: 8388608 sectors, 4.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 81F92DF9-A075-4CAC-9D0F-556F5DBD6BF6
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 8388574
Partitions will be aligned on 2048-sector boundaries
Total free space is 6108 sectors (3.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1048576   511.0 MiB   EF00  EFI system partition
   2         1050624         2097152   511.0 MiB   8200  Linux swap
   3         2099200         8388574   3.0 GiB     8300  Linux filesystem
```

最後にパーティション情報を書き込んで作業完了です。

```sh
Command (? for help): w ★入力

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/loop0.
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot or after you
run partprobe(8) or kpartx(8)
The operation has completed successfully.
$
```

gdiskを終了した後は、パーティション情報を再読み込みすることで、作成したパーティション( `/dev/loop0p1` とか)が見えるようになります。

```sh
$ # パーティション情報を再読み込みする。
$ sudo partprobe /dev/loop0
```

### ファイルシステムのフォーマットとマウント

作成したパーティション上にファイルシステムを作成します。

```sh
$ # EFIパーティションをフォーマットする。
$ sudo mkfs.fat -F32 /dev/loop0p1
$
$ # スワップを作成する。
$ sudo mkswap /dev/loop0p2
$
$ # Linuxパーティションをフォーマットする。
$ sudo mkfs.ext4 /dev/loop0p3
```

Arch Linuxではファイルシステムに対してCLI上からインストールを進めるため、EFTとLinuxのパーティションをマウントします。

```sh
$ # /mnt/bootにはEFTパーティションをマウントするため、
$ # Linuxパーティション→EFIパーティションの順でマウントする。
$ sudo mount /dev/loop0p3 /mnt
$ sudo mkdir /mnt/boot
$ sudo mount /dev/loop0p1 /mnt/boot
```

### Arch Linuxの配布物をインストールする

配布物のインストールは `pacstrap` コマンドで行います。

```sh
$ # Arch Linuxの配布物をインストールする。
$ sudo pacstrap /mnt base linux linux-firmware
```

### /etc/fstabの設定

`genfstab` コマンドで `/etc/fstab` を生成します。

```sh
$ genfstab -U /mnt | sudo tee -a /mnt/etc/fstab
# /dev/loop0p3
UUID=a322f882-4104-4677-93de-4eabdd284da3       /               ext4            rw,relatime     0 1

# /dev/loop0p1
UUID=5631-C2B0          /boot           vfat            rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro   0 2

# /dev/sda2
UUID=ca6f5a48-18db-433f-9c65-347325718f5f       none            swap            defaults        0 0
```

### ブートローダの設定

ブートローダーの設定を行います。ここからは `arch-chroot` で `/dev/loop0` が指すパーティションにchrootして作業します。

```sh
$ sudo arch-chroot /mnt
[root@arch /]# # 設定ファイルの編集用にviをインストールします。
[root@arch /]# pacman -S vi
```

EFIパーティションにbootctlをインストールします。

```sh
[root@arch /]# bootctl install
```

ブート時に使用するデフォルトコンフィグを作成します。ここでは `arch` というデフォルトコンフィグを参照する設定としています。

```sh
[root@arch /]# vi /boot/loader/loader.conf
...(loader.confの内容を編集する)...
[root@arch /]# cat /boot/loader/loader.conf
default arch
timeout 3
#console-mode keep
editor no
```

デフォルトコンフィグは `/boot/loader/entries/arch.conf` に作成します。

```sh
# cat <<_EOF > /boot/loader/entries/arch.conf
title   Arch Linux
linux   /vmlinuz-linux
initrd  /initramfs-linux.img
_EOF
#
# # ルートパーティションのUUIDを使用する設定があるので、
# # これは/etc/fstabから抽出する。
# cat /etc/fstab | grep ext4 | awk '{ print "options root="$1" rw" }' >> /boot/loader/entries/arch.conf
```

今回の例では、以下のような設定内容になります。

```sh
# bootctl list
Boot Loader Entries:
        title: Arch Linux (default)
           id: arch.conf
       source: /boot/loader/entries/arch.conf
        linux: /vmlinuz-linux
       initrd: /initramfs-linux.img
      options: root=UUID=a322f882-4104-4677-93de-4eabdd284da3 rw

        title: Reboot Into Firmware Interface
           id: auto-reboot-to-firmware-setup
       source: /sys/firmware/efi/efivars/LoaderEntries-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
```

### Rootパスワードの設定

Rootパスワードの設定を行います。

```sh
# passwd
```

これで一連の設定は完了です。 `exit` コマンドでchroot環境から抜けます。

```sh
# exit
$
```

### マウントを解除する

最後に各ファイルシステムのマウントを解除します。

```sh
$ sudo umount /mnt/boot
$ sudo umount /mnt
$ mount | grep /mnt | wc -l
0
```

## インストールしたArch Linuxを起動する

```sh
$ sudo qemu-system-x86_64 \
      -bios /usr/share/edk2-ovmf/x64/OVMF.fd \
      -m 512M \
      -boot order=c \
      -hdd /dev/loop0
```

## 上手く起動できなかった場合

ここまで全てが上手くいっていた場合はArch Linuxが起動してきますが、何か問題が発生して起動に失敗することもあり、その場合に再度Arch Linuxのインストールをやり直すのは大変です。

ここでループバックデバイスを利用してインストールしていると、ファイルシステムをマウントしなおしてトラブルシューティングと修正作業が容易に行えます。起動に失敗したQEMUを停止した後、以下のコマンドでファイルシステムを再マウントします。

```sh
$ sudo mount /dev/loop0p3 /mnt
$ sudo mount /dev/loop0p1 /mnt/boot
$ sudo arch-chroot /mnt
#
```

良くありがちなミスが、 `/boot/loader/entries/arch.conf` の `options` の記述を間違えている、というものです。
(個人的には、起動できなかった場合の原因は `options` の設定が間違っていることがほとんどでした...)

```sh
$ grep options /boot/loader/entries/arch.conf
options root=UUID=20174e0a-be82-4683-b404-5236d8c3e8de rw
```

間違っている個所を修正したら、chroot環境を抜けたあとにファイルシステムをアンマウントします。
マウントしたままだとQEMUから `/dev/loop0` を参照した際にファイルシステムの変更内容が反映されていないことがあるため、面倒でも都度アンマウントするのが良いかもしれません。

```sh
# exit
$ sudo umount /mnt/boot
$ sudo umount /mnt
$ mount | grep /mnt | wc -l
```

## まとめ

ループバックデバイスと組み合わせた、Arch Linuxのインストール手順とトラブルが出た際の調査・修正方法を紹介しました。
ファイルシステムを参照して原因調査を行えるという点では、インストール手順を振り返りながら修正できるため、Arch Linuxの硬派なインストール手順という利点(？)をより活かせそうです。