Skip to main content

Полное восстановление 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. Краткий аварийный чек-лист

  1. Остановить контейнеры, Docker и containerd.
  2. Выполнить sync.
  3. Прекратить работу из нестабильной Ubuntu.
  4. Загрузиться с SystemRescue.
  5. Определить диски по модели и серийному номеру.
  6. Сохранить полезный /boot с целевого диска.
  7. Запустить ddrescue -f -n с map-файлом.
  8. Выполнить ограниченное число повторных проходов.
  9. Сохранить финальный map-файл.
  10. Отключить исходный диск.
  11. Активировать LVM только на клоне.
  12. Выполнить e2fsck -fn.
  13. Смонтировать ro,noload.
  14. Проверить Docker, volumes и базы.
  15. Создать отдельный архив и проверить SHA-256.
  16. Исправить ext4 через e2fsck -fy.
  17. Восстановить /boot.
  18. Переустановить GRUB.
  19. Обновить initramfs и grub.cfg.
  20. Корректно размонтировать систему.
  21. Загрузиться с нового SSD.
  22. Подключить дополнительные диски.
  23. Проверить bind mounts.
  24. Запустить стеки в правильном порядке.
  25. Создать логические дампы баз.