Корневой раздел на нуле - авария с особым характером: она ломает сразу всё понемногу - логи не пишутся, базы падают на транзакциях, SSH отказывает в аутентификации, и при этом система продолжает работать, позволяя чинить себя без перезагрузки. В этой статье - практический маршрут: мгновенное освобождение первых гигабайтов для дыхания, поиск истинного виновника по каталогам, разбор типичных поглотителей места, особая тема журналов journald и профилактика, которая предотвращает повторение.

Симптомы и что ломается при заполнении

Первые признаки знакомы по опыту, но полный список полезен для быстрой диагностики. СУБД пишут ошибки транзакций в лог и не могут зафиксировать записи. Сервисы падают при попытке создать временный файл - ошибка No space left on device. SSH-аутентификация отказывает, потому что PAM не может записать в utmp и wtmp. Команды shell ведут себя странно, потому что история не сохраняется. При этом ping и мониторинг часто зелёные: система жива, ей просто некуда писать.

Диагноз ставится одной командой: df -h показывает использование по смонтированным файловым системам, и корень со ста процентами или близко к тому - подтверждение. Но df не отвечает на главный вопрос - где именно лежит занятое место, и следующий раздел об этом.

Мгновенное освобождение места без перезагрузки

Первые гигабайты находятся за минуты по трём типовым местам. Первое - журналы пакетных менеджеров и утилит: journalctl --vacuum-size=500M сокращает журнал systemd до указанного размера мгновенно, truncate -s 0 обнуляет раздутые логи приложений в /var/log. Второе - кэши пакетного менеджера: apt clean или dnf clean all освобождает сотни мегабайт - гигабайты загруженных пакетов. Третье - временные каталоги: /tmp чистится с осторожностью относительно открытых файлов, а старые файлы кэшей приложений удаляются через find с фильтром по времени.

Уже после первой минуты этих операций система обычно обретает достаточно места для работы, и дальше начинается неторопливый поиск истинного виновника. Важное правило этапа: удалять только понятное - системные кэши и журналы, про которые известно, что они восстановимы, - и ничего подозрительного без понимания.

Поиск виновника по каталогам

Инструмент поиска - du в связке с sort: du -xh / --max-depth=2 2>/dev/null | sort -rh | head -20 показывает крупнейшие каталоги двух уровней, и спуск по списку за две-три итерации приводит к источнику. Флаг -x ограничивает обход текущей файловой системой, чтобы соседние монтирования не искажали картину. Для больших каталогов с миллионами файлов процесс занимает минуты, и ускоряется исключением известных тяжёлых путей.

Хороший тон на этом этапе - сразу фиксировать, что именно нашлось, потому что структура потребления меняется и через неделю уже никто не вспомнит, что раздувало раздел. Скриншот или вывод в файл - тридцать секунд, которые сэкономят вечер при разборе повторения.

Типичные поглотители места

Практика разборов сходится в короткий список. Первый - журналы приложений без ротации: сервисы с выводом в собственные файлы без logrotate разрастаются до десятков гигабайт. Второй - старые ядра и пакеты: /boot и кэши пакетного менеджера накапливают версии, которые никто не чистил годами. Третий - дампы аварий и core-файлы, сваленные в рабочие каталоги. Четвёртый - бэкапы, которые скрипт складировал в не предназначенное для них место - классика cron-скриптов с жёстким путём.

Пятый - докер: образы, слои и тома растут тихо, и без периодической чистки система контейнеров съедает терабайты. Шестой - почтовые очереди и spool-каталоги локальной доставки. Знание этого списка ускоряет поиск: чаще всего виновник - в первых трёх пунктах, и проверка их занимает минуты.

Журналы journald как отдельная тема

Постоянный журнал systemd заслуживает отдельного внимания, потому что растёт он неочевидно. По умолчанию без настройки лимитов журнал может разрастись до размера, ограниченного только долей диска. Диагноз - journalctl --disk-usage. Мгновенное лечение - journalctl --vacuum-size=500M, как в разделе выше. Постоянное - в /etc/systemd/journald.conf: SystemMaxUse=500M ограничивает журнал на всегда, и после systemctl restart systemd-journald.conf параметр вступает в силу.

Именно этот пункт стоит проверять на всех новых серверах заранее: журнал без лимита - отложенная бомба с часовым механизмом, и одна строка конфигурации разряжает её навсегда.

Поиск удалённых-но-открытых файлов

Особая категория - место, которое не видно в du, но занято: файлы удалённые, но всё ещё открытые процессами. Удалённый открытый файл освобождает место только после закрытия, и типичный сценарий - гигантский лог, который админ удалил, а место не вернулось, потому что процесс держит дескриптор. Поиск - lsof +L1: вывод показывает удалённые файлы с процессами, держащими их. Лечение - перезапуск процесса, и место возвращается без перезагрузки.

Именно этот сценарий объясняет загадку, когда df показывает сто процентов, а du - вполовину меньше: разница сидит в невидимых удалённых файлах, и для полноценной диагностики обе команды обязаны использоваться вместе.

Профилактика и мониторинг

Предотвращение повторения - короткий список. Алерты на заполнение при 80 и 90 процентах по всем ключевым разделам - базовая гигиена мониторинга, дающая время на реакцию. Лимиты журналов и ротация логов всех приложений настраиваются один раз при вводе сервера в строй. Периодическая чистка кэшей пакетного менеджера и старых ядер - либо по расписанию, либо в регламентных работах. Через все эти меры сценарий этой статьи из аварийного превращается в исключительный.

История из практики

Характерный случай: вечером пятницы мониторинг сообщил о 98% на корне продакшена. Быстрый du показал 40 гигабайт в /var/log/nginx - один access.log без ротации рос месяцами. Первая помощь - truncate, освобождение 40 гигабайт за секунды без перезагрузки и без нарушения работы сервисов. Затем - настройка logrotate по стандартному шаблону, проверка journald на лимит, алерты на заполнение. Общее время - двадцать минут, из которых девятнадцать - это был поиск виновника по du. С тех пор на всех новых серверах logrotate проверяется в чек-листе первого дня.

Памятка

Порядок всей темы: df для диагноза; мгновенная помощь через vacuum, clean и truncate; du по слоям до виновника; lsof +L1 для невидимого места; лимиты и ротация на будущее; алерты на 80 процентов. От алерта до полной нормализации - тридцать-сорок минут без единой перезагрузки, и в этом весь смысл: заполненный корень - одна из немногих тяжёлых аварий, которую можно устранить целиком по живой системе, если знать куда смотреть.
Чистка контейнеров и образов Docker

Отдельного разговора заслуживает Docker, потому что его потребление места живёт по своим правилам и растёт незаметно для du по обычным каталогам. Диагноз - docker system df: сводка по образам, контейнерам, томам и кэшу сборки с их размерами. Освобождение одной командой - docker system prune с флагами по потребности: без флагов чистит остановленные контейнеры и висячие образы, с -a - все неиспользуемые образы, с volumes - осиротевшие тома.

Практические предупреждения: prune с volumes удаляет данные томов, и флаг добавляется только осознанно; перекачанные каждый раз образы в CI - отдельная тема, где лечением становится registry-кэш и чистка по расписанию через cron. На сборочных серверах объём кэша слоёв иногда измеряется терабайтами, и без еженедельного prune раздел кончается с завидной стабильностью.

Опасные ошибки при освобождении

Ошибки на этом этапе воспроизводятся из лучших побуждений, поэтому обзор их - часть техники безопасности. Первая - удаление файлов, занятых процессами: rm огромного лога не возвращает место, пока процесс не перезапущен, и правильный ход - truncate или обнуление через перенаправление, которые работают мгновенно. Вторая - рекурсивная чистка по маске из корня: find с размытым критерием за секунды находит лишнее, поэтому все find-команды вживую прогоняются сначала без -delete.

Третья - чистка /boot ручным удалением файлов вместо пакетного менеджера: система после этого грузится через раз, и правильный путь - удаление старых ядер средствами пакетной системы. Четвёртая - обнуление активных логов базы без координации: СУБД держит дескриптор, и после truncate нужен перезапуск для новых файлов. Все четыре ошибки - из серии "было место, стало хуже", и минута на чтение этого абзаца дешевле каждой из них.

Проверка после освобождения

Завершение работы - короткий контрольный круг. df -h подтверждает освобождение до разумных значений. lsof +L1 - пустой список, значит невидимых файлов не осталось. Сервисы, чинившиеся на ходу, проверяются systemctl --failed и рестартом. Мониторинг сбрасывает алерт. И финал - запись в runbook: что раздувало, сколько освободилось, какая мера постоянная. Без этой записи разбирательство повторится, а с ней инфраструктура получает иммунитет.

Скрипт еженедельной чистки

Там, где парк однороден, имеет смысл собрать скрипт tidy.sh с проверенными командами: vacuum журнала, clean кэшей пакетного менеджера, prune докера, отчёт по du в почту. Скрипт в cron с расшифровкой каждой строки в комментариях превращает чистку из аварийной задачи в фоновую, и главное правило здесь - каждая команда скрипта сначала выполняется вручную и понимается полностью, потому что автоматизация непонятных действий - это просто более быстрый способ ошибиться.

Финальный принцип

Заполненный корень не падает с неба - он зреет неделями по тихим углам, которые никто не проверял. Инженер, который один раз прошёл по этой статье целиком, видит эти углы заранее и закрывает их ротацией и лимитами, а его df по будням остаётся скучным числом около сорока процентов. Сама скучность этого числа и есть высшая похвала системному администрированию.
Связка с общей культурой эксплуатации

Заполненный корень - яркий пример аварии, которая полностью предотвращается культурой, а не героизмом. Серверы с настроенными лимитами журналов, ротацией логов и алертами на заполнение просто не попадают в сценарий этой статьи; серверы без этого набора попадают в него с регулярностью сезонных явлений. Разница между двумя типами серверов создаётся за один вечер настройки, и этот вечер всегда дешевле любой из ночей разбора - хотя бы потому, что после него разбирать попросту нечего.

Строка для памятки

Всё многообразие темы жмётся в пять действий: vacuum и clean для быстрого глотка воздуха, du по слоям для поиска виновника, lsof для невидимого места, ротация и лимиты для вечного покоя, runbook для коллективной памяти. Эти пять шагов выполняются по памяти за сорок минут и не требуют ни перезагрузки, ни даунтайма, ни полночных поездок в стойку.
Нюанс с inodes - отдельная тема

Редкая, но коварная разновидность той же аварии: df показывает свободное место, а система ведёт себя как заполненная. Причина - исчерпание индексных дескрипторов: каждая файловая система держа их ограниченное число, и миллионы мелких файлов выбирают inodes раньше байт. Диагноз - df -i: колонка IUse% на ста процентах при свободном месте в гигабайтах. Лечение - поиск каталогов с гигантским числом файлов: find / -xdev -type d -exec sh -c "echo {} $(ls {} | wc -l)" ; - в топе обычно session-файлы, кэши и почтовые спулы. Профилактика та же, что и для места: мониторинг по обеим колонкам df, потому что inodes кончаются тихо и в самый неподходящий момент.

Именно эта разновидность объясняет, почему зрелый чек-лист мониторинга всегда содержит df -i рядом с df -h: две колонки, два риска, одинаковые последствия при невнимании.
Пусть ваши разделы дышат свободно, а мониторинг приносит только скучные отчёты о норме - это и есть лучшая новость в нашей профессии.

На этом тема закрытого без воздуха корня закрыта окончательно и бесповоротно.