Удалённая переустановка Raspberry Pi OS по ssh
На habr’е уже была публикация про удаленную переустановку Linux по ssh. Воистину, так гланды ещё никто не удалял… Я подивился находчивости и целеустремлённости автора, и искренне пожелал себе, чтобы материал никогда не пригодился на практике.
Но внезапно мне понадобилось переустановить Raspberry Pi OS на microSD-карте, установленной в Raspberry Pi 4. При этом доставать microSD-карту очень не хотелось, дело в том, что плата Raspberry Pi установлена внутрь корпуса, который не позволяет достать microSD-карту без разборки.
Памятуя, что это не единственная моя плата Raspberry Pi и это не последний раз, когда придётся полностью переустанавливать Raspberry Pi OS, я решил, что лучше полчаса потерять, потом за пять минут обновить.
Сразу оговорюсь, что приведённая ниже инструкция не универсальна. Я буду рассматривать очень простой случай:
есть небольшая локальная сеть IPv4 с DHCP-сервером и выходом в Интернет;
имеется плата Raspberry Pi (далее rpi), подключённая к локальной сети по Ethernet, на которой уже установлена и функционирует Raspberry Pi OS не слишком старой версии;
в локальной сети есть компьютер (далее host), с которого мы можем подключаться к rpi по ssh (на host установлены wget, openssh-client и xz);
rpi может получать IP-адрес по DHCP; мы умеем найти rpi в локальной сети (хотя бы по MAC-адресу).
Вот стартовав в таких тепличных условиях, попробуем полностью обновить содержимое microSD-карты в rpi, перезагрузиться и снова подключиться к rpi по ssh.
Не нагуглив готового решения для Raspberry Pi OS, я обратился к посту Удаленная переустановка Linux по ssh без доступа к консоли. Ключевым моментом изложенной там методики является возможность на ходу логически «отключить» накопитель, с которого работает «старая» ОС, а затем уже переподключить его, но так, что «старая» ОС будет его считать другим накопителем и не будет противиться его полной перезаписи.
Увы, то, что дозволено делать со SCSI-диском не дозволено делать с microSD-картой, по крайней мере мне подобные возможности найти не удалось.
Я рассмотрел другие возможности:
порвать с
/dev/mmcblk0
при помощиpivot_root
;перезагрузиться, так, чтобы совсем не задействовать
/dev/mmcblk0
для /: использовать nfsroot или nbd для rootfs.
Эти решения сопряжены с дополнительными хлопотами, которых хотелось бы избежать.
И почти готовое решение нашлось: Alpine Linux.
Alpine Linux это дистрибутив, который требует очень скромных ресурсов накопителя и ОЗУ так, что оказывается возможным, загрузившись с microSD-карты, развернуть корневую файловую систему только в ОЗУ.
Будем действовать так:
загрузим на rpi Alpine Linux в специальной конфигурации, в которой производится автоматическая настройка сетевого интерфейса Ethernet по DHCP и обеспечивается запуск ssh-сервера с беспарольным доступом;
из Alpine Linux запишем на microSD-карту базовый образ Raspberry Pi OS и проведём его минимальную настройку;
перезагрузимся в Raspberry Pi OS.
Развёртывание Alpine Linux
Подключаемся по ssh к root@rpi, где работает Raspberry Pi OS, и развёртываем там Alpine Linux.
Для Raspberry Pi OS microSD-карта размечается так, что карте создаётся два раздела:
/dev/mmcblk0p1
--- небольшой раздел с файловой системой FAT, где находятся загрузчик и всё, что надо для запуска ядра ОС;/dev/mmcblk0p2
--- раздел с файловой системой EXT4; этот раздел содержит корневую файловую систему ОС.
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
mmcblk0 179:0 0 59.7G 0 disk
├─mmcblk0p1 179:1 0 512M 0 part /boot/firmware
└─mmcblk0p2 179:2 0 59.2G 0 part /
ВНИМАНИЕ! Не забудьте сохранить ценные данные с microSD-карты, если таковые там есть, через несколько минут они отправятся в страну вечной охоты!
Будем развёртывать Alpine в разделе загрузчика, то есть в /boot/firmware
:
root@raspberrypi:~# cd /boot/firmware/
root@raspberrypi:/boot/firmware# wget -O - https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/aarch64/alpine-rpi-3.18.5-aarch64.tar.gz | tar fxz -
root@raspberrypi:/boot/firmware# wget https://raw.githubusercontent.com/macmpi/alpine-linux-headless-bootstrap/f35a86687534306895ea147953730f56af4cff37/headless.apkovl.tar.gz
Скачанный файл headless.apkovl.tar.gz
как раз и обеспечивает настройку сетевого интерфейса rpi при старте и запуск ssh-сервера. Подробности тут.
Итак, microSD-карта настроена так, что при следующей загрузке на rpi запустится Alpine Linux.
Перезагружаемся:
root@raspberrypi:/boot/firmware# reboot
Развёртывание и настройка Raspberry Pi OS
Немного подождём и попробуем подключиться к rpi с работающим Alpine Linux по ssh.
Это можно сделать так:
host$ ssh -x -o userknownhostsfile=/dev/null -o stricthostkeychecking=no root@rpi
Дополнительные опции нужны, чтобы клиент ssh не смущался новому, неведомому серверу, жить которому только несколько минут.
Чтобы не набирать такую длинную команду несколько раз, закинем её в переменную:
host$ RPISSH="ssh -o userknownhostsfile=/dev/null -o stricthostkeychecking=no root@rpi"
Освобождаем mmcblk0:
host$ $RPISSH umount /.modloop/
host$ $RPISSH umount /media/mmcblk0p1/
Теперь можно накатывать образ Raspberry Pi OS. Чтобы не возиться с установкой на Alpine Linux xz-utils, будем запускать xz на host:
host$ wget -O - https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-06/2023-12-05-raspios-bookworm-arm64-lite.img.xz | xz -cd | $RPISSH dd bs=8M of=/dev/mmcblk0
С некоторых пор нельзя просто так взять и подключиться к Raspberry Pi OS по ssh, см. https://www.raspberrypi.com/news/raspberry-pi-bullseye-update-april-2022/, необходима небольшая начальная настройка.
Начнём с того, что разрешим запуск ssh-сервера при старте:
host$ $RPISSH mount /dev/mmcblk0p1 /mnt
host$ $RPISSH "echo > /mnt/ssh"
Опционально можно включить использование последовательного порта в качестве консоли:
host$ $RPISSH "echo enable_uart=1 >> /mnt/config.txt"
Работы с разделом загрузчика завершены, этот раздел можно отключить:
host$ $RPISSH umount /mnt
В современной Raspberry Pi OS по умолчанию ни пользователю root, ни пользователю pi нельзя подключиться по ssh. Поэтому подложим пользователю root готовый открытый ключ, чтобы в дальнейшем можно было подключиться к root@rpi без пароля:
host$ $RPISSH mount /dev/mmcblk0p2 /mnt
host$ $RPISSH "cat > /mnt/root/.ssh/authorized_keys" < ~/.ssh/id_rsa.pub
host$ $RPISSH umount /mnt
Подробнее про ключи openssh можно почитать тут.
Перезагрузимся в Raspberry Pi OS:
host$ $RPISSH reboot
Подождём не более пары минут и voilà:
host$ $RPISSH uname -a
...
Linux raspberrypi 6.1.0-rpi7-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.63-1+rpt1 (2023-11-24) aarch64 GNU/Linux
Эпилог
Инструкция несколько раз проверена на Raspberry Pi 4 Model B. Для других плат понадобятся внести незначительные корректировки, но, полагаю, вдумчивый читатель догадается где надо заменить aarch64/arm64 на armhf.