Диск забился на 100%, сервис падает, а в логах «No space left on device». Ниже набор команд, которые помогают быстро найти, что именно съело место, и скрипт, который собирает всё это за один запуск.
Команды в этой заметке взяты из телеграм-канала srv_admin.
1. Общая картина
# df -h
Смотрим, какая точка монтирования заполнена. Дальше работаем уже с ней.
2. Что занимает место
Размер директорий первого уровня:
# du -h -d 1 / | sort -hr
# du -hs /* | sort -hr
Ограничить вывод десятком самых больших:
# du -h -d 1 / | sort -hr | head -10
Топ 20 самых больших директорий (-x не уходит на другие файловые системы, 2>/dev/null прячет ошибки доступа):
# du -hcx --max-depth=6 / 2>/dev/null | sort -rh | head -n 20
Топ 20 самых больших файлов:
# find / -mount -ignore_readdir_race -type f -exec du -h "{}" + 2>&1 | sort -rh | head -n 20
Топ 20 самых больших файлов, которым больше 30 дней. Обычно это старые логи, дампы и архивы, которые можно удалять:
# find / -mount -ignore_readdir_race -type f -mtime +30 -exec du -h "{}" + 2>&1 | sort -rh | head -20
-mount не даёт find заходить в другие файловые системы, поэтому /proc, /sys и сетевые шары не попадут в вывод. Для проверки другого раздела подставьте его точку монтирования вместо /.
3. Проверяем inodes
Иногда место «заканчивается» не по байтам, а по inodes. Так бывает, когда в каталоге миллионы мелких файлов: сессии PHP, кэш, очередь почты.
# df -ih
Если IUse% равен 100%, а по df -h место есть, ищите каталог с огромным числом файлов.
4. Удалённые, но не освобождённые файлы
Бывает, что df показывает занятое место, а сумма du по директориям заметно меньше. Скорее всего, файл удалили, но его всё ещё держит открытым какой-то процесс. Место не освободится, пока процесс не закроет файл. В du такой файл не виден, зато его покажет lsof:
# lsof | grep '(deleted)'
# lsof +L1
Если среди результатов есть файлы большого размера, посмотрите, какая служба их держит, и перезапустите её. Обычно этого достаточно. Если служба зависла, остаётся принудительно завершить процесс:
# kill -9 <pid>
Это крайняя мера, сначала попробуйте обычный systemctl restart.
5. Всё вместе одним скриптом
Скрипт запускает те же проверки подряд. Сохраните его, например, как diskcheck.sh, и запускайте от root, иначе du и lsof увидят не всё.
#!/usr/bin/env bash
# Быстрая диагностика: куда делось место на диске.
# Использование: ./diskcheck.sh [точка_монтирования] (по умолчанию /)
set -u
TARGET="${1:-/}"
echo "=== Использование дисков ==="
df -h
echo
echo "=== Inodes ==="
df -ih
echo
echo "=== Топ 20 директорий в ${TARGET} ==="
du -hcx --max-depth=6 "$TARGET" 2>/dev/null | sort -rh | head -n 20
echo
echo "=== Топ 20 файлов в ${TARGET} ==="
find "$TARGET" -mount -ignore_readdir_race -type f -exec du -h "{}" + 2>/dev/null | sort -rh | head -n 20
echo
echo "=== Топ 20 файлов старше 30 дней ==="
find "$TARGET" -mount -ignore_readdir_race -type f -mtime +30 -exec du -h "{}" + 2>/dev/null | sort -rh | head -n 20
echo
echo "=== Удалённые файлы, которые держат процессы ==="
lsof +L1 2>/dev/null | head -n 30
Если нужно только предупреждение о заполнении диска, например из cron, хватит короткой проверки порога:
#!/usr/bin/env bash
# Печатает предупреждение, если какая-то файловая система занята больше THRESHOLD %.
THRESHOLD=90
df -hP -x tmpfs -x devtmpfs | awk -v t="$THRESHOLD" 'NR>1 {
use = $5; sub(/%/, "", use)
if (use+0 >= t) printf "ВНИМАНИЕ: %s занят на %s%% (%s)\n", $6, use, $1
}'
Если скрипт что-то напечатал, cron отправит вывод на почту пользователя. Так о нехватке места узнаете до того, как упадёт сервис.