Learning Platform
Глоссарий Troubleshooting
Урок 15.01 · 22 мин
Начальный
dfduinodesdisk usagefilesystemsdata engineering

Почему DE сталкивается с дисками постоянно

Twoй Airflow worker внезапно падает с OSError: [Errno 28] No space left on device. Spark пишет: FileSystemException: Disk quota exceeded. Kafka log directory растёт, и брокер начинает отвергать продьюсеров. Postgres стопится потому что WAL-директория переполнена. Все эти сценарии — про диск, и первая команда, которую ты вводишь в такой ситуации, — df -h.

Дисковое пространство — это не просто «сколько байт свободно». У файловой системы есть два независимых ресурса: блоки данных (физическое место под содержимое файлов) и inodes (метаданные: имя, размер, владелец, указатели на блоки). Можно исчерпать один, не тронув второй, — и это будет ровно та же ошибка «No space left on device», но с совершенно другой причиной.

В этом уроке разберём df и du — две команды, которые отвечают на разные вопросы: «сколько на ФС-уровне» и «сколько внутри конкретной папки».

df: свободное место по mountpoint-ам

df (disk free) показывает использование всех смонтированных файловых систем. Не директорий — именно ФС. Если у тебя /, /home, /data — это три отдельные ФС, и df покажет их три строки.

$ df
Filesystem     1K-blocks      Used Available Use% Mounted on
/dev/nvme0n1p2 488245288 312456712 151012576  68% /
tmpfs            8164532         0   8164532   0% /dev/shm
/dev/nvme0n1p1    523248    137224    386024  27% /boot
/dev/sdb1     1953514496 187654400 1668236096  11% /data

Без флагов df показывает блоки по 1 KiB. Это нечитаемо. Всегда используй -h (human-readable):

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  466G  298G  144G  68% /
tmpfs           7.8G     0  7.8G   0% /dev/shm
/dev/nvme0n1p1  511M  134M  377M  27% /boot
/dev/sdb1       1.9T  179G  1.6T  11% /data
TIP

Флаг -h использует степени 1024 (KiB, MiB, GiB). Если хочешь степени 1000 (как у дисковых вендоров) — флаг -H. Разница на 1 TB диске — около 7%, и она объясняет, почему «купил 2 ТБ, а в системе 1.81».

df -hT: добавить тип ФС

Иногда важно знать, какая именно ФС под mountpoint-ом — ext4, xfs, tmpfs, или сетевая. Флаг -T:

$ df -hT
Filesystem     Type      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4      466G  298G  144G  68% /
tmpfs          tmpfs     7.8G     0  7.8G   0% /dev/shm
/dev/nvme0n1p1 vfat      511M  134M  377M  27% /boot
/dev/sdb1      xfs       1.9T  179G  1.6T  11% /data
nas:/parquet   nfs4       10T  4.2T  5.8T  42% /mnt/lake

tmpfs — это RAM, не диск. Если процесс пишет в /dev/shm или /tmp (на современных дистрибутивах часто tmpfs) — твой объём ограничен RAM, не SSD. nfs4 — сетевая, latency на ней в десятки/сотни раз выше.

df -i: inode-resource (отдельный счётчик)

Файловая система хранит для каждого файла inode — структуру в фиксированной таблице, созданной при mkfs. Сколько inodes доступно — задаётся при форматировании и не растёт автоматически. На ext4 типичное соотношение — один inode на каждые 16 KB места.

Это значит: если на разделе 1 TB и bytes-per-inode=16384, то inodes будет около 61 миллиона. Кажется много? Если у тебя миллиарды мелких лог-файлов (по 1 KB каждый), ты исчерпаешь inodes задолго до байтов.

$ df -i
Filesystem        Inodes   IUsed   IFree IUse% Mounted on
/dev/nvme0n1p2  30924800  892341  30032459  3% /
/dev/sdb1      121634816 60123884 61510932  50% /data

Внимательно посмотри: /data — 50% inode-использования при 11% использования места. Если на этом разделе бесконтрольно создаются мелкие файлы (например, Kafka segment files или Spark _temporary), ты можешь упереться в IUse=100% при свободных 1.5 ТБ места.

Два независимых ресурса: блоки и inodes

Файловая система может исчерпать один ресурс, не тронув второй. Оба дают одну и ту же ошибку 'No space left on device'.

Блоки данныхbytes
что хранятсодержимое файлов
командаdf -h
когда упираетсямало больших файлов
reserve~5% для root
Inodesметаданные
что хранятимя, права, owner, ссылки
командаdf -i
когда упираетсямного мелких файлов
reserveзаложено при mkfs
Inode: что это, сколько их и как заканчиваются

Реальный кейс: «диск пустой, но писать нельзя»

$ df -h /data
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdb1       1.9T  640G  1.3T  34% /data

$ touch /data/test
touch: cannot touch '/data/test': No space left on device

$ df -i /data
Filesystem        Inodes    IUsed   IFree IUse% Mounted on
/dev/sdb1      121634816 121634816      0  100% /data

100% inodes занято — touch не может создать новый файл, потому что для него нет inode-слота. Решение: найти, где живут миллионы мелких файлов. Это обычно: /data/spark_temp, /data/kafka-logs с маленьким segment.bytes, или дикое количество .parquet маленького размера.

$ for d in /data/*/; do echo -n "$d: "; find "$d" 2>/dev/null | wc -l; done | sort -t: -k2 -n
/data/checkpoints/: 4321
/data/parquet/: 89432
/data/spark_temp/: 12489321

Виновник найден: spark_temp забит мусором, который Spark не успел очистить. Удалить — освободить миллионы inodes.

du: размер директорий снизу вверх

du (disk usage) считает размер содержимого, рекурсивно проходя дерево директорий. В отличие от df, du оперирует на уровне путей, не файловых систем.

$ du /var/log
4       /var/log/sysstat
12      /var/log/apt/history.log
156     /var/log/apt
8       /var/log/journal/4e3a.../[email protected]
...
204800  /var/log

Без флагов du печатает размер в KB на каждую под-директорию рекурсивно — много шума. Полезные флаги:

Флаги du, которые DE использует каждый день

Разные комбинации флагов отвечают на разные вопросы.

-hhuman-readable размеры
-sтолько итог (summary)
-d Nmax depth (только N уровней)
--apparent-sizeлогический, не на диске
-lcount hardlinks each time
-xне пересекать mount boundaries

du -sh: размер всей папки одной строкой

$ du -sh /var/log
2.3G    /var/log

$ du -sh /data/parquet
640G    /data/parquet

Это самая частая форма. «Сколько весит вот эта папка?» — du -sh.

du -sh DIR/*: размер каждого подкаталога

$ du -sh /var/log/*
4.0K    /var/log/alternatives.log
156K    /var/log/apt
892M    /var/log/airflow
1.2G    /var/log/journal
12M     /var/log/nginx
240K    /var/log/syslog

Раскладывает по подкаталогам верхнего уровня. Глаз сразу видит виновника — /var/log/airflow и /var/log/journal занимают вместе ~2 ГБ.

du -hd 1: то же, но включая родитель

$ du -hd 1 /var/log
156K    /var/log/apt
892M    /var/log/airflow
1.2G    /var/log/journal
12M     /var/log/nginx
2.3G    /var/log

Разница с du -sh /var/log/*: здесь видна суммарная строка «общий итог по /var/log = 2.3G». Удобно для отчётов.

du + sort -h: топ самых тяжёлых

DE-классика — найти, кто именно жрёт место:

$ du -sh /var/log/* 2>/dev/null | sort -h
4.0K    /var/log/alternatives.log
12M     /var/log/nginx
156K    /var/log/apt
240K    /var/log/syslog
892M    /var/log/airflow
1.2G    /var/log/journal

Флаг -h у sort понимает 4.0K, 892M, 1.2G и сортирует по реальной величине, не лексикографически. Без него 1.2G оказался бы где-то между 12M и 156K.

Хочешь топ-10 самых больших? Добавь | tail -10 или (если нужно от большего к меньшему) | sort -hr | head -10.

$ du -sh /var/log/* 2>/dev/null | sort -hr | head -5
1.2G    /var/log/journal
892M    /var/log/airflow
12M     /var/log/nginx
240K    /var/log/syslog
156K    /var/log/apt
Swap, overcommit и OOM killer: когда памяти нет

df vs du: почему числа не совпадают

Каноничный вопрос на собеседовании Junior DE/SRE: «Запустил df -h / — показывает 80% занято. Сделал du -sh / — выходит, что файлов на ~50% диска. Куда делось 30%?»

Причины расхождения:

Почему df больше du

Несколько типичных причин, в порядке частоты на практике.

1. deleted-but-open fileslsof | grep deleted
2. reserved blocks5% для root + журнал
3. permission deniedзапусти du с sudo
4. sparse filesapparent vs allocated
5. mounted-over filesскрыты mountpoint-ом

Случай №1 — самый частый и коварный. Сценарий: Airflow worker пишет в /var/log/airflow/scheduler.log через logging.FileHandler. Logrotate в полночь делает mv scheduler.log scheduler.log.1 + создаёт новый файл. Но Airflow всё ещё держит файл-дескриптор на старом inode. Logrotate потом удаляет .log.1 — старый inode переходит в state «unlinked». Блоки не освобождаются, пока процесс не закроет дескриптор или не перезапустится.

$ sudo lsof | grep deleted | head
airflow   12345 airflow   3w   REG  254,2  892M  98321 /var/log/airflow/scheduler.log (deleted)

892M место занято — не виден через du, но df его считает. Решение: перезапустить процесс или попросить его reopen логи (kill -HUP для тех, кто понимает).

DE-паттерн: «найди жирные логи по всем сервисам»

Реальная задача: на VM с Airflow + Spark кончается место, нужно быстро понять, чьи логи самые тяжёлые.

$ sudo du -hd 1 /var/log 2>/dev/null | sort -hr | head -10
2.3G    /var/log
1.2G    /var/log/journal
892M    /var/log/airflow
156M    /var/log/spark
24M     /var/log/postgres
12M     /var/log/nginx
8.4M    /var/log/auth.log
240K    /var/log/syslog
156K    /var/log/apt
84K     /var/log/dpkg.log

Лидеры — journal и airflow. Дальше: внутри airflow что именно?

$ sudo du -hd 1 /var/log/airflow | sort -hr | head
892M    /var/log/airflow
412M    /var/log/airflow/scheduler
298M    /var/log/airflow/dag_processor
156M    /var/log/airflow/webserver
20M     /var/log/airflow/triggerer
6.1M    /var/log/airflow/celery

Самый тяжёлый — scheduler. Внутри его — поиск самого жирного файла:

$ sudo find /var/log/airflow/scheduler -type f -printf '%s %p\n' | sort -nr | head -5
345678901 /var/log/airflow/scheduler/scheduler.log
67890123  /var/log/airflow/scheduler/scheduler.log.1
12345678  /var/log/airflow/scheduler/scheduler.log.2
...

Найден огромный файл — теперь решение: настроить ротацию, ограничить уровень логов через AIRFLOW__LOGGING__LOGGING_LEVEL=WARNING, или почистить старые.

Подробнее про cleanup-стратегии — в уроке 03-find-by-size-and-cleanup. Управление systemd-журналом — в модуле 14-systemd-services.

Попробуй сам

  1. Сколько свободно на твоём корневом разделе:
    df -h /
  2. Посмотри тип твоих ФС:
    df -hT
  3. Сколько inodes ты использовал:
    df -i /
  4. Размер твоей home-директории:
    du -sh ~
  5. Топ-5 самых тяжёлых поддиректорий в /var:
    sudo du -hd 1 /var 2>/dev/null | sort -hr | head -5
  6. Найди deleted-but-open файлы (часто это «потерянное» место):
    sudo lsof 2>/dev/null | grep '(deleted)' | awk '{print $7, $9, $10}'

macOS-различия

  • df на macOS использует флаг -H (а не -h) для степеней 1000. Флаг -h тоже есть, но он по-другому форматирует. Лучше всегда -h для совместимости.
  • На macOS файловая система APFS поддерживает clones (copy-on-write копии). Из-за этого du может показывать суммарный размер больший, чем реально на диске — потому что физические блоки шарятся между файлами.
  • du -d N на macOS работает, но GNU-вариант --max-depth=N — нет. Используй короткую форму.

Главное

  • df -h — свободно место по ФС-mountpoint-ам. Первая команда при No space left on device.
  • df -hT добавляет тип ФС — отличает tmpfs (RAM) от ext4 (SSD) от nfs (network).
  • df -iinodes. Отдельный ресурс. Может закончиться, оставив сотни ГБ свободного места.
  • du -sh DIR — размер одной папки. du -hd 1 DIR — раскладка по подкаталогам.
  • du DIR/* | sort -h — топ по размеру. Базовый паттерн «найти виновника».
  • df и du могут расходиться: deleted-but-open files (lsof | grep deleted), reserved blocks, permission errors, sparse files.
  • DE-паттерн: du -sh /var/log/* | sort -h находит самый жирный лог за две секунды.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. `df -h /data` показывает 11% использования. `touch /data/test` падает с 'No space left on device'. Что делать?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4