Полное восстановление Ubuntu Server и Docker после отказа системного SSD
Назначение: практическая инструкция по восстановлению Ubuntu Server с LVM, отдельным разделом /boot и Docker-инфраструктурой после деградации системного SSD.
Основание: инструкция составлена по фактически выполненному восстановлению сервера homeserver2.
Критическое предупреждение: буквенные имена устройств /dev/sda, /dev/sdb, /dev/sdc могут меняться после каждой загрузки и каждого переподключения. Перед любой командой, которая пишет на диск, необходимо проверять модель, серийный номер, размер и интерфейс подключения.
1. Исходная ситуация
Сервер работал под Ubuntu Server 24.04 LTS. Системный диск начал проявлять признаки деградации:
- нестабильная загрузка Ubuntu;
- ошибки ввода-вывода;
- ошибки при копировании файлов;
- появление
Bus errorу обычных команд; - периодическое исчезновение SSD, подключённого через USB-SATA адаптер;
- невозможность надёжно завершить резервное копирование штатными средствами.
На сервере находилась критичная Docker-инфраструктура:
- Docker root:
/var/lib/docker; - прикладные данные и bind mounts:
/home/rich650/docker_config_and_data; - MariaDB/InnoDB;
- PostgreSQL Immich;
- Redis/Valkey;
- Portainer;
- BookStack;
- Nginx Proxy Manager;
- Vaultwarden;
- Guacamole;
- другие контейнеры и сервисы.
Файловая структура системного диска:
/dev/sdX1— служебный раздел около 1 МБ;/dev/sdX2— ext4, отдельный/bootоколо 2 ГБ;/dev/sdX3— LVM physical volume;- VG:
ubuntu-vg; - LV:
ubuntu-lv; - корневая файловая система: ext4.
2. Первые попытки сохранения данных
Сначала предпринималась попытка сохранить данные из ещё загружающейся Ubuntu.
Были штатно остановлены контейнеры:
docker ps -a
docker stop $(docker ps -q)
systemctl stop docker
systemctl stop containerd
sync
Дополнительно проверялось отсутствие активных СУБД:
ps aux | grep -Ei 'mariadb|mysqld|postgres|redis|mongod' | grep -v grep
Копирование планировалось выполнить на Kingston DataTraveler.
Однако при копировании возникли:
Input/output error;- ошибки записи после нескольких гигабайт;
Bus errorу командrsync,findmnt,mount,df.
Это означало, что продолжать работу из запущенной Ubuntu небезопасно. Причиной могла быть как деградация системного SSD, так и общая нестабильность ввода-вывода.
Решение: прекратить любые попытки длительного копирования из работающей системы и перейти к аварийному восстановлению из отдельной Live-системы.
3. Почему был выбран SystemRescue
Для восстановления использовался SystemRescue, загруженный через Ventoy.
Это позволило:
- не загружать неисправную Ubuntu;
- не монтировать системную файловую систему автоматически;
- использовать GNU ddrescue;
- работать с LVM;
- проверять ext4;
- выполнять chroot и восстановление GRUB;
- хранить map-файл и архивы на отдельном HDD Ventoy.
4. Инвентаризация дисков
После загрузки SystemRescue были определены все накопители:
lsblk -o NAME,SIZE,MODEL,SERIAL,TRAN,FSTYPE,MOUNTPOINTS
lsblk -d -o NAME,SIZE,MODEL,SERIAL,TRAN
В фактическом восстановлении использовались:
- Patriot Burst Elite 120GB — неисправный исходный SSD;
- WDC WDS120G2G0A — исправный целевой SSD;
- ST9320325AS — HDD с Ventoy и каталогом резервного восстановления;
- Kingston DataTraveler — накопитель, который ранее проявил ошибки записи.
Размер исходного и целевого SSD был проверен:
- Patriot: 120034123776 байт;
- WD: 120040980480 байт.
WD оказался немного больше Patriot, поэтому полное поблочное клонирование помещалось.
Главное правило:
Источник = неисправный Patriot
Назначение = исправный WD, который можно полностью перезаписать
5. Анализ LVM и файловых систем до клонирования
Были проверены:
pvs
vgs
lvs
dmsetup ls --tree
findmnt
Выяснилось, что:
- корень находится на LVM;
- отдельный раздел
/bootнаходится вне LVM; - WD и Patriot уже имели одинаковые структуры разделов и одинаковые UUID;
- LVM выбирал только один из одинаковых PV;
- после клонирования одновременно держать оба диска подключёнными опасно из-за дублирования UUID.
6. Сохранение актуального /boot с WD до перезаписи
До клонирования WD содержал более актуальный рабочий раздел /boot. Его необходимо было сохранить, потому что полное клонирование Patriot полностью перезаписывало WD.
mkdir -p /mnt/wd-boot
mount -o ro /dev/sda2 /mnt/wd-boot
mkdir -p /mnt/ventoy/server-recovery/wd-before-clone
tar --xattrs --acls --numeric-owner \
-C /mnt/wd-boot -cpf - . \
| zstd -T0 -3 \
-o /mnt/ventoy/server-recovery/wd-before-clone/boot-wd.tar.zst
Дополнительно были сохранены:
dd if=/dev/sda \
of=/mnt/ventoy/server-recovery/wd-before-clone/wd-first-16MiB.img \
bs=1M count=16 status=progress
sfdisk -d /dev/sda \
> /mnt/ventoy/server-recovery/wd-before-clone/wd-partitions.sfdisk
vgcfgbackup \
-f /mnt/ventoy/server-recovery/wd-before-clone/ubuntu-vg.conf \
ubuntu-vg
Архив был проверен:
zstd -t /mnt/ventoy/server-recovery/wd-before-clone/boot-wd.tar.zst
cd /mnt/ventoy/server-recovery/wd-before-clone
sha256sum boot-wd.tar.zst wd-first-16MiB.img > SHA256SUMS
sha256sum -c SHA256SUMS
После этого:
umount /mnt/wd-boot
sync
7. Поблочное клонирование через ddrescue
7.1. Создание рабочего каталога
mkdir -p /mnt/ventoy/server-recovery/patriot-to-wd
lsblk -o NAME,SIZE,MODEL,SERIAL,TRAN,FSTYPE,MOUNTPOINTS \
> /mnt/ventoy/server-recovery/patriot-to-wd/disks-before-clone.txt
7.2. Первый проход
ddrescue -f -n -v \
/dev/sdc \
/dev/sda \
/mnt/ventoy/server-recovery/patriot-to-wd/patriot-to-wd.map
Назначение параметров:
-f— разрешить запись на блочное устройство;-n— сначала копировать читаемые области, не тратя много времени на плохие блоки;-v— подробный вывод;- map-файл — хранит состояние восстановления и позволяет продолжать после остановки.
Первый проход завершился с результатом около 99,99% восстановленных данных.
Были обнаружены:
- несколько плохих областей;
- около 10 МБ
non-scraped; - несколько килобайт подтверждённых
bad-sector.
7.3. Проверка map-файла
sync
ddrescuelog -t \
/mnt/ventoy/server-recovery/patriot-to-wd/patriot-to-wd.map
cp -a \
/mnt/ventoy/server-recovery/patriot-to-wd/patriot-to-wd.map \
/mnt/ventoy/server-recovery/patriot-to-wd/patriot-to-wd.map.after-n
7.4. Повторный проход
ddrescue -f -r1 -v \
/dev/sdc \
/dev/sda \
/mnt/ventoy/server-recovery/patriot-to-wd/patriot-to-wd.map
Часть данных удалось дочитать, но Patriot начал исчезать из системы.
Сообщение:
Can't open input file: No such file or directory
означало, что устройство /dev/sdc физически исчезло из ядра Linux.
Причиной были не одинаковые UUID, а аппаратный отвал:
- USB-SATA адаптера;
- USB-порта;
- контроллера SSD;
- самого деградирующего Patriot.
7.5. Переподключение исходного SSD
udevadm settle
sleep 5
lsblk -d -o NAME,SIZE,MODEL,SERIAL,TRAN
Patriot находили по серийному номеру, а не по букве устройства.
7.6. Попытка чтения по одному сектору
ddrescue -f -r1 -c1 -v \
/dev/sdc \
/dev/sda \
/mnt/ventoy/server-recovery/patriot-to-wd/patriot-to-wd.map
Этот режим оказался слишком медленным и завершался ошибкой:
Unaligned read error. Is sector size correct?
Оценка времени доходила до нескольких суток. При этом было восстановлено уже 99,99% диска.
Решение: прекратить дальнейшее мучительное чтение, чтобы не добить источник окончательно.
7.7. Финальная фиксация карты
sync
ddrescuelog -t \
/mnt/ventoy/server-recovery/patriot-to-wd/patriot-to-wd.map
cp -a \
/mnt/ventoy/server-recovery/patriot-to-wd/patriot-to-wd.map \
/mnt/ventoy/server-recovery/patriot-to-wd/patriot-to-wd.map.final
8. Отключение исходного Patriot
После клонирования Patriot и WD имели одинаковые:
- UUID ext4;
- UUID LVM PV;
- структуры разделов;
- метаданные VG/LV.
Поэтому Patriot был физически отключён.
После отключения:
udevadm settle
sleep 3
lsblk -o NAME,SIZE,MODEL,SERIAL,TRAN,FSTYPE,MOUNTPOINTS
В системе должен оставаться только целевой WD.
9. Активация LVM на клоне
pvscan --cache
vgchange -ay ubuntu-vg
pvs -o pv_name,pv_uuid,vg_name,devices
lvs -o lv_name,vg_name,lv_size,devices
Было подтверждено:
/dev/sda3 -> ubuntu-vg -> ubuntu-lv
10. Первая проверка файловых систем без исправлений
Корень:
e2fsck -fn /dev/mapper/ubuntu--vg-ubuntu--lv
Раздел /boot:
e2fsck -fn /dev/sda2
Корневая ext4 прошла все пять этапов. Были найдены только:
- неправильный счётчик свободных блоков;
- неправильный счётчик свободных inode.
Структурных ошибок inode, каталогов и ссылок не было.
/boot прошёл проверку без ошибок.
11. Монтирование только для чтения
mkdir -p /mnt/wd-root
mount -o ro,noload \
/dev/mapper/ubuntu--vg-ubuntu--lv \
/mnt/wd-root
mkdir -p /mnt/wd-root/boot
mount -o ro,noload \
/dev/sda2 \
/mnt/wd-root/boot
Проверка:
findmnt /mnt/wd-root
findmnt /mnt/wd-root/boot
df -h /mnt/wd-root
Опция noload не позволяла ext4 автоматически воспроизводить журнал и изменять файловую систему.
12. Проверка Docker-инфраструктуры
12.1. Размеры каталогов
du -sh /mnt/wd-root/var/lib/docker
du -sh /mnt/wd-root/var/lib/containerd 2>/dev/null
du -sh /mnt/wd-root/home/rich650/docker_config_and_data
В фактическом восстановлении:
/var/lib/docker— около 9,7 ГБ;/var/lib/containerd— около 904 КБ;/home/rich650/docker_config_and_data— около 1,8 ГБ.
12.2. Контейнеры и volumes
find /mnt/wd-root/var/lib/docker/containers \
-mindepth 1 -maxdepth 1 -type d | wc -l
find /mnt/wd-root/var/lib/docker/volumes \
-mindepth 1 -maxdepth 1 -type d | wc -l
Было найдено:
- 17 каталогов контейнеров;
- 8 Docker volumes.
12.3. Поиск баз
find /mnt/wd-root \
-type f \
\( -name 'ibdata1' \
-o -name '*.ibd' \
-o -name 'aria_log_control' \
-o -name 'PG_VERSION' \
-o -name 'WiredTiger' \
-o -name 'dump.rdb' \) \
-print 2>/dev/null
Были найдены:
- MariaDB/InnoDB;
- таблицы BookStack;
- таблицы Nginx Proxy Manager;
- таблицы Guacamole;
- PostgreSQL Immich;
- Redis dump.
12.4. Поиск Compose и Portainer stacks
find /mnt/wd-root \
-type f \
\( -name 'compose.yml' \
-o -name 'compose.yaml' \
-o -name 'docker-compose.yml' \
-o -name 'docker-compose.yaml' \
-o -name '.env' \) \
-print 2>/dev/null
Были обнаружены Portainer stacks, docker-compose.yml, stack.env и прикладные .env.
13. Создание независимого архива Docker
До исправления файловой системы была создана отдельная резервная копия критичных каталогов:
mkdir -p /mnt/ventoy/server-recovery/docker-backup
tar \
--xattrs \
--xattrs-include='*' \
--acls \
--numeric-owner \
-C /mnt/wd-root \
-cpf - \
var/lib/docker \
var/lib/containerd \
home/rich650/docker_config_and_data \
etc \
root \
| zstd -T0 -3 \
-o /mnt/ventoy/server-recovery/docker-backup/docker-infrastructure.tar.zst
Во время архивирования появлялись сообщения:
socket ignored
Это нормально. Unix-сокеты являются временными объектами и создаются заново после запуска контейнеров.
Итог:
- исходный объём — около 11,2 ГБ;
- архив — около 4,27 ГБ.
13.1. Проверка архива
zstd -t \
/mnt/ventoy/server-recovery/docker-backup/docker-infrastructure.tar.zst
cd /mnt/ventoy/server-recovery/docker-backup
sha256sum docker-infrastructure.tar.zst \
> docker-infrastructure.sha256
sha256sum -c docker-infrastructure.sha256
Результат:
docker-infrastructure.tar.zst: OK
13.2. Проверка содержимого
zstd -dc docker-infrastructure.tar.zst \
| tar -tf - \
| grep -E '^var/lib/docker/(containers|volumes|overlay2)/' \
| head -50
zstd -dc docker-infrastructure.tar.zst \
| tar -tf - \
| grep -E 'ibdata1|\.ibd$|PG_VERSION|dump\.rdb' \
| head -100
zstd -dc docker-infrastructure.tar.zst \
| tar -tf - \
| grep 'portainer_data/_data/compose/.*/docker-compose.yml'
14. Исправление ext4
После создания архива файловые системы были размонтированы:
cd /
umount /mnt/wd-root/boot
umount /mnt/wd-root
Корень исправлен:
e2fsck -fy /dev/mapper/ubuntu--vg-ubuntu--lv
Во время исправления:
- был восстановлен журнал;
- исправлен счётчик свободных блоков;
- исправлен счётчик свободных inode.
Контрольная проверка:
e2fsck -fn /dev/mapper/ubuntu--vg-ubuntu--lv
После исправления ошибок больше не было.
Раздел /boot:
e2fsck -fy /dev/sda2
15. Восстановление актуального /boot
mount /dev/mapper/ubuntu--vg-ubuntu--lv /mnt/wd-root
mount /dev/sda2 /mnt/wd-root/boot
Содержимое клонированного /boot было очищено:
find /mnt/wd-root/boot \
-mindepth 1 \
-maxdepth 1 \
-exec rm -rf -- {} +
Затем восстановлен ранее сохранённый рабочий /boot:
zstd -dc \
/mnt/ventoy/server-recovery/wd-before-clone/boot-wd.tar.zst \
| tar \
--xattrs \
--acls \
--numeric-owner \
-xpf - \
-C /mnt/wd-root/boot
Проверка:
ls -lah /mnt/wd-root/boot
ls -lah /mnt/wd-root/boot/grub
Были доступны ядра:
6.8.0-134-generic;6.8.0-136-generic.
16. Восстановление GRUB и initramfs
16.1. Подготовка chroot
mount --bind /dev /mnt/wd-root/dev
mount --bind /dev/pts /mnt/wd-root/dev/pts
mount -t proc proc /mnt/wd-root/proc
mount -t sysfs sys /mnt/wd-root/sys
mount --bind /run /mnt/wd-root/run
Проверка:
findmnt | grep /mnt/wd-root
16.2. Вход в chroot
chroot /mnt/wd-root /bin/bash
16.3. Установка загрузчика
grub-install --target=i386-pc --recheck /dev/sda
update-initramfs -u -k all
update-grub
Ключевой успешный результат:
Installation finished. No error reported.
Предупреждения mdadm были допустимы, поскольку программный RAID mdadm не использовался.
Выход:
exit
17. Корректное размонтирование
umount /mnt/wd-root/run
umount /mnt/wd-root/sys
umount /mnt/wd-root/proc
umount /mnt/wd-root/dev/pts
umount /mnt/wd-root/dev
umount /mnt/wd-root/boot
umount /mnt/wd-root
Деактивация LVM:
vgchange -an ubuntu-vg
sync
Проверка:
lvs
findmnt | grep -E '/dev/sda|ubuntu--vg' || true
После этого:
poweroff
18. Первая загрузка с восстановленного WD
Перед загрузкой были отключены:
- Patriot;
- Ventoy/SystemRescue;
- Kingston DataTraveler.
В BIOS был выбран WD и сохранён режим Legacy/CSM.
После загрузки Ubuntu:
lsblk -o NAME,SIZE,MODEL,FSTYPE,MOUNTPOINTS
findmnt /
findmnt /boot
systemctl --failed
systemctl status docker --no-pager
docker ps -a
Результат:
- Ubuntu загрузилась;
- корень смонтирован с
ubuntu-vg/ubuntu-lv; /bootсмонтирован с/dev/sda2;- аварийных systemd units — 0;
- Docker активен;
- основные контейнеры были видны и часть из них запустилась автоматически.
19. Почему часть контейнеров осталась остановленной
После первой загрузки часть контейнеров имела состояние Exited.
Наиболее вероятная причина — при первой загрузке не был подключён второй диск с дополнительными данными и bind mounts.
Проверка:
lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,UUID,MOUNTPOINTS
cat /etc/fstab
findmnt
mount -av
df -hT
Проверка mount-путей контейнеров:
for c in \
immich_server immich_postgres immich_redis \
flaresolverr jackett gluetun-jackett transmission \
vaultwarden guacamole some-guacd
do
echo "===== $c ====="
docker inspect "$c" \
--format '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}'
done
Важно: не запускать контейнеры до подключения и монтирования всех нужных дисков. Иначе приложение может начать использовать пустой каталог на системном SSD.
20. Последовательный запуск контейнеров
20.1. Immich
docker start immich_postgres immich_redis
sleep 10
docker logs --tail 100 immich_postgres
docker logs --tail 100 immich_redis
docker exec immich_postgres pg_isready
docker start immich_server
docker logs --tail 150 immich_server
20.2. Gluetun, Jackett, Transmission
docker start gluetun-jackett
sleep 15
docker logs --tail 100 gluetun-jackett
docker start jackett transmission flaresolverr
20.3. Guacamole
docker start some-guacd guacamole
docker logs --tail 100 some-guacd
docker logs --tail 100 guacamole
20.4. Vaultwarden
docker start vaultwarden
docker logs --tail 100 vaultwarden
21. Проверка баз после восстановления
21.1. MariaDB
docker logs --tail 100 mariadb-db-1
docker exec mariadb-db-1 mariadb-admin ping
docker exec mariadb-db-1 mariadb -e 'SHOW DATABASES;'
21.2. PostgreSQL Immich
docker exec immich_postgres pg_isready
docker logs --tail 150 immich_postgres
21.3. Создание логических дампов
mkdir -p /home/rich650/recovery-dumps
docker exec mariadb-db-1 \
mariadb-dump \
--all-databases \
--single-transaction \
--routines \
--events \
--triggers \
> /home/rich650/recovery-dumps/mariadb-all.sql
Проверить параметры PostgreSQL:
docker inspect immich_postgres \
--format '{{range .Config.Env}}{{println .}}{{end}}' \
| grep -E 'POSTGRES_USER|POSTGRES_DB'
Дамп PostgreSQL:
docker exec immich_postgres \
pg_dumpall -U postgres \
> /home/rich650/recovery-dumps/immich-postgresql-all.sql
22. Что в итоге было восстановлено
- загрузочная способность сервера;
- MBR и GRUB Legacy BIOS;
- раздел
/boot; - LVM
ubuntu-vg/ubuntu-lv; - корневая ext4;
- Docker daemon;
- контейнеры и Docker metadata;
- Portainer stacks;
- Docker volumes;
- MariaDB;
- PostgreSQL Immich;
- Redis/Valkey;
- BookStack;
- Nginx Proxy Manager;
- прикладные bind mounts;
- системные конфигурации Ubuntu.
23. Основные выводы
- Неисправный SSD нельзя «лечить» до создания клона.
- GNU ddrescue с map-файлом — основной инструмент для аварийного копирования.
- При 99,99% восстановленных данных иногда разумнее остановиться, чем добивать источник сутками.
- Одинаковые UUID не заставляют диск исчезнуть, но создают конфликт LVM и файловых систем.
- Исходный диск после клонирования нужно отключать.
- До
e2fsck -fyнеобходимо сделать файловую копию критичных данных. - Контейнеры нельзя запускать до монтирования дополнительных дисков.
- Физическая копия базы недостаточна — после восстановления нужны логические дампы.
24. Краткий аварийный чек-лист
- Остановить контейнеры, Docker и containerd.
- Выполнить
sync. - Прекратить работу из нестабильной Ubuntu.
- Загрузиться с SystemRescue.
- Определить диски по модели и серийному номеру.
- Сохранить полезный
/bootс целевого диска. - Запустить
ddrescue -f -nс map-файлом. - Выполнить ограниченное число повторных проходов.
- Сохранить финальный map-файл.
- Отключить исходный диск.
- Активировать LVM только на клоне.
- Выполнить
e2fsck -fn. - Смонтировать
ro,noload. - Проверить Docker, volumes и базы.
- Создать отдельный архив и проверить SHA-256.
- Исправить ext4 через
e2fsck -fy. - Восстановить
/boot. - Переустановить GRUB.
- Обновить initramfs и grub.cfg.
- Корректно размонтировать систему.
- Загрузиться с нового SSD.
- Подключить дополнительные диски.
- Проверить bind mounts.
- Запустить стеки в правильном порядке.
- Создать логические дампы баз.