Что такое inode и почему место есть, а файл не создаётся
Знакомая ситуация: df -h показывает 20 гигабайт свободного места, а сервер при этом отказывается создавать новые файлы с ошибкой No space left on device. Первая мысль — «диск же не полный, в чём проблема?» — оказывается неверной, потому что кончилось не место в байтах, а совсем другой ресурс: свободные inode. Разберёмся, что это такое, почему их можно исчерпать при полупустом диске и как быстро найти виновника.
Содержание
Что такое inode на самом деле
В файловых системах вроде ext4 (и большинства других unix-подобных ФС) каждый файл — это не единый объект, а как минимум два: сами данные (содержимое файла, разбитое на блоки на диске) и inode — структура метаданных об этом файле. В inode хранится всё, кроме имени и содержимого: тип файла, права доступа, владелец и группа, размер, время создания/изменения/доступа, количество жёстких ссылок на файл и указатели на блоки данных, где реально лежит содержимое.
Имя файла в этой схеме — не часть inode. Оно живёт в записи каталога (directory entry), которая просто сопоставляет имя с номером inode. Поэтому одна и та же inode может быть видна под разными именами в разных местах — это и есть механизм жёстких ссылок (ln без -s): два имени, один inode, одни данные.
Проверить номер inode конкретного файла можно командой:
ls -i /var/www/app/index.php
stat /var/www/app/index.php
stat выведет размер, права, время доступа и количество ссылок — то есть буквально распечатает содержимое inode этого файла.
Важно понимать: даже файл нулевого размера занимает целую inode. Пустой файл, файл на 10 байт и файл на 10 гигабайт — с точки зрения количества inode это ровно одна штука в каждом случае. Именно это свойство и создаёт описанную ниже проблему.
Почему число inode ограничено заранее
В отличие от места в байтах, которое расходуется постепенно по мере роста файлов, количество inode на классических файловых системах (ext2/ext3/ext4) фиксируется в момент создания файловой системы и не меняется динамически. При форматировании раздела mkfs.ext4 резервирует под таблицу inode фиксированный процент диска, исходя из среднего ожидаемого размера файла (параметр -i — количество байт на одну inode, по умолчанию обычно порядка нескольких КБ на инод, точное значение зависит от версии e2fsprogs и размера раздела).
Логика простая: система заранее закладывает «на диске будет примерно вот столько файлов среднего размера» и резервирует ровно такое количество слотов метаданных. Если реальность отличается — например, на разделе оказывается на порядки больше мелких файлов, чем закладывалось при форматировании, — свободные inode заканчиваются раньше, чем свободные байты.
Посмотреть, сколько inode заложено и использовано на конкретной ФС, можно так:
sudo dumpe2fs -h /dev/sda1 | grep -i inode
Строки Inode count и Free inodes покажут исходный лимит и остаток. Это статическая величина для ext4 — увеличить число inode на уже смонтированном и заполненном разделе без пересоздания файловой системы нельзя (это одно из практических ограничений подхода, и о нём стоит знать заранее, а не постфактум).
Заметим, что не у всех файловых систем так: например, XFS и Btrfs выделяют inode динамически по мере необходимости и в норме не упираются в этот лимит так, как ext4. Если вы выбираете ФС под нагрузку с огромным количеством мелких файлов, это стоит учитывать на этапе разметки — мы разбирали разницу подробнее в статье про выбор файловой системы ext4 против XFS.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPSКлассический симптом: место есть, а записать нельзя
Итог два независимых ресурса файловой системы:
| Ресурс | Что измеряет | Как проверить | Кончается когда |
|---|---|---|---|
| Место в байтах | Объём данных на диске | df -h | Много больших файлов |
| Inode | Количество файловых объектов | df -i | Много мелких файлов |
Проблема в том, что стандартная команда df -h, к которой все привыкли смотреть в первую очередь, ресурс inode вообще не отражает. Она покажет честные свободные гигабайты, пока приложение будет получать отказ на любую попытку создать файл — временный, лог, кэш, сессию — с той же самой ошибкой ENOSPC / No space left on device, которую система выдаёт и при нехватке байтов.
Отличить один случай от другого по одной только ошибке в логе приложения невозможно — сообщение идентично. Разница видна только если явно посмотреть на использование inode, а не только на место.
Типичная картина, которая должна сразу навести на подозрение: диск заполнен на 40-60% по месту, но веб-сервер, PHP или демон логирования пишут в лог No space left on device. Если бы кончилось именно место, заполнение обычно приближается к 100%. Расхождение — верный признак, что дело в inode. Мы подробно разбирали смежный сценарий в статье не хватает inode при свободном месте — если хотите более широкий обзор причин, стоит заглянуть и туда.
Кто обычно виновник: сессии PHP, кэш, логи
Проблема почти всегда одна и та же по механике: где-то в системе работает процесс, который создаёт огромное количество мелких файлов и не удаляет их вовремя. Самые частые источники на практике:
- Файловые сессии PHP. По умолчанию PHP хранит сессии как отдельные файлы в
/var/lib/php/sessions(путь зависит от дистрибутива). Каждый визит анонимного посетителя без активной сессии создаёт новый файл. Штатная сборка мусора (session.gc_probability/session.gc_maxlifetime) должна их подчищать, но если она отключена, настроена неверно, либо работает через cron-задачу, которая перестала выполняться (например, из-за смены пакетаphp-fpmна что-то, что больше не запускаетphpsessionclean), файлы копятся месяцами. - Кэш приложений и фреймворков. Кэш-бэкенды, которые пишут кэш на файловую систему (а не в Redis/Memcached), — частый источник миллионов мелких файлов: кэш шаблонов, кэш opcache-дампов, кэш thumbnails у CMS, кэш сборки статики.
- Логи с ротацией по файлам, а не по размеру. Приложение, которое на каждое событие создаёт отдельный файл лога вместо дозаписи в один — антипаттерн, но встречается. Ротацию логов в целом мы разбирали в статье про ротацию логов, чтобы не забивался диск — принципы там применимы и к количеству файлов, не только к их суммарному объёму.
- Очереди задач и временные файлы обработки. Системы очередей, которые складывают задания как отдельные файлы в директорию (варианты вроде файловых очередей почты, необработанных webhook-payload'ов), при остановке обработчика продолжают копить входящие файлы.
- Docker и контейнеры с большим количеством слоёв/образов. Хотя это отдельная история, отметим: она тоже способна съедать и место, и inode одновременно — если сталкивались с этим, у нас есть отдельный разбор про Docker, занимающий всё место на диске.
Объединяет все эти сценарии одно: суммарный объём данных может быть небольшим (сессии и мелкие кэш-файлы часто весят считаные байты-килобайты каждый), но количество файлов — исчисляться миллионами, и именно количество, а не объём, съедает лимит inode.
Как проверить: df -i для диагностики
Первый и самый быстрый шаг диагностики — команда, зеркальная привычной df -h, но показывающая inode вместо байтов:
df -i
Пример вывода (числа условные, у вас будут свои):
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 1310720 1310720 0 100% /
Если колонка IUse% показывает 100% (или близко к тому) на разделе, где по df -h полно свободного места, — вот и подтверждение диагноза. Дальше нужно понять, какая директория съела все свободные inode. Здесь одной команды недостаточно — нужно последовательно спускаться по дереву каталогов, считая количество файлов в каждом.
Стоит держать под рукой сразу обе команды и смотреть на них вместе — привычка проверять df -h и df -i одной парой экономит время при любой похожей проблеме на VPS. Общий обзор мониторинга дискового пространства и на что вообще стоит смотреть регулярно есть в статье про настройку мониторинга диска на VPS.
Как найти виновника: find с подсчётом файлов
Дальше — практический поиск директории-виновника. Идея простая: посчитать количество файлов в каждой из top-level директорий и спускаться туда, где число аномально большое.
Быстрый подсчёт файлов по каждой директории первого уровня:
for d in /var/*/; do echo -n "$d: "; find "$d" -type f | wc -l; done
Если подозрение падает на конкретную ветку (например, /var/lib/php или /var/www), сузьте поиск и идите на уровень глубже:
for d in /var/lib/php/*/; do echo -n "$d: "; find "$d" -type f | wc -l; done
Когда виновник — известный кандидат вроде сессий PHP, можно сразу проверить его напрямую:
find /var/lib/php/sessions -type f | wc -l
Если число исчисляется сотнями тысяч или миллионами — это она. Полезно также посмотреть на возраст файлов, чтобы отличить «активные сессии живых пользователей» от «мусор, который никто не чистил месяцами»:
find /var/lib/php/sessions -type f -mtime +7 | wc -l
Это покажет, сколько файлов не менялись больше недели — с высокой вероятностью такие сессии уже неактуальны и их можно смело удалять.
Само удаление старых файлов сессий стоит делать с осторожностью — не rm -rf всей директории на живом проде без проверки, а точечно, по возрасту:
find /var/lib/php/sessions -type f -mtime +7 -delete
Обратите внимание: сама команда find ... -delete на директории с миллионами файлов может занять заметное время и создать ощутимую нагрузку на диск — на проде разумнее выполнять такую очистку в период низкой нагрузки, а не в момент пиковой посещаемости.
После очистки полезно сразу перепроверить df -i, чтобы убедиться, что свободные inode действительно вернулись, а не просто уменьшилось общее число файлов без реального высвобождения (это может случиться, если файл всё ещё открыт каким-то процессом — тогда inode не освобождается до закрытия дескриптора, даже если запись каталога уже удалена).
Если разбор ситуации в целом с местом на диске интересен шире, чем только тема inode, — у нас есть отдельная статья о том, что делать, когда закончилось место на диске VPS, где разобраны и другие типовые причины.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPSНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли увеличить количество inode на уже созданной ext4 без переформатирования?
Нет, для ext4 количество inode фиксируется при создании файловой системы (mkfs.ext4) и не меняется на лету. Единственный надёжный способ изменить лимит — пересоздать файловую систему с другим параметром -i (или воспользоваться -N для явного указания числа inode), что означает полное резервное копирование данных, форматирование и восстановление. На практике проще выбрать изначально файловую систему без такого жёсткого лимита (XFS, Btrfs) под нагрузку с большим количеством мелких файлов.
Почему df -h вообще не предупреждает о нехватке inode?
Потому что это принципиально разные счётчики файловой системы: df -h считает занятые и свободные блоки данных, df -i — занятые и свободные записи в таблице inode. Диск может быть заполнен на 30% по байтам и на 100% по inode одновременно — это два независимых ресурса, и ни один не выводится из другого.
Как понять заранее, что раздел приближается к исчерпанию inode, не дожидаясь ошибки?
Регулярно (например, через cron или систему мониторинга) снимать df -i и следить за трендом IUse%, так же как обычно следят за занятым местом в байтах. Если у вас уже настроен мониторинг диска, стоит добавить в него и эту метрику отдельным пунктом — большинство готовых систем мониторинга её поддерживают, но по умолчанию не всегда включают.
Удаление больших файлов поможет освободить inode?
Нет — большой файл занимает столько же inode, сколько маленький: ровно одну. Освобождение inode напрямую зависит только от количества удалённых файлов, а не от их суммарного размера. Если проблема в inode, нужно избавляться именно от избыточного количества мелких файлов, а не от объёма данных.
Что если удалённые файлы всё ещё держат inode занятым?
Значит какой-то процесс до сих пор держит файл открытым по файловому дескриптору — ядро не освобождает inode, пока последний открытый дескриптор не закроется, даже после того как имя файла убрано из каталога. Найти такие процессы можно через lsof +L1 (файлы с нулевым числом ссылок из каталога, но всё ещё открытые) или lsof | grep deleted. Обычно решает перезапуск виновного процесса (например, php-fpm или веб-сервера).
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →