Нехватка места на диске и исчерпание inode¶
У «No space left on device» бывает несколько причин. Файловая система может быть заполнена блоками данных, может исчерпать inode или держать место за файлами, которые удалены, но всё ещё открыты. Для каждого случая своё лекарство, а df -h в одиночку показывает только первый.
О выводе на этой странице
Примеры с inode сняты при реальном запуске на Ubuntu 22.04: крошечная tmpfs с намеренно малым лимитом inode, смонтированная внутри непривилегированного user namespace (unshare -rm), так что хост не затрагивался. Пути монтирования сокращены до /data. Пример с удалёнными, но открытыми файлами — иллюстративный.
Как это устроено¶
Что такое inode¶
Inode хранит метаданные файла (владелец, права, время, размер, расположение блоков данных). Имя файла находится в записи каталога, которая указывает на номер inode. Каждый файл, каталог и символическая ссылка занимают один inode. В ext4 число inode фиксируется при создании файловой системы (mkfs.ext4 -i <байт-на-inode> или -N <количество>), по умолчанию один inode на 16 КиБ места. XFS выделяет inode динамически, поэтому они почти не заканчиваются, хотя возможен упор в лимит пространства под inode (imaxpct). У Btrfs нет фиксированной таблицы inode.
Значит, файловая система с миллионами мелких файлов (очереди почты, файлы сессий PHP, кэши, слои контейнеров) может исчерпать inode, когда большинство гигабайт ещё свободно.
Как отличить одно от другого¶
Реальный снимок случая с inode (tmpfs на 10 МБ с лимитом 200 inode, заполненная мелкими файлами):
$ df -i .
Filesystem Inodes IUsed IFree IUse% Mounted on
tmpfs 200 189 11 95% /data
$ df -h .
Filesystem Size Used Avail Use% Mounted on
tmpfs 10M 0 10M 0% /data
Место занято на 0%, а inode — на 95%. Когда занят последний inode, создать ничего нельзя, хотя диск пуст:
Текст ошибки тот же, что и при полном диске. Поэтому, если df -h выглядит нормально, всегда проверяйте df -i.
Где закончились inode¶
Посчитайте файлы по каталогам верхнего уровня (хватает coreutils):
-xdev не даёт find заходить в другие точки монтирования. GNU du умеет то же одной командой:
Повторяйте на уровень глубже внутри самого большого каталога, пока не найдёте виновника. Здесь это sessions, и удаление устаревших файлов сессий освободило inode:
$ rm -f sessions/sess_*
$ df -i .
Filesystem Inodes IUsed IFree IUse% Mounted on
tmpfs 200 70 130 35% /data
На реальном сервере выполняйте это для затронутого раздела (/var, /var/lib/docker, /home), начиная с привычных подозреваемых: /var/spool, /var/lib/php/sessions, /tmp, очереди почты, кэши сборки.
Нехватка блоков и поиск крупных объектов¶
sudo du -xh --max-depth=1 /var | sort -rh | head
sudo find /var -xdev -type f -size +500M -exec ls -lh {} +
-x (du) и -xdev (find) остаются в пределах одной файловой системы. sort -rh сортирует читаемые размеры от большего к меньшему.
Место, занятое удалёнными файлами¶
Если df показывает полный диск, а du по всей файловой системе даёт намного меньше, вероятно, какой-то процесс держит открытым удалённый файл. Блоки освобождаются только после закрытия последнего файлового дескриптора:
sudo lsof +L1 /var # открытые файлы с числом ссылок 0 (удалённые)
sudo lsof -nP +L1 | awk '$7 > 100000000' # только крупнее ~100 МБ (колонка 7 — SIZE/OFF)
Типичный случай: rm app.log, пока приложение продолжает писать в него. Решение — перезапустить процесс или послать ему сигнал, чтобы он переоткрыл лог (systemctl reload app или kill -HUP <pid>, если программа это поддерживает). Чтобы освободить место без перезапуска, обрежьте файл через открытый дескриптор: : > /proc/<pid>/fd/<n> (найти <n> можно через ls -l /proc/<pid>/fd). В дальнейшем обрезайте логи (truncate -s 0 file) или используйте logrotate вместо rm.
Зарезервированные блоки (ext4)¶
ext4 по умолчанию резервирует 5% блоков для root, поэтому обычные пользователи упираются в «No space» примерно на 95%, хотя df ещё показывает Avail. Проверка: sudo tune2fs -l /dev/<dev> | grep -i reserved. Уменьшить значение на больших разделах с данными (sudo tune2fs -m 1 /dev/<dev>) безопасно, но на / оставьте значение по умолчанию, чтобы системные службы продолжали работать почти на заполненном диске.
Различия Ubuntu/Debian и RHEL-подобных систем¶
Инструменты одинаковы. Различается файловая система по умолчанию: RHEL-подобные системы используют XFS (inode динамические, поэтому исчерпание по df -i редкость, а XFS нельзя уменьшить), а Ubuntu и Debian — ext4 (фиксированное число inode, поэтому исчерпание реальный риск на разделах с почтой или кэшем). Какая у вас, покажут df -T /path или findmnt -no FSTYPE /path.
Практическое задание¶
Безопасно на любой машине: используется одноразовая tmpfs в вашем собственном пространстве имён монтирования, root не нужен.
mkdir -p ~/inode-lab
unshare -rm bash # новая оболочка в частном mount namespace (нужны включённые user namespaces)
mount -t tmpfs -o size=10m,nr_inodes=64 tmpfs ~/inode-lab
cd ~/inode-lab
df -i .
i=0; while touch f$i 2>/dev/null; do i=$((i+1)); done; echo "created $i files"
touch one-more # завершится ошибкой "No space left on device"
df -h . ; df -i .
rm -f f* # освободить inode
touch one-more && echo ok
Очистка: наберите exit, чтобы выйти из оболочки в namespace; tmpfs исчезнет вместе с ней. Затем rmdir ~/inode-lab. Если unshare отказывает (на усиленных системах непривилегированные user namespaces бывают отключены), используйте запасную ВМ и sudo mount -t tmpfs ..., затем sudo umount.
Как проверить, что всё сделано правильно¶
df -i .сразу после монтирования показывает 64 inode всего и около 1 занятого.- Цикл останавливается на 63 файлах (64 inode минус один, занятый корневым каталогом точки монтирования), а
touch one-moreпечатаетNo space left on device. df -h .в этот момент показывает0%занято: это доказательство, что причина не в нехватке блоков.- После
rm -f f*df -i .показывает много свободных inode, иtouch one-moreпроходит.
Типичные ошибки¶
- Смотреть только
df -hи считать ошибку загадочной. Всегда проверяйте иdf -i. - Запускать
duбез-xпо/и считать/proc,/sysи другие точки монтирования. - Удалять через
rmогромный лог, который держит процесс. Место не вернётся, пока процесс не закроет файл. Обрезайте или ротируйте. - Чистить не тот слой на хостах Docker. Слои контейнеров расходуют inode в
/var/lib/docker. Перед очисткой посмотритеdocker system df. - Создавать файловую систему под миллионы мелких файлов с настройками по умолчанию. Выбирайте
-i/-Nприmkfs(или используйте XFS): число inode в ext4 нельзя увеличить потом без пересоздания.
⚠ Опасные команды¶
rm -rfпо пути из переменной (rm -rf $DIR/*при пустой$DIRпревращается в/*). Берите в кавычки и используйте${DIR:?}:rm -rf -- "${DIR:?}"/*.- Удаление миллионов файлов через
rm -rfна рабочем диске может забить ввод-вывод и остановить сервис. Предпочитайтеfind <dir> -xdev -type f -delete, при необходимости подionice -c3 nice. - Очистка
/var/lib/php/sessionsили кэша при работающем приложении разлогинивает всех. Удаляйте только файлы старше времени жизни сессии:find ... -type f -mmin +1440 -delete. docker system prune -a --volumesудаляет неиспользуемые образы и тома вместе с данными. Сначала выполнитеdocker system dfи никогда не добавляйте--volumesна хосте с базами данных.tune2fs -m 0на корневой файловой системе: полный/может нарушить вход и службы, которым нужно писать на диск.
Связанные статьи¶
- Основы LVM и программный RAID (mdadm): как расширить том, если причина всё-таки в блоках
- Иерархия файловой системы Linux, права и владельцы: что лежит в
/var,/tmpи/home - Диагностика высокой нагрузки CPU и памяти в Linux
Контрольные вопросы¶
- Диск показывает 40% в
df -h, но ничего нельзя создать. Какую команду выполнить следующей? - Почему в ext4 inode могут закончиться, а в XFS — редко?
dfпоказывает 100%, аdu -x /в сумме даёт только 60% диска. Какова вероятная причина и как её найти?- Почему обрезать лог безопаснее, чем удалять его, пока служба работает?
- Какой параметр
findне даёт ему заходить в другие смонтированные файловые системы?