Ubuntuに「リカバリー」システムを作ってみる
どのOSでも、時々システム内部からでは解決できない問題が発生するのは避けられません。何らかの変更が原因でシステムが起動しなくなった時の修復や、システムパーティションの調整をしたい時などです。Linuxデスクトップを使い、自分でPCをいじるのが好きな人にとっては、なおさらでしょう。そんな時、メインOSから独立したOSがあると助かります。
デュアルブートやLiveUSBなど、既存の方法はたくさんありますが、それぞれに問題や限界があります。この問題に関しては、私はmacOSの「リカバリー」システムの仕組みがとても気に入っています。同じハードドライブにインストールされていながらメインシステムから独立した読み取り専用のシステムで、一連のツールが付属しており、起動時に切り替えることができます。そこで、私は自分のUbuntuデスクトップにも、このような「リカバリー」システムを、よりカスタマイズしやすく、より使いやすい形で自作してみることにしました。
課題
Linuxデスクトップをメインシステムとして使っている人にとって、一般的な「リカバリー」システムの選択肢は主に2つあります。
- デュアルブート:元々デュアルブート環境を構築している人なら、別のシステムに再起動するだけで多くの問題を解決できます。
- LiveUSBシステム:多くのディストリビューションのインストールイメージには試用機能が備わっており、多くのツールも含まれています。また、SystemRescueのような専用のメンテナンスシステムも利用できます。
しかし、これらの方法にはそれぞれ問題があります。私の場合、最初の方法の問題点は、デュアルブート環境ではないということです。かなり早い段階からLinuxデスクトップ単独の環境で、Windowsが必要な時は仮想マシンを起動していました。この構成の方がシンプルで便利ですし、デュアルブートで2つのシステムが互いに干渉して問題を引き起こす可能性も避けられます。
LiveUSBも悪くありませんが、いくつかの主な欠点があります。
- 起動速度:USBメモリの速度は非常に遅いです。USB 3.0のUSBメモリでも、公称の読み取り速度は100MB/s程度が多く、速いものでも400MB/s程度でしょう。主流のSSDが最低でも600MB/s、速いものでは数GB/sに達するのと比べると、大きな差があります。
- 利便性:通常、LiveUSBは既存のISOを書き込んで作成されるため、汎用性が重視されており、特定のデバイスや環境向けに最適化されていません。つまり、起動するたびにディスプレイ設定の変更やネットワーク接続の設定など、何らかの調整が必要になる可能性があります。それに加え、snapperやsedutilなど、私が必要とする特定のツールが含まれていないこともあります。
- 互換性:これは特に専用のメンテナンスシステムに当てはまりますが、これらのシステムで使われているカーネルやツールセットのバージョンは比較的古く、使用している環境と完全に互換性があるとは限りません。例えば、Btrfsはカーネルのバージョンごとに新しい機能が導入されるため、もしそれらの機能を使っている場合、メンテナンスシステムのカーネルが新しくないと互換性の問題を引き起こす可能性があります。
私もCubicを使ってカスタムISOを作成し、それをUSBメモリに書き込んで「リカバリー」システムとして使ってみたことがあります。これで利便性と互換性の問題は一部解決しましたが、何かを調整するたびにそのプロセスを繰り返してUSBメモリに書き込むのは非常に面倒でした。しかし、Cubicがワークスペースを作成し、ファイルシステムをその中に残すというアプローチは、私にとって大きなヒントになりました。
多くの資料を参考にした結果、私は自分のPC用に、Ubuntuベースの「リカバリー」システムを一から構築することに決めました。
目標と方針
この「リカバリー」システムに設定した目標は以下の通りです。
- 「リカバリー」システムとして、何よりもまずそれ自体が比較的安定していること。つまり、毎回同じ状態で起動できるべきです。もし誤ってこのシステムを壊してしまっても、再起動するだけで元の状態に戻れるようにしたいです。このため、このシステムはLiveUSBのように、読み取り専用+Overlayファイルシステムという形式を採用すべきです。
- このシステムは、マシンのハードウェアや環境に適合し、起動後すぐに使えること。適切なドライバ(グラフィックドライバなど)がインストールされ、WiFi接続などが設定済みであることを含みます。
- カスタマイズや更新が容易であること。新しいツールが必要になったら簡単に追加でき、中のパッケージも比較的簡単にアップグレードできるようにします。
- USBメモリではなく、ハードドライブに置いて使用できること。特にこのシステムは現在のデバイスに合わせて作るので、USBメモリに入れる意味は大幅に減りますし、ハードドライブに置けばロード速度も向上します。
これらの要件と既存のツールに基づき、私の大まかな計画は次のようになりました。
- 「リカバリー」システムを構築するためのワークスペースを作成し、その中で必要に応じて調整や再構築を行えるようにします。
- 技術面では、Casperを使ってLiveシステムを起動し、EFIから直接起動できる実行ファイルとして、ユニファイドカーネルイメージを作成します。
ワークスペースとスクリプト
まず、新しいBtrfsサブボリューム/recovery
をワークスペースとして作成します。
sudo btrfs subvolume create /recovery
この場所は任意ですが、ほとんどの操作にはroot権限が必要なので、ユーザーディレクトリの外に置くと手間が省けます。独立したサブボリュームを作成する利点は、調整を行うたびに新しいスナップショットを作成して現在の状態を保存できることです。もし後の調整で壊してしまっても、いつでもスナップショットから以前の正常なバージョンに戻すことができ、ファイルシステムレベルのバージョン管理のようなものです。
このワークスペースには、3つのフォルダを置きました。
scripts
:「リカバリー」システムのメンテナンスに使うスクリプトを格納します。詳細は後述します。fs
:「リカバリー」システムが使用するファイルシステムの中身です。files
:繰り返し使用する可能性のあるファイルをいくつか置きます。
ベースファイルシステム
次に、ファイルシステムの中身を初期化する必要があります。公式サイトのミラーからUbuntu Baseのデイリービルド版をダウンロードできます。例えば、私の「リカバリー」システムをUbuntu 24.04 LTSベースにしたい場合、Ubuntu Base 24.04 (Noble Numbat) Daily Buildから対応するアーキテクチャの圧縮ファイル、例えばnoble-base-amd64.tar.gz
をダウンロードし、ベースファイルシステムとして使います。fs
ディレクトリに移動し、その中身を展開します。
cd /recovery/fs
sudo tar xvf path/to/noble-base-amd64.tar.gz
デイリービルド版を使うメリットは、含まれているパッケージがそのメジャーバージョンの中で最新であるため、ダウンロード後にこれらのパッケージを再度アップグレードする必要がないことです。
chrootスクリプト
Ubuntu Baseには最も基本的なシステムしか含まれていないため、当然ながらより多くのパッケージをインストールし、その後も多くの調整を行う必要があります。そのため、この「リカバリー」システムのファイルシステムに「入って」変更を加えるためのスクリプトが必要です。ここでは、そのためのchrootスクリプトstart-chroot.sh
を作成しました。
#!/bin/sh
set -fv
# rootで実行されていることを確認
if [ "$(id -u)" != "0" ]; then
echo "Require root!"
exit 1
fi
cd /recovery
# メインシステムのDNS設定を使用してネットワークを確保
mv fs/etc/resolv.conf fs/etc/resolv.conf.1
cp /etc/resolv.conf fs/etc/resolv.conf
# 必要なシステムマウントポイントをマウント
mount --bind /dev fs/dev
mount none -t proc fs/proc
mount none -t sysfs fs/sys
mount none -t devpts fs/dev/pts
# chrootに入る
chroot fs
# 一時ファイルをクリーンアップ
rm -f fs/var/lib/dbus/machine-id
rm -f fs/root/.bash_history
rm -rf fs/tmp/*
# システムマウントポイントをアンマウント
umount fs/dev/pts
umount fs/dev
umount fs/proc
umount fs/sys
# DNS設定を元に戻す
mv fs/etc/resolv.conf.1 fs/etc/resolv.conf
このスクリプトがあれば、いつでも「リカバリー」システムが使用するファイルシステムに入り、その内容を変更できます。
スクリプト内でresolv.conf
を変更しているのは、このファイル自体がsystemdによって管理されるシンボリックリンクであり、「リカバリー」システム内でこの構造を変更したくないためです。しかし、chrootでこのファイルシステムに入ったとき、このシステム内のsystemdは実行されていないため、resolv.conf
は空になります。これにより、ネットワークを必要とするプログラムがDNS設定なしで正常に動作しなくなるため、一時的にメインシステムのファイルで置き換える必要があります。
ビルドスクリプト
次に必要なのは、ファイルシステムを読み取り専用にパッケージ化し、「リカバリー」システムを起動するために必要なEFI実行ファイルを構築するスクリプトです。以下が私のbuild-image.sh
スクリプトです。
#!/bin/sh
set -efv
# rootで実行されていることを確認
if [ "$(id -u)" != "0" ]; then
echo "Require root!"
exit 1
fi
cd /recovery
# 古いイメージを削除
if [ -L image ]; then
old_image=$(readlink image)
if [ -d "$old_image" ]; then
rm -rf "$old_image"
fi
rm image
fi
# ビルド結果を格納するための一時ディレクトリを作成
tmp_dir=$(mktemp -d)
ln -s "$tmp_dir" image
mkdir image/casper
# ファイルシステムをパッケージ化
mksquashfs fs image/casper/filesystem.squashfs -comp zstd
# カーネルイメージをビルド
ukify build \
--linux=fs/boot/vmlinuz \
--initrd=fs/boot/initrd.img \
--cmdline="boot=casper noprompt libata.allow_tpm=1" \
--output=image/recovery.efi
ここでは、ビルド結果を一時フォルダに入れ、その一時フォルダをimage
サブフォルダにシンボリックリンクしています。これは必須ではありませんが、私がこの方法を選んだ理由は以下の通りです。
- ビルド結果のイメージがBtrfsパーティションに保存され、スナップショットに含まれるのを避けるため。
- 生成されたイメージはシステムシャットダウン後に自動的に削除されるため。
イメージファイルはすでに圧縮された大きなバイナリブロックであり、ファイルシステムに追跡させるメリットは特にありません。それに、このスクリプトを使えばいつでもfs
から新しいイメージを素早くビルドできるので1、古い結果を保持する必要はありません。
ここではukify
を使ってユニファイドカーネルイメージをビルドし、いくつかの簡単なカーネルパラメータを使用しています。
boot=casper
は、Casperを使って読み取り専用のLiveシステムを起動することを示します。その後、パッケージ化されたSquashfsファイルシステムを対応するパーティションのcasper
ディレクトリに置くだけでよくなります。noprompt
はCasperのパラメータで、再起動時にCDやUSBメモリを取り出すよう促すプロンプトを表示しないようにします。私たちは「リカバリー」システムをハードドライブに置く予定なので、取り出すものは何もなく、プロンプトは不要です。libata.allow_tpm=1
は、sedutilがOPAL自己暗号化ドライブを操作する際に必要なカーネルパラメータです。sedutilを使用しない場合は、この項目を追加する必要はありません。
スナップショット
前述の通り、ワークスペースを独立したBtrfsサブボリュームに置くことで、スナップショットの作成が容易になります。私はsnapperを使ってスナップショットを管理しており、そのためにまず設定ファイル/etc/snapper/configs/recovery
を作成します。
SUBVOLUME="/recovery"
FSTYPE="btrfs"
QGROUP=""
NUMBER_CLEANUP="yes"
NUMBER_MIN_AGE="1800"
NUMBER_LIMIT="5"
NUMBER_LIMIT_IMPORTANT="1"
その後、最初のスナップショットを作成します。
sudo snapper -c recovery create
将来的にも同じコマンドでさらにスナップショットを作成できます。
システムの設定
ワークスペースを作成し、対応するスクリプトを配置したら、「リカバリー」システムを実際に使えるように設定する必要があります。
このセクションのコマンドは、特に断りのない限り、start-chroot.sh
によって作成されたchroot環境内で実行します。
基本パッケージのインストール
まず、ベースシステムをインストールします。
apt update
apt install linux-generic
apt install --no-install-recommends ubuntu-minimal
apt install casper discover laptop-detect os-prober
apt install ubuntu-desktop
Casperはデフォルトで起動時にMD5チェックを行いますが、これは私たちにとってあまり意味がないので無効にします。
systemctl disable casper-md5check.service
次に、「リカバリー」システムではあまり必要ないと思われるパッケージをいくつかクリーンアップします。
apt autoremove --purge \
snapd \
rhythmbox \
libreoffice-common \
totem \
gnome-calendar \
gnome-clocks \
gnome-characters \
gnome-startup-applications \
gnome-online-accounts \
transmission-gtk \
cloud-init \
unattended-upgrades \
firefox \
thunderbird \
ubuntu-docs \
ubuntu-report
ここに挙げたのは私が削除したパッケージで、主にメディア再生、オフィス文書編集、システムアップグレードなど、「リカバリー」システムではあまり使わないと思われるものです。おそらくもっと多くのパッケージをアンインストールしても「リカバリー」システムとしての機能に影響はないでしょうが、まだ詳しく調べていません。snapもアンインストールしました。snapベースのソフトウェアで必要なのはFirefoxだけですが、私はMozilla公式リポジトリのバージョンを好むからです。これについては後のセクションで詳しく説明します。
その他、エディタ、よく使うコマンドラインツール、メンテナンス用ツールなど、必要なツールをインストールします。
apt install vim-gtk3 ripgrep curl bash-completion
apt install gparted mtools dmraid \
efibootmgr btrfs-progs \
nvme-cli smartmontools \
cryptsetup lvm2 \
snapper-gui \
systemd-ukify systemd-boot-efi
具体的なツールはもちろん、個人の使用習慣や実際のニーズに合わせて調整してください。例えば、ソフトウェア暗号化パーティションがなければcryptsetup
は不要かもしれませんし、Emacsを常用しているならVIMは不要かもしれません。
APTリポジトリにないツールがあれば、自分でfs
ディレクトリにコピーしてもよいです。例えば、私はsedutilが必要なので、そのリリースぺージからダウンロードし、バイナリファイルsedutil-cli
を/usr/sbin
にコピーして実行権限を付与しました。
Firefoxのインストール
上記のステップでFirefoxを削除しましたが、「リカバリー」システムにはブラウザが必要です。今の時代、PCが使えなくてもスマートフォンで情報を調べることはできますが、やはりPCで直接調べられる方が便利です。
ここでは、Mozillaの公式ドキュメントInstall Firefox on Linuxに従ってインストールします。
# 署名キーをインストール
install -d -m 0755 /etc/apt/keyrings
wget -q https://packages.mozilla.org/apt/repo-signing-key.gpg -O- | \
tee /etc/apt/keyrings/packages.mozilla.org.asc > /dev/null
# APTリポジトリを追加
cat <<EOF | tee /etc/apt/sources.list.d/mozilla.sources
Types: deb
URIs: https://packages.mozilla.org/apt
Suites: mozilla
Components: main
Signed-By: /etc/apt/keyrings/packages.mozilla.org.asc
EOF
# 優先度を設定
cat <<EOF | tee /etc/apt/preferences.d/mozilla
Package: *
Pin: origin packages.mozilla.org
Pin-Priority: 1000
EOF
# パッケージをインストール
apt update
apt install firefox-esr
「リカバリー」システムは頻繁に更新しないので、サポート期間が長く安定しているESR版を選びましたが、これはそれほど重要なことではありません。
Firefoxには、自動更新チェック、Firefoxアカウント、プロファイルインポートなど、「リカバリー」システムには不要な機能が多くあります。また、Casperが作成する新規ユーザーに対して、「リカバリー」システムでは意味のない多くの通知が表示されます。これらはFirefoxのポリシーファイルを使ってすべて無効にできます。ここでは、ワークスペースにfiles/firefox-policies.json
を追加しました。
{
"policies": {
"DisableAppUpdate": true,
"DisableFirefoxAccounts": true,
"DisableFirefoxStudies": true,
"DisablePocket": true,
"DisableProfileImport": true,
"DisableProfileRefresh": true,
"DisableSystemAddonUpdate": true,
"DontCheckDefaultBrowser": true,
"DisplayBookmarksToolbar": "never",
"NoDefaultBookmarks": true,
"OfferToSaveLogins": false,
"OverrideFirstRunPage": "",
"UserMessaging": {
"ExtensionRecommendations": false,
"FeatureRecommendations": false,
"SkipOnboarding": true,
"MoreFromMozilla": false,
"FirefoxLabs": false
}
}
}
具体的なオプションはポリシーファイルのドキュメントを参照してください。多くのポリシーが選択可能です。
このポリシーファイルを「リカバリー」システム内のFirefoxに適用するには、chrootの外で以下を実行する必要があります。
mkdir -p fs/etc/firefox/policies
ln files/firefox-policies.json \
fs/etc/firefox/policies/policies.json
これにより、ワークスペースのfiles
とファイルシステム内の対応するファイルがリンクされます。これにより、外部から直接変更できるだけでなく、「リカバリー」システムを最初から作り直す必要がある場合でも、これらの設定ファイルを直接再利用できます。
WiFiの設定
「リカバリー」システムでネットワークが使えることは非常に重要です。情報を調べられるだけでなく、必要であればインストールしていないツールをダウンロードすることもできます。WiFiでインターネットに接続する場合、WiFiのパスワードも「リカバリー」システムに設定しておく必要があります。
nmcli --offline connection add type wifi \
autoconnect yes \
ssid "<SSID>" \
wifi-sec.key-mgmt wpa-psk \
wifi-sec.psk "<password>" \
> /etc/NetworkManager/system-connections/wifi.nmconnection
chmod 0600 /etc/NetworkManager/system-connections/wifi.nmconnection
ここの<SSID>
と<password>
を実際のWiFi設定に置き換えれば、「リカバリー」システムを起動すると自動的に指定のWiFiに接続され、追加の操作は不要になります。
ディスプレイの設定
システムがディスプレイ設定を正しく検出できないことがあります。特に、ディスプレイを回転させていたり、異なるスケーリング比率を使用したい場合です。
その場合、~/.config/monitors.xml
をfs/root/monitors.xml
にコピーし、Casperのinitramfsスクリプトをfs/usr/share/initramfs-tools/scripts/casper-bottom/99gnome_monitors
に作成します。
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
# get pre-requisites
prereqs)
prereqs
exit 0
;;
esac
chroot /root install \
-o $USERNAME -g $USERNAME \
/root/monitors.xml \
/home/$USERNAME/.config/monitors.xml
そして、以下を実行します。
chmod +x /usr/share/initramfs-tools/scripts/casper-bottom/99gnome_monitors
update-initramfs -u
これにより、このディスプレイ設定ファイルは起動時に自動的に一時ユーザーの.config
にインストールされ、ディスプレイ設定が正しく適用されるようになります。
GNOMEデスクトップ設定の上書き
Ubuntuのドックにデフォルトで表示されるアプリケーションは、私たちが望むものではないかもしれませんが、これも変更可能です。
先ほどのFirefoxのポリシーファイルと同様に、まず設定ファイルfiles/gnome-settings.schema.override
を作成します。
[org.gnome.shell]
favorite-apps = [ 'org.gnome.Nautilus.desktop', 'firefox-esr.desktop', 'org.gnome.Terminal.desktop', 'gparted.desktop' ]
[org.gnome.shell:ubuntu]
favorite-apps = [ 'org.gnome.Nautilus.desktop', 'firefox-esr.desktop', 'org.gnome.Terminal.desktop', 'gparted.desktop' ]
次に、chrootの外で以下を実行します。
ln files/gnome-settings.schema.override \
fs/usr/share/glib-2.0/schemas/99_settings.gschema.override
これで設定ファイルがファイルシステムにリンクされます。最後に、chroot内で以下を実行します。
cd /usr/share/glib-2.0/schemas
rm gschemas.compiled
glib-compile-schemas .
これにより、GNOMEのデータベースの情報が正しく更新されます。
ここではファイルブラウザ、ブラウザ、ターミナル、GPartedを配置しましたが、具体的にどのように配置するかは、個人のニーズに合わせて調整しても構いません。なぜここで:ubuntu
を含む設定を2回書く必要があるのかは、私もはっきりとはわかりません。しかし、これらのデータが正しいかどうかは、次のコマンドで確認できます。
gsettings --schemadir . get org.gnome.shell favorite-apps
ビルドとデプロイ
システムの構成が完了したら、先ほど述べたビルドスクリプトを使って「リカバリー」システムをビルドします。
sudo ./scripts/build-image.sh
パッケージ化が完了するのを待つと、ワークスペースのimage
フォルダ内にrecovery.efi
とcasper/filesystem.squashfs
という2つのファイルが見つかります。あとは、EFIがこのファイルを使えるようにするだけです。
私はこれらを直接EFIシステムパーティションにコピーしました2が、一般的にはこのパーティションは200MB程度で十分だとされているようです。しかし、Ubuntuベースの「リカバリー」システムは簡単に1GB以上のサイズになります。もしEFIシステムパーティションに十分な空き容量がない場合は、FAT32パーティションを新たに作成して保存する方法もあります。注意点として、filesystem.squashfs
はパーティションのルートにあるcasper
サブディレクトリに置く必要がありますが、recovery.efi
はどこに置いても構いません。
最後に、EFIが「リカバリー」システムを見つけられるようにすれば完了です。recovery.efi
が/dev/nvmeXnYpZ
パーティションのルートディレクトリにあると仮定して、以下を実行します。
sudo efibootmgr --create \
--disk /dev/nvmeXnY --part Z \
--label "Ubuntu Recovery" \
--loader recovery.efi
完了後、efibootmgr
でこの「リカバリー」システムの起動番号を確認し、
sudo efibootmgr --bootnext XXXX
を使って次回の起動時に「リカバリー」システムに入ることができます。また、BIOSによってはインターフェースからEFIの起動エントリを選択して起動することもできます。
再起動してテストし、問題がなければ、snapperでワークスペースの現在の状態の新しいスナップショットを作成し、作業内容を保存します。
参考資料
- 从零开始制作 Ubuntu 22.04 Live CD - narukeu(中国語)
- Minimal Ubuntu Install - Northwestern MSR Hackathon(英語)
- LiveCD Customization - Ubuntu Community Help Wiki(英語)
- Unified kernel image - ArchWiki(英語)