Raspberry Pi 5 + Ubuntu 26.04 が再起動後にSSHできない — 犯人はA/Bブート(piboot-try)と自動カーネル更新だった
TL;DR
- Raspberry Pi 5 / Ubuntu 26.04 のノードが、再起動後に pingは通るのにSSHが
Connection refused、しかもIPが固定IPではなくDHCPアドレスになる - 原因は
unattended-upgradesが入れた新カーネルが起動に失敗し、A/Bブート機構(piboot-try)のお試し起動で固まっていた - お試し起動は本来のルートをマウントしないので journaldにログが1行も残らない。これが原因究明を難しくする
- 対処は「失敗するカーネルをpurge」+「自動更新を止める」+「apt-mark hold」
環境
| 項目 | 内容 |
|---|---|
| ハード | Raspberry Pi 5 8GB ×2 |
| OS | Ubuntu 26.04 LTS (resolute) |
| ストレージ | USB SSD ブート (BUFFALO SSD-PUTU3C) |
| 用途 | Kubernetes クラスタのワーカーノード |
| ネットワーク | netplanで固定IP (192.168.2.50) |
症状
再起動後、ノードが以下の状態になる。
$ ping 192.168.2.146
64 bytes from 192.168.2.146: icmp_seq=1 ttl=64 time=0.359 ms # ← 応答する
$ ssh osushi@192.168.2.146
ssh: connect to host 192.168.2.146 port 22: Connection refused # ← 拒否される
ポートスキャンをかけると、全ポートが closed(RSTを返す)。
$ sudo nmap -Pn 192.168.2.146
Host is up (0.00025s latency).
Not shown: 1000 closed tcp ports (reset)
TCPスタックは生きているのに、待ち受けているサービスが1つもない。
さらに奇妙なのがIPアドレス。netplanには dhcp4: false で固定IP 192.168.2.50 を書いてあるのに、実機はDHCPレンジの 192.168.2.146 を持っている。
しばらく放置するか電源を入れ直すと、何事もなかったかのように 192.168.2.50 で正常起動する。
外した仮説たち
先に供養しておく。どれも「それらしい」が全部ハズレだった。
| 仮説 | 否定した根拠 |
|---|---|
| cloud-initがnetplanを上書きしている |
99-disable-network-config.cfg は存在し、netplanも静的設定のままだった |
| microSDが挿さっていて別のrootから起動している |
lsblk に mmcblk0 なし。rootは /dev/sda2 一択 |
| 起動が遅いだけ(apt-dailyが重い) | 5分間監視しても .146 のまま、SSHは一度も開かず |
| dracutのinitramfsにUSBストレージモジュールが無い | 新旧どちらの initrd.img にも uas / xhci が入っていた |
そして肝心の journalctl -b -1 には、失敗した起動の記録が1行も残っていない。ここで手詰まりになった。
答えはMOTDに書いてあった
正常起動したときのログインメッセージに、こう出ていた。
piboot-try:
New boot assets in /boot/firmware/new failed. Fallen back to known good
state. Use piboot-try --reset-new to re-test boot assets.
これが全てだった。
A/Bブート機構(piboot-try)とは
Ubuntu 25.10 以降、Raspberry Pi版にはA/Bブートのフォールバック機構が入っている。カーネル・initramfs・デバイスツリーといった起動アセットが更新されると、こう動く。
- 新しいアセットが
/boot/firmware/new/に置かれ、stateはunknown - 次の起動時に
piboot-try-rebootサービスがそれを検知し、stateをtryingにして即座にtrybootモードで再起動 - tryboot起動が成功すれば
current/に昇格、失敗すればstateをbadにして既知の正常アセットへフォールバック
つまり 新カーネルを一度お試し起動する 仕組み。更新後に「2回起動する」のはこのため。
症状が説明できる
お試し起動が失敗し、dracutのinitramfs内で止まっていた。失敗ブートのログにこの行があった。
eth0: Configuring with /run/systemd/network/zzzz-dracut-default.network
dracutのデフォルトネットワーク設定は DHCP。だから .146 を拾う。そしてinitramfsにsshdは存在しないので Connection refused。ARPやICMPに応答するのはカーネルが生きているから。
ログが残らない理由も同じで、本来のルートをマウントできていないので /var/log/journal に何も書けない。探しても見つからないのは当然だった。
cmdline.txt に panic=10 が入っているため、busyboxシェルに落ちずにリセットがかかり、最終的にフォールバックする。
犯人の特定
/boot/firmware/new/ のタイムスタンプと apt の履歴が一致した。
$ ls -la /boot/firmware/new/
-rwxr-xr-x 1 root root 14749965 Aug 24 21:31 vmlinuz
$ cat /boot/firmware/new/state
bad
$ grep -B2 -A6 'linux-image' /var/log/apt/history.log
Start-Date: 2026-08-24 21:30:57
Commandline: /usr/bin/unattended-upgrade
Install: linux-image-7.0.0-1017-raspi:arm64 (7.0.0-1017.17, automatic)
Upgrade: linux-image-raspi:arm64 (7.0.0-1016.16, 7.0.0-1017.17)
End-Date: 2026-08-24 21:31:34
21:30に unattended-upgrades がカーネル 7.0.0-1017-raspi を勝手に入れ、そのお試し起動が失敗していた。
対処
1. 失敗するカーネルを削除
sudo apt purge -y linux-image-7.0.0-1017-raspi
purgeすると flash-kernel が走り、new/ の中身が動作実績のある旧カーネルで作り直される。
flash-kernel: Flashing the remaining highest-versioned kernel (7.0.0-1016-raspi).
Copying kernel assets to /boot/firmware/new/
Please be aware next reboot will boot twice
2. 自動更新を止める
sudo systemctl disable --now unattended-upgrades apt-daily.timer apt-daily-upgrade.timer
3. カーネル関連をhold
sudo apt-mark hold linux-image-raspi flash-kernel
4. 再起動して確認
2回起動するので3〜5分待つこと。
$ uname -r
7.0.0-1016-raspi
$ cat /boot/firmware/current/state
good
$ ls /boot/firmware/ | grep new
# ← 何も出なければA/B機構が正常化した合図
落ち穂拾い:1017が悪いとは限らない
同じPi 5 8GB、同じUbuntu 26.04、同じ型番のUSB SSDを使ったもう1台は、カーネル 7.0.0-1017-raspi で何度再起動しても正常に動いている。
つまり「1017が壊れている」という単純な話ではない。残る差分は、問題が出た側にはUSBシリアル変換ケーブル(PL2303)が挿さっていたことくらい。USBデバイスの列挙順序が変わって初期化タイミングがずれた可能性はあるが、ここは推測の域を出ない。
原因を確定させるなら、シリアルケーブルを抜いた状態で1017を入れ直す、という単一変数の実験になる。
なお、K8sはノードごとにカーネルバージョンが違っても問題なく動くので、片方1016・片方1017のまま運用している。
教訓
Kubernetesノードでは自動アップグレードを切る。 教科書によく書いてあるが、実際に踏むと重みが違う。クラスタ稼働中にこれが起きていたら「ノードが1台勝手にNotReady、SSHも入れない、ログも残っていない」という状況になっていた。
sudo systemctl disable --now unattended-upgrades apt-daily.timer apt-daily-upgrade.timer
sudo apt-mark hold kubelet kubeadm kubectl linux-image-raspi flash-kernel
そして、推測より証跡。 5回仮説を外したあと、答えを出したのはMOTDの1行とaptの履歴だった。ログイン時のメッセージは意外と読み飛ばしがちなので、異常時はまず全部読むべきだと反省した。
調査に使ったコマンドまとめ
# A/Bブートの状態確認
cat /boot/firmware/current/state
cat /boot/firmware/new/state
ls -la /boot/firmware/current/ /boot/firmware/new/
# カーネル更新の履歴
grep -B2 -A6 'linux-image' /var/log/apt/history.log
# 前回起動のログ(失敗ブートでは残らない点に注意)
journalctl --list-boots
journalctl -b -1 -p err --no-pager
# initramfsの中身確認
sudo lsinitrd /boot/firmware/new/initrd.img | grep -E 'uas|xhci'
# ネットワーク側からの生死確認
sudo arp-scan --interface=eth0 --localnet
sudo nmap -Pn <IP>