MAATRIX / Блог / Что такое inode и почему место есть, а файл не создаётся

Что такое inode и почему место есть, а файл не создаётся

MAATRIX

Знакомая ситуация: 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 ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →