Debian 13: «Файлова система заповнена» зламала вашу БД — кроки відновлення, що справді працюють (випадок №59)

Було корисно?

Сигнал каже «filesystem full», додаток починає віддавати 500-ті, а ваша база—раніше впевнена—тепер поводиться так, ніби забула, як записувати.
Ви звільнили «трохи місця» й перезапустили все, а тепер БД або відмовляється запускатися, або нескінченно відтворює WAL/redo, або ще гірше: запускається і бреше.

Це керівництво з відновлення для Debian 13, коли ENOSPC (немає вільного місця на пристрої) влучив у вашу базу на продакшні. Не теорія. Не «просто видаліть логи».
Реальний runbook: як знайти, що саме заповнене, як звільнити місце не погіршивши консистентність, і як довести систему до безпечного, перевіреного стану.

Що «filesystem full» насправді робить з базами даних

«Диск заповнений» — це не один режим відмови. Це сімейство відмов, які однаково виглядають на дашборді, але поводяться зовсім по-різному о 03:00.
На Linux ви можете бути «заповнені» через закінчення блоків, через закінчення інодів, через квоти, або через те, що файловій системі потрібне відносно суцільне місце для власних механізмів безпеки (думаємо про WAL, redo, тимчасові файли).

Найнебезпечніший момент — перші кілька хвилин після ENOSPC. Бази даних реагують шляхом:

  • Зриву записів посеред транзакції, лишаючи частковий стан, який потрібно або відкотити, або відтворити.
  • Збоїв fsync. Ось де з’являється «я думав, що це було надійно».
  • Поганої ротації логів. Деякі рушії продовжують писати в той самий файловий дескриптор і «не бачать», що ви звільнили місце, поки не перезапуститеся.
  • Зависання під час відновлення, тому що само відновлення потребує тимчасового дискового простору.
  • Розриву реплікації, бо WAL/binlogs не можуть бути архівовані чи передані.

Ваше завдання — відновити інваріант, який очікує БД: достатньо вільного місця для завершення аварійного відновлення, застосування/відкату, чекпоінту та відновлення нормального патерну записів.
«Достатньо» — це не «кілька сотень МБ». У продакшні я орієнтуюся на принаймні 10–20% вільного на блоці бази як мінімальний запас для роботи.
Якщо ви не можете цього досягти, розглядайте це як аварію ємності, а не як господарське завдання.

Цитата, яку варто пам’ятати, — парафраз ідеї Вернера Вогельса (CTO Amazon): все ламається, тож проектуйте для швидкого виявлення і автоматичного відновлення.
Події «диск заповнений» — найбанальніша форма відмови — і найбільш принизлива, бо їх також найпростіше передбачити.

Плейбук швидкої діагностики (перший/другий/третій)

Ви під тиском. Потрібна коротка послідовність дій, яка швидко ідентифікує вузьке місце і уникає «випадкового прибирання», що знищує докази або ускладнює відновлення.
Робіть це в порядку. Не проявляйте креативності, поки не виконали базові кроки.

Перший: підтвердьте, який тип «заповнення» у вас

  1. Блоки закінчилися? Перевірте df -h на монтуванні БД.
  2. Іноди закінчилися? Перевірте df -i. Вичерпання інодів відчувається як «заповнено», навіть коли df -h виглядає нормально.
  3. Видалені, але відкриті файли? Перевірте lsof +L1. Ви можете «видалити» логи й нічого не звільнити.

Другий: виявляйте найбільших пишучих, а не найбільші файли

  1. Перевірте недавній ріст логів/журналів: journalctl --disk-usage, du у /var/log.
  2. Перевірте тимчасові теки та тимчасове використання БД: /tmp, /var/tmp, еквіваленти Postgres base/pgsql_tmp.
  3. Перевірте шари та образи контейнерів за потреби: docker system df (або ваш рантайм).

Третій: вирішіть стратегію відновлення

  1. Якщо БД не запускається: спочатку звільніть місце, потім запустіть БД і перевірте консистентність.
  2. Якщо БД запускається, але помилки при записах: тримайте її в робочому стані лише настільки, щоб захопити стан і відвести трафік; потім лагодьте ємність.
  3. Якщо є реплікація: розгляньте промоцію здорової репліки замість «геройських ремонтов» на первинному сервері.

Жарт №1: інциденти «диск заповнений» схожі на гравітацію — усі «в неї не вірять», поки не впадуть з даху.

Цікавинки та історичний контекст (коротко)

  • Резервовані блоки в ext-файлових системах: ext2/3/4 традиційно резервують ≈5% блоків для root, щоб уникнути повного «задриба», чудово для серверів, але збиває з пантелику людей.
  • ENOSPC не лише про блоки: однакова помилка часто повертається при вичерпанні інодів або квот, тому «df показує 40% вільно» все ще може бути кризою.
  • Журналірування не чарівне: ext4 journaling захищає консистентність метаданих, а не логічну правильність вашої бази даних. У БД є свій журнал з причини.
  • Стара Unix-порада: видалення файлу не звільняє місце, поки хоча б один процес тримає його відкритим — це правда десятиліттями і все ще спричиняє сучасні інциденти.
  • Підсилення записів реальне: бази даних можуть перетворювати один логічний запис у кілька фізичних (WAL/redo + дані + індекс + чекпоінт). «Ми лише вставляємо 1 GB/день» — ось як ви втрачаєте вихідні.
  • Іноди були розраховані під робочі навантаження 1980-х: значення за замовчуванням все ще можуть вдарити при мільйонах дрібних файлів (думаємо про кеші), навіть на багатотерабайтних дисках.
  • Аварійне відновлення потребує місця: Postgres може потребувати простору для відтворення WAL і тимчасових файлів; InnoDB може потребувати розширення логів; на порожньому ходу відновитись не вдасться.
  • Семантика файлових систем відрізняється: XFS поводиться інакше ніж ext4 під тиском (і подібно обробляє видалені відкриті файли); ZFS має власну культуру «не заповнювати пул понад ≈80%» через продуктивність.

Спочатку стабілізація: зупиніть кровотечу безпечно

Коли файлова система заповнена, неправильний крок — панічні перезапуски. Перезапуски можуть перетворити «тимчасово зависле, але консистентне» на «цикл відновлення, що потребує більше диска».
Стабілізація означає: зменшити записи, зберегти докази і уникати додаткової роботи для БД, поки у вас немає запасу місця.

Задача 1: підтвердьте, що ламається на шарі сервісів

cr0x@server:~$ systemctl --failed
  UNIT                      LOAD   ACTIVE SUB    DESCRIPTION
● postgresql@16-main.service loaded failed failed PostgreSQL Cluster 16-main

Що це означає: systemd підтверджує, що ваша БД падає, а не просто додаток.
Рішення: не спамте перезапусками. Перейдіть до логів і стану диска.

Задача 2: зафіксуйте останні релевантні помилки перед ротацією

cr0x@server:~$ journalctl -u postgresql@16-main.service -n 200 --no-pager
Dec 30 02:11:44 db1 postgres[9123]: FATAL:  could not write to log file: No space left on device
Dec 30 02:11:44 db1 postgres[9123]: PANIC:  could not write to file "pg_wal/00000001000000A9000000FF": No space left on device
Dec 30 02:11:44 db1 systemd[1]: postgresql@16-main.service: Main process exited, code=exited, status=1/FAILURE

Що це означає: записи WAL зазнали збою. Це не «приємна дрібниця»; це основна проблема довговічності.
Рішення: ваша перша мета — відновити достатньо місця для WAL і аварійного відновлення.

Задача 3: зупиніть трафік або поставте БД під «maintenance»-ворота

cr0x@server:~$ systemctl stop myapp.service
cr0x@server:~$ systemctl stop nginx.service

Що це означає: ви зменшуєте тиск записів під час відновлення.
Рішення: якщо у вас є fallback для читання — використовуйте його; інакше прийміть простій замість корупції.

Задача 4: заморозьте стан процесу БД (якщо він в циклі), а не вдавайтеся до жорсткого вбивства

cr0x@server:~$ systemctl kill -s SIGSTOP postgresql@16-main.service
cr0x@server:~$ systemctl status postgresql@16-main.service | sed -n '1,12p'
● postgresql@16-main.service - PostgreSQL Cluster 16-main
     Loaded: loaded (/lib/systemd/system/postgresql@.service; enabled)
     Active: activating (start) since Tue 2025-12-30 02:12:01 UTC; 3min ago
    Process: 10455 ExecStart=/usr/bin/pg_ctlcluster --skip-systemctl-redirect 16-main start (code=exited, status=1/FAILURE)
   Main PID: 10501 (code=killed, signal=STOP)

Що це означає: процес призупинено, він не трясеться по диску.
Рішення: робіть це, якщо процес повторно намагається відновитися і «зжирає» місце, яке ви тільки-но звільнили; відновіть з SIGCONT, коли буде запас.

Практичне правило: якщо ви не знаєте, що видалити, зупиніть пишучі сервіси першими. Ви завжди можете перезапустити сервіси; ви не можете повернути видалений помилковий файл під час інциденту консистентності.

Знаходження вільного місця з наміром (команди + рішення)

Вам потрібно швидко відповісти на чотири питання:
Яка файлова система заповнена? Це блоки, іноди, квоти чи «відкриті видалені файли»?
Хто пише? Чи можу я створити стабільне вільне місце, яке залишиться вільним?

Задача 5: ідентифікуйте заповнену файлову систему(и)

cr0x@server:~$ df -hT
Filesystem     Type   Size  Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4    80G   79G     0 100% /
/dev/nvme1n1p1 ext4   1.8T  1.2T  520G  70% /var/lib/postgresql

Що це означає: коренева файлова система повністю заповнена; монтування БД в порядку. Це все одно ламає БД, якщо вона логівиться в /var/log або використовує /tmp на /.
Рішення: фокусуйтеся на /, а не на томі БД. «Диск БД 70%» вас не рятує.

Задача 6: перевірка вичерпання інодів

cr0x@server:~$ df -i
Filesystem      Inodes   IUsed   IFree IUse% Mounted on
/dev/nvme0n1p2 5242880 5242880       0  100% /
/dev/nvme1n1p1 61054976  712345 60342631    2% /var/lib/postgresql

Що це означає: іноди на / вичерпані. Це часто спричинюють кеші, неконтрольовані дрібні файли або поштові черги.
Рішення: видалення одного великого файлу не допоможе. Потрібно видалити багато дрібних файлів або перемістити навантаження.

Задача 7: виявлення видалених, але відкритих файлів (класика «я видалив, чому все ще заповнено?»)

cr0x@server:~$ sudo lsof +L1 | head -n 15
COMMAND   PID USER   FD   TYPE DEVICE  SIZE/OFF NLINK    NODE NAME
rsyslogd  721 syslog  6w   REG  259,2 2147483648     0 1049883 /var/log/syslog.1 (deleted)
java     2041 app    12w   REG  259,2 1073741824     0 1051122 /var/log/myapp/app.log (deleted)

Що це означає: простір все ще утримується працюючими процесами. Ім’я файлу зникло, але інод все ще зайнятий.
Рішення: перезапустіть або сигналізуйте цим процесам закрити/відкрити логи (наприклад, systemctl restart rsyslog), або, якщо потрібно, усуньте через /proc/<pid>/fd/<fd>.

Задача 8: знайдіть, яка директорія на заповненій файловій системі винна

cr0x@server:~$ sudo du -xhd1 / | sort -h
0	/boot
1.2G	/etc
2.8G	/home
4.5G	/opt
9.9G	/usr
12G	/var
49G	/

Що це означає: /var займає багато місця на root; зазвичай це логи, кеші, спули або сміття від контейнерів.
Рішення: заглиблюйтеся в /var далі, залишаючись на тій самій файловій системі (-x важливий).

Задача 9: визначте найбільших споживачів всередині /var

cr0x@server:~$ sudo du -xhd1 /var | sort -h
120M	/var/cache
260M	/var/tmp
1.1G	/var/lib
1.7G	/var/spool
8.4G	/var/log
12G	/var

Що це означає: логи великі. Це поширено, але не припускайте, що можна безпечно видалити все.
Рішення: інспектуйте /var/log, особливо журнали і логи додатків.

Задача 10: перевірте використання systemd-journald і ліміти

cr0x@server:~$ journalctl --disk-usage
Archived and active journals take up 6.8G in the file system.

Що це означає: journald займає значну частину. На невеликих root-розділах він може тихо виграти конкурс «хто з’їв мій диск».
Рішення: звакуумуйте журнали до безпечного розміру, потім встановіть персистентні ліміти, щоб це не повторилося.

Задача 11: швидке знаходження великих файлів (тріаж за розміром)

cr0x@server:~$ sudo find /var/log -xdev -type f -size +200M -printf '%s %p\n' | sort -n | tail -n 10
268435456 /var/log/journal/3b2b1a.../system@0000000000000000-0000000000000000.journal
536870912 /var/log/myapp/app.log
2147483648 /var/log/syslog.1

Що це означає: у вас кілька дуже великих файлів. Це найпростіший тип прибирання — якщо вони не утримуються відкритими.
Рішення: якщо логи утримуються відкритими, перезапустіть логгер/додаток після правильної обрізки/ротації.

Задача 12: перевірте кеші пакетів (відносно безпечне для видалення)

cr0x@server:~$ sudo du -sh /var/cache/apt/archives
1.1G	/var/cache/apt/archives

Що це означає: apt cache непомітний.
Рішення: очищення зазвичай безпечне і швидке; це не змінює стан БД.

Задача 13: перевірте, чи сам том БД не має прихованого нагромадження (WAL, temp, бекапи)

cr0x@server:~$ sudo du -sh /var/lib/postgresql /var/lib/postgresql/* 2>/dev/null | sort -h | tail -n 8
48G	/var/lib/postgresql/16
48G	/var/lib/postgresql/16/main
7.9G	/var/lib/postgresql/16/main/pg_wal

Що це означає: WAL великий, але не обов’язково неправильний — може бути, що репліка відстала, архівування не працює або довгі транзакції.
Рішення: не видаляйте WAL-файли вручну. Виправте первинну причину (реплікація/архівування) і дозвольте Postgres управляти утриманням.

Задача 14: перевірте тиск пам’яті + swap (бо відновлення потребує ОЗУ і tmp)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            32Gi        26Gi       1.1Gi       214Mi       4.9Gi       3.8Gi
Swap:            0B          0B          0B

Що це означає: у вас мало доступної RAM, немає swap. Деякі задачі відновлення витісняють у тимчасові файли; низька RAM може посилити використання диска і тривалість.
Рішення: уникайте важких операцій обслуговування (VACUUM FULL, OPTIMIZE) під час відновлення; спочатку стабілізуйтеся.

Безпечне звільнення місця (що можна видаляти, а що ні)

Пріоритет — створити стабільне вільне місце — простір, який залишиться вільним після відновлення сервісів.
Видалення випадкових файлів може уповільнити завантаження системи, втратити аудиторські сліди або зламати стан пакетів. Видалення неправильних файлів БД може закінчитися тижнем роботи.

Низькоризикові кроки (робіть це першими)

Задача 15: вакуумуйте systemd journal до обмеженого розміру

cr0x@server:~$ sudo journalctl --vacuum-size=500M
Vacuuming done, freed 6.3G of archived journals from /var/log/journal.

Що це означає: ви повернули реальний простір. Це зазвичай безпечно під час інцидентів.
Рішення: якщо це мало вирішило проблему, journald не ваш основний винуватець; рухайтесь далі.

Задача 16: встановіть ліміти journald, щоб це не повторилося завтра

cr0x@server:~$ sudo sed -i 's/^#SystemMaxUse=.*/SystemMaxUse=500M/' /etc/systemd/journald.conf
cr0x@server:~$ sudo systemctl restart systemd-journald

Що це означає: ви обмежили майбутній ріст логів.
Рішення: оберіть значення, що відповідає вашому диску й вимогам зберігання; правильне число залежить від ваших потреб з інцидентного реагування.

Задача 17: очистіть apt cache

cr0x@server:~$ sudo apt-get clean
cr0x@server:~$ sudo du -sh /var/cache/apt/archives
4.0K	/var/cache/apt/archives

Що це означає: ви повернули частину місця, не торкаючись стану додатку.
Рішення: робіть це для швидкого полегшення; це не усуває корінної причини.

Задача 18: правильно оберніть/обріжте runaway логи додатків

cr0x@server:~$ sudo truncate -s 0 /var/log/myapp/app.log
cr0x@server:~$ sudo systemctl restart myapp.service

Що це означає: обрізка звільняє місце відразу (якщо тільки файл не буде замінено шаблонами logrotate).
Рішення: обрізайте лише ті логи, які вам не шкода втратити. Після інциденту краще вирішити проблему через logrotate.

Задача 19: вирішіть випадок видалених, але відкритих логів, перезапустивши відповідний демон

cr0x@server:~$ sudo systemctl restart rsyslog.service
cr0x@server:~$ sudo lsof +L1 | head
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME

Що це означає: більше немає (або менше) записів про відкриті видалені файли.
Рішення: якщо використання місця не впало — ваша проблема не в відкритих файлах; перевірте df і іноди знову.

Середньоризикові кроки (робіть з наміром)

Ці кроки можуть бути безпечними, але не позбавлені наслідків.

Задача 20: очистіть великі кеші з відомим власником (приклад: директорія кешу додатку)

cr0x@server:~$ sudo du -sh /var/cache/myapp
3.4G	/var/cache/myapp
cr0x@server:~$ sudo rm -rf /var/cache/myapp/*
cr0x@server:~$ sudo du -sh /var/cache/myapp
12K	/var/cache/myapp

Що це означає: ви повернули простір, але можливо збільшите навантаження при «підгріві» кешу.
Рішення: прийнятно під час інциденту, але координуйте з власниками додатків; слідкуйте за CPU і затримками після перезапуску.

Задача 21: очистіть core dumps (часто величезні, часто забуті)

cr0x@server:~$ sudo coredumpctl list | head
TIME                            PID   UID   GID SIG COREFILE  EXE
Tue 2025-12-30 00:13:19 UTC    8123  1001  1001  11 present   /usr/bin/myapp
cr0x@server:~$ sudo du -sh /var/lib/systemd/coredump
5.2G	/var/lib/systemd/coredump
cr0x@server:~$ sudo rm -f /var/lib/systemd/coredump/*
cr0x@server:~$ sudo du -sh /var/lib/systemd/coredump
0	/var/lib/systemd/coredump

Що це означає: ви видалили артефакти діагностики.
Рішення: робіть це лише якщо ви вже зібрали потрібні дані для налагодження або якщо час роботи важливіший за постмортем.

Високоризикові кроки (зазвичай уникати під час відновлення)

  • Видалення файлів бази даних під /var/lib/postgresql або /var/lib/mysql, бо вони «виглядають великими». Так ви створите другий інцидент.
  • Запуск агресивного обслуговування БД (VACUUM FULL, REINDEX DATABASE, OPTIMIZE TABLE) коли мало диска. Ці операції часто потребують більше диска для завершення.
  • Переміщення каталогу бази даних на льоту без протестованого плану. Якщо потрібно рухати сховище — робіть це з контрольованою зупинкою, копіюванням/rsync, верифікацією, оновленням конфігу сервісу, а потім запуском.

Жарт №2: найшвидший спосіб звільнити диск — видалити /var/lib; найшвидший спосіб оновити резюме — зробити це в продакшні.

Кроки відновлення бази даних, що реально працюють

«Що справді працює» тут означає: ви отримуєте базу, що запускається, приймає записи і достатньо консистентна, щоб їй довіряти — підкріплено перевірками, а не відчуттями.
Точні кроки залежать від рушія, але форма постійна:
звільнити місце → запустити безпечно → підтвердити цілісність → виправити вектор росту → відновити запаси.

Нульовий крок: отримайте реальний запас

Перед тим, як знову запускати БД, прагніть принаймні:

  • PostgreSQL: вільне місце ≥ WAL, потрібний для відтворення + трохи для тимчасових файлів; я орієнтуюся на 10–20% обсягу кластера або принаймні кілька ГБ для маленьких систем.
  • MySQL/InnoDB: достатньо для redo/undo та тимчасових таблиць; знову ж таки, 10–20% на файловій системі, де розміщені datadir і tmpdir.

Задача 22: перевірте вільне місце після прибирання (не припускайте)

cr0x@server:~$ df -hT /
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4   80G   62G   14G  82% /

Що це означає: у вас тепер 14G вільно на root. Це робочий запас для логів, тимчасових і відновлення.
Рішення: продовжуйте з перезапуском БД. Якщо ви все ще >95% — продовжуйте звільняти місце або розширюйте файлову систему спочатку.

PostgreSQL на Debian 13 (кластеризована служба)

Пакети PostgreSQL у Debian використовують кластери (наприклад, postgresql@16-main), і зазвичай він поводиться коректно під час відновлення — якщо дати йому місце.
Два найпоширеніші «підводні камені» Postgres при заповненому диску: ручне видалення WAL і повторні перезапуски, які ніколи не дають відновленню завершитися.

Задача 23: відновіть призупинену БД (якщо ви використовували SIGSTOP), потім запустіть чисто

cr0x@server:~$ sudo systemctl kill -s SIGCONT postgresql@16-main.service
cr0x@server:~$ sudo systemctl start postgresql@16-main.service
cr0x@server:~$ sudo systemctl status postgresql@16-main.service | sed -n '1,12p'
● postgresql@16-main.service - PostgreSQL Cluster 16-main
     Loaded: loaded (/lib/systemd/system/postgresql@.service; enabled)
     Active: active (running) since Tue 2025-12-30 02:24:09 UTC; 3s ago

Що це означає: служба піднялась. Ще не гарантована її повна працездатність, але вона «дихає».
Рішення: перевірте логи на завершення відновлення і впевніться, що вона приймає підключення.

Задача 24: підтвердіть завершення аварійного відновлення по логах

cr0x@server:~$ sudo journalctl -u postgresql@16-main.service -n 80 --no-pager
Dec 30 02:24:08 db1 postgres[11001]: LOG:  database system was interrupted; last known up at 2025-12-30 02:10:21 UTC
Dec 30 02:24:08 db1 postgres[11001]: LOG:  redo starts at A9/FF000028
Dec 30 02:24:09 db1 postgres[11001]: LOG:  redo done at A9/FF9A2B30
Dec 30 02:24:09 db1 postgres[11001]: LOG:  database system is ready to accept connections

Що це означає: відтворення WAL завершилося і Postgres оголосив готовність.
Рішення: переходьте до перевірки цілісності і поступового повернення робочого навантаження.

Задача 25: перевірте підключення БД і базовий запис/читання

cr0x@server:~$ sudo -u postgres psql -d postgres -c "select now();"
              now
-------------------------------
 2025-12-30 02:24:31.12345+00
(1 row)

cr0x@server:~$ sudo -u postgres psql -d postgres -c "create table if not exists diskfull_probe(x int); insert into diskfull_probe values (1);"
CREATE TABLE
INSERT 0 1

Що це означає: ви можете підключитися і зафіксувати транзакцію.
Рішення: залиште цю пробну таблицю або видаліть пізніше; важливо перевірити, що запис можливий.

Задача 26: перевірте наявність залишкових помилок «немає місця» на SQL-рівні

cr0x@server:~$ sudo -u postgres psql -d postgres -c "select datname, temp_bytes, deadlocks from pg_stat_database;"
  datname  | temp_bytes | deadlocks
-----------+------------+-----------
 postgres  |          0 |         0
 template1 |          0 |         0
(2 rows)

Що це означає: тимчасове використання мінімальне; явних артефактів contention немає.
Рішення: якщо temp_bytes різко росте, навантаження витісняє на диск; впевніться, що temp_tablespaces і запас файлової системи адекватні.

Задача 27: запустіть таргетовану перевірку цілісності (не повну паніку по всіх таблицях)

cr0x@server:~$ sudo -u postgres psql -d postgres -c "select * from pg_stat_wal;"
 wal_records | wal_fpi | wal_bytes | wal_buffers_full | wal_write | wal_sync
-------------+---------+-----------+------------------+----------+----------
       18234 |     112 |  12345678 |                0 |      214 |       93
(1 row)

Що це означає: підсистема WAL працює. Це не гарантія відсутності корупції, але сигнал «зараз не горить».
Рішення: якщо підозрюєте корупцію, заплануйте amcheck або відновлення з бекапу; не імпровізуйте вікні інциденту.

MySQL / MariaDB (InnoDB) на Debian 13

InnoDB виконує відновлення шляхом відтворення redo логів. Заповнення диска може перервати це і залишити вас з сервісом, що не стартує або стартує в режимі тільки для читання.
Найгірший крок — видалити ib_logfile або ibdata файли, щоб «примусити» старт. Це не ремонт; це втрата даних із додатковими кроками.

Задача 28: прочитайте останні повідомлення відновлення InnoDB

cr0x@server:~$ journalctl -u mariadb.service -n 120 --no-pager
Dec 30 02:13:02 db1 mariadbd[9322]: InnoDB: Error: Write to file ./ib_logfile0 failed at offset 1048576.
Dec 30 02:13:02 db1 mariadbd[9322]: InnoDB: Error: 28  No space left on device
Dec 30 02:13:02 db1 mariadbd[9322]: InnoDB: Plugin initialization aborted with error Generic error
Dec 30 02:13:02 db1 systemd[1]: mariadb.service: Main process exited, code=exited, status=1/FAILURE

Що це означає: запис у redo лог провалився. Відновлення не може відбутися без простору.
Рішення: звільніть місце на файловій системі, що містить datadir (часто /var/lib/mysql) та будь-який налаштований tmpdir.

Задача 29: підтвердіть розташування datadir і tmpdir, щоб не ганятися по неправильному монту

cr0x@server:~$ sudo my_print_defaults --mysqld | egrep -i 'datadir|tmpdir'
--datadir=/var/lib/mysql
--tmpdir=/tmp

Що це означає: tmpdir на /. Якщо root заповнений, запити, що створюють тимчасові таблиці, впадуть, навіть якщо /var/lib/mysql в порядку.
Рішення: розгляньте переміщення tmpdir на більший файловий простір після відновлення (і протестуйте це).

Задача 30: запустіть MariaDB/MySQL і спостерігайте прогрес відновлення

cr0x@server:~$ sudo systemctl start mariadb.service
cr0x@server:~$ sudo systemctl status mariadb.service | sed -n '1,14p'
● mariadb.service - MariaDB 10.11.6 database server
     Loaded: loaded (/lib/systemd/system/mariadb.service; enabled)
     Active: active (running) since Tue 2025-12-30 02:27:40 UTC; 2s ago

Що це означає: сервіс запустився. Тепер підтвердіть, що він не мовчки працює з помилками.
Рішення: перевірте лог помилок і виконайте базові запити.

Задача 31: перевірте базовий запис/читання і стан InnoDB

cr0x@server:~$ sudo mariadb -e "select @@version, now();"
@@version	now()
10.11.6-MariaDB-0+deb13u1	2025-12-30 02:28:03

cr0x@server:~$ sudo mariadb -e "create database if not exists diskfull_probe; use diskfull_probe; create table if not exists t(x int); insert into t values (1);"
cr0x@server:~$ sudo mariadb -e "show engine innodb status\G" | sed -n '1,35p'
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
2025-12-30 02:28:11 0x7f9c2c1fe6c0 INNODB MONITOR OUTPUT
...
Log sequence number 123456789
Log flushed up to   123456789

Що це означає: відмивання redo логів наздогнало; базові коміти працюють.
Рішення: повертайте трафік поступово, моніторьте помилки й використання диска уважно.

Якщо БД все ще не запускається: що робити (і чого не робити)

Якщо ви створили запас місця, а БД все ще падає, не тягніться одразу за «примусовими режимами відновлення», якщо не готові відновлювати з бекапу.
Примусове відновлення може бути інструментом для витягання даних, а не стратегією повернення в сервіс.

Задача 32: перевірте права й сигнали здоров’я файлової системи (швидка санітарна перевірка)

cr0x@server:~$ sudo dmesg -T | tail -n 20
[Thu Dec 30 02:22:11 2025] EXT4-fs warning (device nvme0n1p2): ext4_dx_add_entry: Directory index full!
[Thu Dec 30 02:22:12 2025] EXT4-fs (nvme0n1p2): Delayed block allocation failed for inode 3932211 at logical offset 0 with max blocks 2 with error 28

Що це означає: ядро бачило відмови алокації, сумісні з ENOSPC. Немає негайних сигналів проблем із носієм, але це правда землі.
Рішення: якщо бачите I/O помилки, remount в режим read-only або попередження про корупцію — зупиніться і плануйте відновлення/міграцію.

Задача 33: якщо ви на LVM — розширте файлову систему замість гри в whack-a-mole з прибиранням

cr0x@server:~$ sudo vgs
  VG   #PV #LV #SN Attr   VSize   VFree
  vg0    1   2   0 wz--n- 200.00g  40.00g

cr0x@server:~$ sudo lvextend -L +20G /dev/vg0/root
  Size of logical volume vg0/root changed from 80.00 GiB (20480 extents) to 100.00 GiB (25600 extents).
  Logical volume vg0/root successfully resized.

cr0x@server:~$ sudo resize2fs /dev/vg0/root
resize2fs 1.47.0 (5-Feb-2023)
Filesystem at /dev/vg0/root is mounted on /; on-line resizing required
old_desc_blocks = 10, new_desc_blocks = 13
The filesystem on /dev/vg0/root is now 26214400 (4k) blocks long.

cr0x@server:~$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/mapper/vg0-root   99G   62G   33G  66% /

Що це означає: ви перетворили повторюваний інцидент на ємнісну проблему.
Рішення: якщо можете розширити — зробіть це. Прибирання — тимчасовий захід; ємність — це виправлення.

Чеклісти / покроковий план (друкований формат мислення)

Чекліст A: реагування на інцидент «файлова система заповнила БД»

  1. Зупиніть сервіси з великим обсягом записів (воркери додатків, ingestion, cron). Тримайте БД в приготуванні, поки не буде місця.
  2. Заберіть логи з systemd та логів БД перед ротацією/обрізкою.
  3. Підтвердіть режим відмови: блоки (df -h), іноди (df -i), deleted-open (lsof +L1), квоти.
  4. Звільніть низькоризиковий простір: vacuum journald, apt cache, відомі безпечні кеші, ротація логів.
  5. Перевірте вільний простір і забезпечте запас (не погоджуйтеся на 200MB).
  6. Запустіть БД один раз і дайте їй відновитися. Не доводьте до повторних перезапусків.
  7. Підтвердіть готовність: БД логує «ready», підключення працює, базовий запис пройшов.
  8. Поступово повертайте трафік: спочатку канаркові воркери, потім скейл.
  9. Виправте причину росту: ротація логів, збій архівування, тимчасові запити, контейнери, бекапи.
  10. Встановіть захисні заходи: моніторинг, квоти, ліміти journald, пороги алертів, план ємності.

Чекліст B: зміцнення після інциденту (як запобігти випадку №60)

  1. Алертуйте одночасно на блоки і іноди. Багато команд алертують тільки на блоки й потім дивуються.
  2. Помістіть тимчасові шляхи БД на том з запасом. Для Postgres розгляньте виділений tablespace для тимчасових файлів, якщо доречно.
  3. Застосуйте обмеження на логи (journald + логи додатків) і протестуйте logrotate під навантаженням.
  4. Майте принаймні один протестований шлях відновлення (відновлення з бекапу або промоція репліки), що не вимагає героїки.
  5. Періодично проводьте «disk full game day» у staging: симулюйте ENOSPC і перевіряйте час і кроки відновлення.

Три корпоративні міні-історії з практики

1) Інцидент через неправильне припущення: «БД має свій диск, отже root може заповнитися»

Компанія середнього розміру розділила сховище «правильно»: дані бази на великому окремому монту, root на маленькому NVMe розділі.
Команда відчувала себе правильно. Том БД мав багато місця; графіки були зеленими. Ротація on-call розслабилась.

Потім запустили незначну зміну: випадково увімкнули детальний debug для сервісу аутентифікації.
Логи вибухнули в /var/log на root. За кілька годин root досяг 100%. Том БД все ще мав сотні гігабайт вільних,
тому командир інциденту спочатку відкинув сховище як причину і ганявся за мережевими та CPU підказками.

PostgreSQL почав падати при записах — не тому, що /var/lib/postgresql був повний, а через неможливість писати логи та створювати тимчасові файли.
Почалися цикли відновлення: systemd намагався перезапустити; кожен перезапуск намагався записати лог; кожен лог-пис провалювався; сервіс флапав.
Тим часом додаток, бачачи відмови з’єднань, агресивно повторював запити, підсилюючи проблему.

Ремонт був болісно простим: vacuum journald, повернути runaway app log в ротацію, зупинити шторм повторних запитів і лише потім перезапустити БД.
Акт після розслідування, що мав значення, був не «додати більше диска», а «ставити root як залежність БД» і налаштувати алерти відповідно.

2) Оптимізація, що повернулась бумерангом: «Вимкнути стиснення logrotate, воно їсть CPU»

Велика внутрішня платформа захотіла зменшити сплески CPU на флоті Debian проксі-баз даних.
Хтось помітив, що стиснення в logrotate кусає CPU в пікові години. Розумна думка: відключити стиснення і лишити ротацію.
CPU вирівнявся. Всі привітали себе і переключились далі.

Через два тижні кілька вузлів одночасно почали стикатися з повним диском. Проксі баз даних були в порядку; проксі — ні.
Нестиснені архівні логи стали величезними, і збереження ретенції було налаштоване під «стиснений розмір», а не сирий. Root-розділи були маленькі, бо «вони нічого важливого не роблять».
Інцидент проявився як хитання підключень і каскадні повтори — класична поведінка розподілених систем: одна мала помилка стає проблемою для всіх.

Ремонт був брудним, бо інженери постійно видаляли старі логи, поки rsyslog ще тримав дескриптори файлів відкритими.
Місце не поверталося, що вело до ще більшого видалення і меншої кількості судових логів. Зрештою вирішили перезапустити rsyslog, налаштувати адекватні політики logrotate і перемістити логи високого обсягу на виділений том.

Урок: оптимізації, що прибирають захисні засоби, рідко бувають безкоштовними. CPU зазвичай легше купити, ніж час на чисте відновлення.
Якщо ви відключаєте стиснення — потрібно переналаштувати ретенцію і алерти, інакше диск стане вашим новим планувальником.

3) Нудна, але правильна практика, що врятувала день: «Завжди мати репліку з реальними runbook-ами промоції»

Команда фінансових послуг експлуатувала PostgreSQL з однією стрімінговою реплікою в іншому стійці. Нічого фешенебельного.
Нудна частина: раз на квартал вони практикували промоцію репліки, оновлення конфігів додатків і повернення/перебудову старого primary.
Вони ставили це як тренування — набридливе, заплановане і обов’язкове.

Під час місячного batch-запуску первинка заповнила файлову систему через помилку архівування, що спричинила бум WAL.
Записи зупинилися. Спроби відновлення билися за диск. Команда не бралася за складні операції на хості, поки стейкхолдери спостерігали.
Вони перевірили, що репліка достатньо догналася, промотували її і відновили сервіс з мінімальною драмою.

Після переміщення трафіку вони спокійно розширили сховище, виправили архівування, перевірили бекапи і чисто перебудували реплікацію.
Постмортем виглядав як список покупок, а не трилер. Оце і є мета. Нудність — це фіча в операціях.

Поширені помилки: симптом → корінь проблеми → виправлення

1) «df показує 0 байтів звільнених після видалення логів»

  • Симптом: ви видаляєте великі файли, але df -h лишається на 100%.
  • Корінь проблеми: файли були видалені, але все ще утримуються процесами відкритими.
  • Виправлення: lsof +L1; перезапустіть сервіс-власник або усуньте відкритий FD через /proc/<pid>/fd/<fd>. Потім перевірте df знову.

2) «df -h виглядає нормально, але все падає з ‘No space left on device’»

  • Симптом: багато ГБ вільно, але створення/записи провалюються.
  • Корінь проблеми: вичерпання інодів (df -i) або квоти/проекти.
  • Виправлення: видаліть велику кількість дрібних файлів у проблемній директорії; для квот — перевірте і підніміть ліміти; для інодів зупиніть роботу, що створює файли, і перегляньте макет сховища.

3) «БД запускається, але додаток отримує періодичні помилки запису»

  • Симптом: сервіс вгорі, але деякі записи провалюються, операції з тимчасовими таблицями падають або сортування падає.
  • Корінь проблеми: tmpdir або директорія логів на все ще заповненій файловій системі (часто root), тоді як datadir в порядку.
  • Виправлення: підтвердіть шляхи tmpdir (локації тимчасових файлів Postgres відрізняються; MySQL tmpdir налаштовується). Звільніть/розширте правильний том; перемістіть tmpdir на більший монту з протестованими змінами конфігу.

4) «Каталог WAL Postgres величезний; давайте видалимо старі WAL файли»

  • Симптом: pg_wal займає десятки ГБ.
  • Корінь проблеми: replication slot утримує WAL, репліка впала, архівування не працює або довгі транзакції перешкоджають очищенню.
  • Виправлення: визначте replication slots і відставання; виправте архівування; видаліть невикористані слоти; вирішіть довгі транзакції. Не видаляйте WAL вручну.

5) «MySQL не стартує; хтось пропонує видалити ib_logfile0»

  • Симптом: помилки ініціалізації InnoDB після ENOSPC.
  • Корінь проблеми: неповні записи redo логу через заповнення диска і недостатній простір для відновлення.
  • Виправлення: відновіть місце, запустіть чисто, перевірте стан InnoDB. Якщо корупція триває — використовуйте бекапи/репліки; forced recovery — для витягання даних, а не для нормального повернення в сервіс.

6) «Ми звільнили 2GB; чому відновлення все ще падає?»

  • Симптом: відновлення стартує, потім знову падає з ENOSPC.
  • Корінь проблеми: саме відновлення генерує записи (відтворення WAL, тимчасові файли, чекпоінти). 2GB — не стратегія.
  • Виправлення: звільніть/розширте, доки не отримаєте значний запас (10–20% або кілька ГБ залежно від розміру БД) і спробуйте ще раз.

7) «Після прибирання диск миттєво заповнюється»

  • Симптом: ви звільняєте місце, перезапускаєте сервіси, і через хвилини все знову 100%.
  • Корінь проблеми: runaway log spam, шторм повторів, зависла черга або пакетна робота, що відновлюється.
  • Виправлення: тримайте писачів зупиненими; ідентифікуйте головного писача; додайте rate limits; змініть рівні логів; акуратно відведіть черги; лише потім поступово поверніть трафік.

FAQ

1) Чи «filesystem full» те саме, що «disk full»?

Не завжди. Файлова система може бути «заповнена» через вичерпання блоків, інодів або через резерви/обмеження квот.
Завжди перевіряйте df -h і df -i, а також скануйте на видалені, але відкриті файли з lsof +L1.

2) Чи можна просто видалити старі логи, щоб виправити це?

Іноді так, але робіть це обдумано. Якщо логи утримуються відкритими, видалення не звільнить простір, доки процес не закриє їх.
Також: видалення логів може знищити єдині докази того, що сталося, тому спершу зафіксуйте потрібні дані.

3) Чому моя БД зламалася, якщо розділ бази даних не був заповнений?

Тому що БД залежить від інших шляхів: директорій логів, tmp, сокетів, PID-файлів і іноді артефактів відновлення.
Заповнений root може зламати БД, навіть якщо datadir знаходиться на окремому монту.

4) Чи варто перезапускати БД повторно, поки вона не підніметься?

Ні. Повторні перезапуски можуть «трясти» відновлення і генерувати більше записів (і більше логів), коли у вас мало місця.
Спочатку звільніть простір, потім стартуйте один раз і дайте відновленню завершитися.

5) Скільки вільного місця «достатньо» перед перезапуском?

Достатньо для завершення відновлення і витримки стрибка нормальних записів. У продакшні я орієнтуюсь на 10–20% вільного на відповідних файлових системах.
Якщо не можете досягти цього — розширюйте сховище або перемістіть навантаження на репліку, а не ризикуйте.

6) Який найнадійніший спосіб негайно звільнити місце?

Vacuum systemd journals, очистити кеш пакетів, видалити відомі безпечні кеші і обернути/обрізати runaway логи додатків.
Уникайте торкання файлів бази даних, якщо ви не слідуєте перевіреній процедурі для конкретного рушія.

7) Що робити, якщо у мене закінчилися іноди?

Видалення кількох великих файлів не допоможе. Потрібно видалити багато дрібних файлів (часто кеши) або перемістити це навантаження з файлової системи.
Довгостроково: відокремте кеші від root і виберіть параметри файлової системи, що відповідають вашому числу файлів.

8) Чи означає резервовані блоки ext4, що я можу звільнити аварійний простір?

Резервовані блоки існують, щоб root міг функціонувати, коли користувачі заповнили диск. Ви можете змінити їх через tune2fs,
але змінювати резерви під час інциденту рідко є найкращим першим кроком. Краще звільнити реальний простір або розширити файлову систему.

9) Чи може заповнений диск спричинити тиху корупцію даних?

Заповнений диск зазвичай викликає гучні помилки (ENOSPC), але він також може поставити вашу базу в стан, де потрібне відновлення і перевірка консистентності.
Якщо підозрюєте корупцію — особливо при наявності I/O помилок у dmesg — віддайте перевагу відновленню/промоції репліки замість «продовжувати перезапускати і сподіватися».

10) Як запобігти цьому в майбутньому?

Алертуйте на блоки і іноди, впровадьте обмеження на логи (journald і додатки), розміщуйте тимчасові шляхи на томах з запасом і практикуйте промоцію/відновлення.
Запобігання — це в основному нудні налаштування — і нудність дешевша за простій.

Висновок: наступні кроки, які можна виконати сьогодні

Інциденти з повним диском непоказні, але чесні: вони виявляють, чи система експлуатується з запасами, захисними засобами і відпрацьованими шляхами відновлення.
Якщо це щойно сталося у вас на Debian 13, ваша пріоритетна мета — завершити верифіковано-добрий стан, а не просто «сервіс працює».

Зробіть ці кроки в такому порядку:

  1. Встановіть жорсткі ліміти для логів (journald + logrotate) і підтвердіть, що вони працюють під навантаженням.
  2. Додайте алерти на іноди і deleted-open файли (або принаймні зробіть lsof +L1 частиною вашої пам’яті на дзвінки).
  3. Перемістіть залежності БД з root, де це доречно: tmp dirs, логи великого обсягу, шари контейнерів.
  4. Визначте політику «мінімального вільного місця» для томів БД (10–20% — розумна відправна точка) і впровадьте її через алерти і план ємності.
  5. Практикуйте шлях відновлення (відновлення з бекапу або промоція репліки). Якщо ви робите це тільки під час збою — це не план, а авантюра.

Найкращий результат інциденту з повним диском — не просто те, що ви відновилися. Найкращий результат — що ви тепер маєте дисципліну в системі, щоб наступний раз це не стало інцидентом.

← Попередня
VPN підключено, але немає інтернету в Windows: контрольний список маршрутів і метрик
Наступна →
Міграція сховища з ESXi в Proxmox: переміщення VMFS на ZFS, NFS або iSCSI з мінімальним простоєм

Залишити коментар