Raspberry-Pi без насилования SD-карты
Как узнал про overlay filesystem — и спешу поделиться с вами
Или история о том, как я перестал хоронить SD-карты каждый год
Фабула
Перепробовав кучу одноплатников — Orange Pi, Banana Pi, несколько Raspberry Pi — я каждый раз упирался в одну и ту же проблему: microSD-карта дохнет меньше чем за год. Полгода-год, и игрушка молча умирает: то SSH не пускает, то читает-пишет через раз.
И как-то всё руки не доходили заняться проблемой. Пока недавно не вылетела очередная SD-карта. Сегодня я узнал про overlay-filesystem — и спешу поделиться находкой.
Опытные линуксоиды, конечно, улыбнутся, но этот опыт я хочу запечатлеть прежде всего для себя — а вдруг ещё кому пригодится. Также, стараюсь описать все максимально подробно - для совсем новичков. Статья, конечно, на 70% состоит из возни с разделами файловой системы, про overlay тут только в самом конце, но это может тоже кому то пригодиться.
Цели, которую я преследую
Операционка в read‑only — корневая файловая система монтируется только на чтение. Ничего не пишется на SD-карту без явного на то желания пользователя.
Всё временное — в оперативку. Логи, настройки, эксперименты, содержимое /var и /tmp — живут в памяти и бесследно исчезают после выключения. Никакого мусора.
Откат при перезагрузке. После ребута система как новенькая — никаких хвостов от случайно установленного пакета или кривого конфига.
Простота доработки. Поставить пакет, поправить конфиг, обновиться — без плясок с монтированием. Как это сделать — расскажу ниже.
Отдельный rw‑раздел для данных — «на всякий случай». Туда можно спокойно копировать файлы по SSH, не думая о том, поместятся ли они в оперативку. Этот раздел будет переживать перезагрузки.
0. Мой сетап
Raspberry Pi Zero W
Raspberry Pi OS Lite
SD-карточка на 32Gb (8Gb на операционку, остальное - в RW разделе)
Ubuntu server 26.04 на виртуальной машине VirtualBox (просто уже стояла, подойдет любой современный дистрибутив)
1. Устанавливаем операционку
Думаю, что тут справится любой, кто имел хоть какое-то знакомство с Raspberry Pi. Скачиваем Raspberry Pi imager, вставляем SD-карту в компьютер, выбираем нашу версию платы, настраиваем, ждем, пока образ запишется на карту.
После этого вставляем SD-карточку в Raspberry Pi, дожидаемся загрузки (логина по ssh или появления десктопа), выключаем, вытаскиваем карточку. Тут можно, конечно, сделать сразу все необходимые манипуляции с операционкой - установить пакеты, настроить, но мы можем это сделать и потом.
2. Работа с разделами
Грузим виртуалку, выводим список доступных /dev/sd*, подключаем SD-карточку, находим искомое устройство:
$ ls -la /dev/sd*
brw-rw---- 1 root disk 8, 0 May 9 18:48 /dev/sda
brw-rw---- 1 root disk 8, 1 May 9 18:48 /dev/sda1
brw-rw---- 1 root disk 8, 2 May 9 18:48 /dev/sda2
<подключаем SD-карту>
$ ls -la /dev/sd*
brw-rw---- 1 root disk 8, 0 May 9 18:48 /dev/sda
brw-rw---- 1 root disk 8, 1 May 9 18:48 /dev/sda1
brw-rw---- 1 root disk 8, 2 May 9 18:48 /dev/sda2
brw-rw---- 1 root disk 8, 16 May 9 18:49 /dev/sdb
brw-rw---- 1 root disk 8, 17 May 9 18:49 /dev/sdb1
brw-rw---- 1 root disk 8, 18 May 9 18:49 /dev/sdb2
Paspberry Pi при записи образа создает два раздела - один для удобной настройки (файловая система FAT), другой - непосредственно системный (ext4). Их мы и видим в списке: /dev/sdb1 и /dev/sdb2.
Вероятнее всего, нам нужен второй раздел, но на всякий случай перепроверим: подмонтируем его, проверим размер и отмонтируем:
# sudo mount /dev/sdb2 /mnt
$ df -h /mnt
Filesystem Size Used Avail Use% Mounted on
/dev/sdb2 29G 2.3G 25G 9% /mnt
# sudo umount /dev/sdb2
Видим, что общий размер 29Gb, занято системой ~2.3Gb. Оно.
Правильный порядок действий при изменении размера (особенно уменьшении) выглядит так:
Проверилиr файловую систему
Уменьшили саму файловую систему
Уменьшили раздел так, чтобы он совпал с новым размером ФС
Начнем с проверки файловой системы. Запускаем и ждём завершения:
# sudo e2fsck -f /dev/sdb2
e2fsck 1.47.2 (1-Jan-2025)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
rootfs: 74119/1847360 files (0.2% non-contiguous), 715247/7506944 blocks
После завершения - задаем новой размер файловой системы в 8Gb:
# sudo resize2fs /dev/sdb2 8G
resize2fs 1.47.2 (1-Jan-2025)
Resizing the filesystem on /dev/sdb2 to 2097152 (4k) blocks.
The filesystem on /dev/sdb2 is now 2097152 (4k) blocks long.
Наша файловая система занимает 2097152 блоков размером 4К (или 4096 байт). Эти числа нам пригодятся в будущем. Теперь нужно разделить диск на 2 части - одну оставить для системы, вторую - для нашего RW раздела. Итак, запускаем fdisk на устройстве /dev/sdb
#sudo fdisk /dev/sdb
Welcome to fdisk (util-linux 2.41.3).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): __
Утилита запускается, и просит ввести команду. Сначала перепроверим разделы и узнаем их номера. Вводим команду p (печать разделов):
Command (m for help): p
Disk /dev/sdb: 29.14 GiB, 31293702144 bytes, 61120512 sectors
Disk model: STORAGE DEVICE
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x9298fc3d
Device Boot Start End Sectors Size Id Type
/dev/sdb1 16384 1064959 1048576 512M c W95 FAT32 (LBA)
/dev/sdb2 1064960 61120511 60055552 28.6G 83 Linux
Мы видим boot раздел в FAT32 (для настройки параметров raspberry pi) и linux-раздел с основной файловой системой. Также видим, что размер сектора тут - 512 байт. Это тоже нам пригодится.
Теперь аккуратно удаляем второй раздел:
Command (m for help): d
Partition number (1,2, default 2): 2
Partition 2 has been deleted.
Теперь нужно создать новый раздел нужного размера (8Gb). Для этого нужно знать его начало (в секторах по 512 байт) и его конец (также в секторах по 512 байт).
Начало раздела мы знаем из результата выполнения команды p выше: 1064960. Нужно расчитать конец. Из результата выполнения команды resize2fs нам известно, что наша файловая система занимает 2097152 4096-байтных блоков.
Пересчитаем это в 512-байтные: 2097152 * 4096 / 512 = 16777216.
Добавив к этому значению начало сектора, получим номер конечного сектора:
16777216 + 1064960 = 17842176
Создадим новый системный раздел взамен удалённого ранее командой n, указав, что нужен primary-раздел (p) и номер 2. Введем номер первого сектора 1064960 и последнего 17842176.
Важно - см. последнюю строку!
Не удаляем предыдущую сигнатуру, иначе файловая система будет уничтожена -> вводим N!
Command (m for help): n
Partition type
p primary (1 primary, 0 extended, 3 free)
e extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2): 2
First sector (2048-61120511, default 2048): 1064960
Last sector, +/-sectors or +/-size{K,M,G,T,P} (1064960-61120511, default 61120511): 17842176
Created a new partition 2 of type 'Linux' and of size 8 GiB.
Partition #2 contains a ext4 signature.
Do you want to remove the signature? [Y]es/[N]o: N
Теперь создаем новый раздел - для пользовательских данных (RW) на всю оставшуюся часть флешки. Номер раздела - третий. Начальный сектор будет (конечный-для-раздела-2 + 1), то есть 17842176 + 1 = 17842177, а конечный - пустой. fdisk автоматически подставит последний доступный сектор:
Command (m for help): n
Partition type
p primary (2 primary, 0 extended, 2 free)
e extended (container for logical partitions)
Select (default p): p
Partition number (3,4, default 3): 3
First sector (2048-61120511, default 2048): 17842177
Last sector, +/-sectors or +/-size{K,M,G,T,P} (17842177-61120511, default 61120511):
Created a new partition 3 of type 'Linux' and of size 20.6 GiB.
Все, что мы делали выше - пока не записалось на флешку и эти изменения только запланированы. Перед записью изменений еще раз все перепроверим, введя команду p снова:
Command (m for help): p
Disk /dev/sdb: 29.14 GiB, 31293702144 bytes, 61120512 sectors
Disk model: STORAGE DEVICE
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x9298fc3d
Device Boot Start End Sectors Size Id Type
/dev/sdb1 16384 1064959 1048576 512M c W95 FAT32 (LBA)
/dev/sdb2 1064960 17842176 16777217 8G 83 Linux
/dev/sdb3 17842177 61120511 43278335 20.6G 83 Linux
Все верно. boot раздел на месте, у системного начальный сектор остался тем же, размр изменился. Появился новый третий раздел на остаток. Запишем изменения, введя команду w:
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
Готово.
Теперь создадим файловую систему на новом разделе командой mkfs.ext4:
# sudo mkfs.ext4 /dev/sdb3
mke2fs 1.47.2 (1-Jan-2025)
Creating filesystem with 5409791 4k blocks and 1354560 inodes
Filesystem UUID: 378e7266-5fa3-4591-a2e5-5b5732bb047d
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
Извлекаем готовую флешку, выключаем виртуалку, она больше не понадобиться:
# sudo eject /dev/sdb
# sudo shutdown
Вставляем флешку в Raspberry Pi, включаем, логинимся.
3. Финалим всё на Raspberry Pi
Здесь я хочу сделать следующее: примонтировать user-раздел, сделать автоматическое монтирование при загрузке, включить overlay-режим файловой системы и сделать автоматическую перезагрузку устройства, дабы чистить память. Поехали!
3.1 Монтируем новый раздел
В Raspberry Pi разделы флешки называются иначе, вот они:
$ ls -la /dev/mmcblk0*
brw-rw---- 1 root disk 179, 0 May 8 22:04 /dev/mmcblk0
brw-rw---- 1 root disk 179, 1 May 8 22:05 /dev/mmcblk0p1
brw-rw---- 1 root disk 179, 2 May 8 22:04 /dev/mmcblk0p2
brw-rw---- 1 root disk 179, 3 May 8 22:04 /dev/mmcblk0p3
/dev/mmcblk0 - устройство
/dev/mmcblk0p1 - boot-раздел
/dev/mmcblk0p2 - системный раздел
/dev/mmcblk0p3 - большой раздел для данных, RW.
Создаем новую точку монтирования для RW-раздела (/dev/mmcblk0p3). Я хочу, чтобы она была в корне файловой системы и открытая для записи от текущего пользователя:
# sudo mkdir /work
# sudo chown $(whoami):$(whoami) /work
# sudo chmod 700 /work
Создаем скрипт автоматического монтирования и открываем его на редактирования:
# sudo nano /etc/systemd/system/work.mount
ВАЖНО! Имя файла .mount должно строго соответствовать пути монтирования, в котором символы / заменяются на - (дефис), а начальный слэш опускается.
Примеры:
/work -> work.mount
/mnt/data -> mnt-data.mount
/var/lib/docker -> var-lib-docker.mount
Содержимое файла:
[Unit]
Description=Mount /dev/mmcblk0p3 on /work
DefaultDependencies=no
After=systemd-udevd.service
Requires=systemd-udevd.service
Before=local-fs.target
Before=umount.target
Conflicts=umount.target
[Mount]
What=/dev/mmcblk0p3
Where=/work
Type=ext4
Options=defaults,noatime
[Install]
WantedBy=local-fs.target
Нажимаем Ctrl + X, подтверждаем сохранение, Y:
Включаем mount-юнит, перезагружаемся чтобы проверить:
# sudo systemctl enable work.mount
Created symlink '/etc/systemd/system/local-fs.target.wants/work.mount' → '/etc/systemd/system/work.mount'.
# sudo reboot
Проверяем:
$ df -h
Filesystem Size Used Avail Use% Mounted on
...
/dev/mmcblk0p2 7.9G 2.3G 5.3G 30% /
/dev/mmcblk0p3 21G 2.1M 20G 1% /work
/dev/mmcblk0p1 505M 78M 427M 16% /boot/firmware
...
Все три раздела - там, где им нужно быть.
3.2 Включаем overlay-filesystem
# sudo raspi-config
Идём в Perfomance options -> P2 Overlay file system Enable/disable read only file system
Стрелкой влево выбираем Yes:
Ждем пока доставятся пакеты ... Процесс завершился успешно:
Нажимаем Yes, если нам не нужно менять информацию на /boot разделе внутри самой системы (если вытащить флешку и вставить в компьютер - все будет работать в режиме чтения-записи):
Две стрелки вправо - <Finish>, соглашаемся с перезагрузкой...
После этого все изменения в системном разделе будут в оперативной памяти. Это очень удобно для экспериментов - пробуем что то настроить - не получается - перезагрузка и система "девственная".
Для того, чтобы перейди в "write" режим (записать конфиг, обновить систему, доставить пакеты и так далее ..) необходимо просто выполнить команду:
# sudo overlayroot-chroot
Сделать все необходимые действия и выйти из режима, на всякий случай сделав синхронизацию
# cd /
# sync
: Эти изменения запишуться в системный раздел.
3.3 Автоматическая перезагрузка для очистки оперативки
Конечно же, система в процессе работы что-то записывает. И все эти записи помещаются в оперативку. На одноплатниках с оперативкой обычно довольно грустно, поэтому для автоматической очистки проще всего создать скрипт, который будет раз в сутки перезагружать нашу малину.
Откроем на запись наш системный раздел и добавим crontab, который выполняет автоматическую перезагрузку, например, в 4:00 ночи:
# sudo overlayroot-chroot
# crontab -e
Дописываем в конец:
0 4 * * * /usr/sbin/reboot
Ctrl+X, соглашаемся на запись ( Y ), видим что новый crontab установлен, синхронизируемся и выходим.
crontab: installing new crontab
# cd /
# sync
# exit
Вот и всё. Теперь ваша SD-карта живёт спокойно, система загружается в read-only, любые эксперименты умирают вместе с перезагрузкой, а для важных данных есть отдельный /work.
Если захотите что-то обновить или поставить — sudo overlayroot-chroot, сделали дело, вышли. Несложно.
Надеюсь, эта инструкция сэкономит вам пару нервных клеток и одну SD-карту в год.







