Файл удалён, а место не освободилось: открытые файлы в Linux
На сервере закончилось место. В каталоге журналов нашли старый файл размером в десятки гигабайт и удалили его. Каталог стал маленьким, а запись в базу всё ещё завершается ошибкой нехватки места. Повторное удаление других файлов может только усложнить разбор: первый файл вполне способен продолжать существовать для работающего процесса.
Имя в каталоге и открытый файл связаны, но живут не одинаково. Когда программа открывает лог, она получает файловый дескриптор. Удаление имени убирает ссылку из каталога. Если это была последняя жёсткая ссылка, но файл ещё открыт, его содержимое сохраняется до закрытия последнего удерживающего дескриптора. На локальной Linux-файловой системе это нормальная семантика unlink.
Сначала сравним один и тот же раздел
df -h /var/log
df -i /var/log
sudo du -x -h --max-depth=1 /var/log
df показывает состояние файловой системы, содержащей указанный путь. du обходит доступные имена и суммирует занятое ими место. Опция -x удерживает обход в пределах одной файловой системы. Поэтому большой разрыв между ними даёт повод искать объекты, которые уже нельзя найти обычным обходом каталога.
Сравнение имеет смысл только для согласованных границ. Если /var/log находится на общем корневом разделе, df учитывает также данные вне этого каталога. Удалённые открытые файлы — одна из причин разрыва, но не единственная. Снимки файловой системы, недоступные каталоги и резервирование пространства также могут влиять на картину. Сообщения du о недостатке прав нельзя молча считать нулевым размером.
Отдельный запрос df -i проверяет запас inode. Когда закончились именно они, создание нового файла может ломаться при оставшихся свободных гигабайтах. Освобождение одного большого файла почти не изменит количество inode, поэтому важно различить эти два ограничения до вмешательства.
Найдём владельца удалённого файла
sudo lsof +L1
Условие +L1 выбирает открытые файлы с числом ссылок меньше единицы. В результате интересуют процесс, PID, дескриптор FD, устройство, размер и прежний путь. Нужны строки, относящиеся к заполненному разделу. Не складывайте размеры одинакового inode из разных строк: один файл могут держать несколько процессов или дескрипторов.
Предположим, строка указывает процесс с PID 1234 и FD 7w, а имя заканчивается на (deleted). Буква описывает режим открытия; в пути proc нужен только номер 7. Прочитаем ссылку и метаданные:
sudo readlink /proc/1234/fd/7 sudo stat -L /proc/1234/fd/7
Числа 1234 и 7 здесь нужно заменить результатами своего lsof. Символическая ссылка показывает объект, удерживаемый процессом, а stat -L следует к этому объекту. Логический размер файла и реально выделенные блоки могут различаться, особенно для разреженных файлов. Поэтому объём ожидаемого освобождения оценивают по фактическому размещению, а затем подтверждают через df.
Освободим объект через его владельца
Для журнала разумный следующий шаг — найти поддерживаемый приложением способ переоткрыть логи. У разных программ это команда, сигнал или часть процедуры ротации. Отправлять сигнал наугад нельзя: обработчик сигнала определяет само приложение, а стандартное действие может завершить процесс.
Если переоткрытие не поддерживается, планируют штатный перезапуск владельца с учётом доступности сервиса. Сам перезапуск не обязан освободить весь объём: другой процесс может держать тот же объект. После действия снова выполните lsof +L1 и df для того же пути. Исчезновение всех ссылок на нужный inode и рост свободного пространства подтверждают результат.
Прямое обнуление файла через /proc/PID/fd/N меняет данные живого процесса. Для базы данных или неизвестного файла это особенно опасное действие, поэтому диагностический маршрут выше ограничивается чтением. Даже для лога обнуление может изменить положение записи и дальнейшее выделение блоков. Сначала определите владельца и его штатный протокол работы с файлом.
Почему ситуация повторяется после ротации
Если ротация переименовала старый лог и создала новый, процесс может продолжать писать через прежний дескриптор. Удаление старого имени не заставляет его открыть новый путь. Проверьте настройку ротации вместе с командой переоткрытия и правами на новый файл. Наблюдение за inode до и после ротации покажет, куда действительно продолжается запись.
В результате аварийное освобождение места превращается в понятную проверку жизненного цикла файла: какое имя исчезло, кто удержал объект и когда закрыл последнюю ссылку. Это помогает исправить причину заполнения раздела и сохранить нужные сведения об инциденте.
Ещё по теме
- EnvironmentFile в systemd: почему переменная работает в терминале, но не в сервисе
- Почему set -e не остановил Bash-функцию внутри if
Продолжить изучение
Хотите перейти от отдельных команд к сопровождению Linux-серверов? Работа с системой, скриптами и сервисами входит в программу «Linux и Bash для DevOps-инженеров».
Продолжить на практике
- Linux и Bash для DevOps-инженеров ↗
Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.
