Сценарий: 3 часа ночи, телефон звонит
PagerDuty присылает: «Airflow scheduler down». Заходишь по SSH:
$ ssh airflow-prod
$ touch /tmp/test
touch: cannot touch '/tmp/test': No space left on device
Диск 100%. Половина сервисов уже легла, потому что не могут писать логи. У тебя есть 15 минут до того, как менеджер начнёт писать в чат «Что происходит?». Этот урок — playbook для такой ситуации.
Главный принцип: диагностируй до того, как что-то удалять. Импульсивное rm -rf /var/log/* может всё сломать ещё больше — например, оставить systemd без места под socket-файлы или сломать privacy logs, которые нужны для audit.
Step 1: подтвердить, что диск действительно полный
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p3 466G 466G 30M 100% /
tmpfs 7.8G 4.0K 7.8G 1% /dev/shm
/dev/sdb1 1.8T 240G 1.6T 14% /data
Корневой раздел / забит. /data (где DE-данные) — в порядке.
Внимание: проверь inodes тоже. Возможно, место есть, но кончились inodes:
$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p3 30924800 30924800 0 100% /
/dev/sdb1 121634816 4892341 116742475 5% /data
Если inodes 100% — стратегия другая: искать миллионы мелких файлов, не гигабайты больших. Подробнее в уроке 01-df-du.
Step 2: найти, какой mountpoint виноват
В нашем случае / забит. Дальше — раскладка по каталогам верхнего уровня:
$ sudo du -hd 1 / 2>/dev/null | sort -hr | head -10
466G /
245G /var
120G /home
45G /usr
12G /opt
8.4G /tmp
1.2G /boot
512M /etc
240M /root
/var лидирует с большим отрывом — типичный паттерн. Идём глубже:
$ sudo du -hd 1 /var 2>/dev/null | sort -hr | head -10
245G /var
198G /var/lib/docker
24G /var/log
8.4G /var/cache
6.1G /var/lib/postgresql
4.2G /var/lib/apt
/var/lib/docker — 198 ГБ. Это классика: Docker image cache + container logs. Обычно его и можно безопасно чистить (см. Step 5).
/var/log — 24 ГБ. Тоже подозрительно много. Идём в обе папки параллельно.
Идём сверху вниз по дереву, каждый раз сужая область до виновника.
Step 3: ghost-файлы (deleted-but-open)
До того как удалять что-то, проверь, не держит ли кто-то «удалённый» файл открытым. Это типичный сценарий: logrotate переименовал файл и сделал rm, но Java/Python пишет в старый дескриптор.
$ sudo lsof 2>/dev/null | grep '(deleted)' | awk '{print $1, $7, $9, $10}'
java 4.5G /var/log/tomcat/catalina.out (deleted)
python 892M /var/log/airflow/scheduler.log.1 (deleted)
Это значит: 4.5 ГБ + 892 МБ = ~5.4 ГБ занято призраками. Никакой find -delete это не уберёт — нужно либо рестартнуть процесс, либо попросить его reopen логи.
Для большинства сервисов работает:
$ sudo systemctl restart tomcat
$ sudo systemctl restart airflow-scheduler
Или (если рестарт нельзя):
# Послать SIGHUP — большинство демонов на него закрывают и открывают логи заново:
$ sudo kill -HUP $(pgrep tomcat)
После рестарта df -h должна показать освобождённое место.
Step 4: цели чистки — типичные виновники
Перечисление по убыванию частоты в production:
Эти пять директорий покрывают 95% случаев. Знаешь их — знаешь disk emergency.
Step 5: cleanup-команды по виновникам
5.1 systemd journal
# Сколько занимает journal:
$ journalctl --disk-usage
Archived and active journals take up 14.3G in the file system.
# Удалить логи старше 7 дней:
$ sudo journalctl --vacuum-time=7d
Deleted archived journal /var/log/journal/.../[email protected] (1.2G).
...
Vacuuming done, freed 11.4G of archived journals from /var/log/journal/.
# Или ограничить размер до 2 ГБ:
$ sudo journalctl --vacuum-size=2G
# Или оставить только последние 50 файлов:
$ sudo journalctl --vacuum-files=50
Чтобы это не повторилось — настроить retention в /etc/systemd/journald.conf:
[Journal]
SystemMaxUse=2G
SystemMaxFileSize=128M
MaxRetentionSec=2week
После правки: sudo systemctl restart systemd-journald. Подробнее про journald — модуль 14-systemd-services.
5.2 Docker
Docker volumes и управление жизненным циклом данных# Сколько занимает Docker:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 84 12 145.2GB 124.8GB (85%)
Containers 45 3 8.4GB 8.1GB (96%)
Local Volumes 23 8 32.4GB 22.1GB (68%)
Build Cache 0 0 12.1GB 12.1GB
# Безопасная чистка: остановленные containers + dangling images + unused networks + build cache:
$ docker system prune
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all dangling build cache
Are you sure you want to continue? [y/N] y
# Агрессивная чистка: ВСЁ неиспользуемое + неиспользуемые images (не только dangling):
$ docker system prune -a
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all images without at least one container associated to them
- all dangling build cache
Are you sure you want to continue? [y/N] y
# С volumes (вне docker system prune по умолчанию):
$ docker system prune -a --volumes
docker system prune -a --volumes удалит все volumes, не используемые в данный момент контейнером. Если у тебя есть persistent data в volume (например, Postgres-данные), но контейнер сейчас остановлен — данные исчезнут. Прежде чем --volumes, проверь docker volume ls и убедись, что важных volumes нет.
5.3 apt cache (Debian/Ubuntu)
# Сколько занимает apt cache:
$ du -sh /var/cache/apt
4.2G /var/cache/apt
# Удалить кэш скачанных .deb:
$ sudo apt clean
# Удалить только .deb, которых уже нет в репозиториях:
$ sudo apt autoclean
# Удалить пакеты, установленные как deps и больше не нужные:
$ sudo apt autoremove
apt clean обычно высвобождает 1-10 ГБ на активной системе.
5.4 Логи приложений (Airflow, Postgres, кастомные)
Если у Airflow логи в /var/log/airflow:
# Проверить, что найдём:
$ sudo find /var/log/airflow -type f -name '*.log' -mtime +7
# Сжать старые:
$ sudo find /var/log/airflow -type f -name '*.log' -mtime +7 -exec gzip {} +
# Удалить совсем старые архивы:
$ sudo find /var/log/airflow -type f -name '*.log.gz' -mtime +90 -delete
См. урок 03-find-by-size-and-cleanup для полного паттерна.
5.5 Core dumps
$ du -sh /var/lib/systemd/coredump
8.4G /var/lib/systemd/coredump
# Удалить старые:
$ sudo journalctl --vacuum-time=1d # это и coredumps зацепит, если systemd-coredump
$ sudo rm /var/lib/systemd/coredump/*.zst # явно
5.6 Старые kernels (Ubuntu/Debian)
# Сколько kernels установлено:
$ dpkg -l linux-image-* | grep '^ii'
# Удалить все, кроме текущего и одного запасного:
$ sudo apt autoremove --purge
Каждый kernel занимает 100-300 МБ в /boot + initramfs. На VM с 500 МБ /boot третий kernel = «No space left on /boot».
Чего НИКОГДА не делать
Эти действия могут сломать систему гораздо сильнее, чем 100% disk.
Резерв: 5% root reserve в ext4
По умолчанию ext4 резервирует 5% места под пользователя root. Это означает: когда обычный юзер видит 100% — у root всё ещё есть запас, чтобы сделать sudo операции (без этого admin не смог бы залогиниться и почистить диск).
Это можно проверить:
$ sudo tune2fs -l /dev/nvme0n1p3 | grep -i reserved
Reserved block count: 5817958
Reserved GID: 0
Reserved UID: 0
5817958 блоков по 4 KB = ~23 ГБ зарезервировано для root.
В extreme cases можно временно уменьшить резерв, чтобы выиграть пространство:
# Установить резерв в 1%:
$ sudo tune2fs -m 1 /dev/nvme0n1p3
5% -> 1% освободит около 4% от размера ФС. На 466 ГБ — это ~18 ГБ. Это «emergency lever»: после чистки верни обратно (tune2fs -m 5).
Real-world recovery: пошаговый сценарий
Реальный кейс из production: VM с Airflow, 100% диска, нужно поднять.
# 1) Проверка
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p3 100G 100G 0 100% /
# 2) Раскладка
$ sudo du -hd 1 / 2>/dev/null | sort -hr | head -5
100G /
56G /var
30G /home
8G /usr
4G /opt
# 3) /var дальше
$ sudo du -hd 1 /var 2>/dev/null | sort -hr | head -5
56G /var
34G /var/lib/docker
18G /var/log
2G /var/cache
1.5G /var/lib/postgresql
# 4) Чистим docker (часто самый большой выигрыш)
$ docker system prune -a -f
Total reclaimed space: 28.4GB
# 5) Чистим journal
$ sudo journalctl --vacuum-time=2d
Vacuuming done, freed 12.1G of archived journals.
# 6) Проверка
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p3 100G 59G 41G 60% /
# 7) Поднимаем сервисы
$ sudo systemctl start airflow-scheduler airflow-webserver
$ systemctl is-active airflow-scheduler
active
41 ГБ выиграли за 5 минут. Сервисы поднялись.
Prevention: чтобы не повторилось
После recovery — настроить, чтобы такое не происходило снова:
- Monitoring и alerts: Prometheus node-exporter + alert на disk usage > 80%. Узнаёшь за неделю до problem, не за 15 минут.
- Log rotation: настроить
logrotateили скриптfind -deleteв cron (модуль15-cron-scheduling). - systemd journal retention:
SystemMaxUse=2Gв/etc/systemd/journald.conf. - Docker pruning по расписанию:
docker system prune -af --volumes=falseраз в неделю в cron. - Apt cleanup:
apt-get autocleanв/etc/apt/apt.conf.d/20auto-upgrades(на Ubuntu обычно уже настроено). - /var/log на отдельном разделе: если приложение бесконтрольно пишет логи, оно сожрёт
/var/log, но не корневую/. Корень останется работоспособным.
Попробуй сам
- Сколько занимает journal на твоей системе:
journalctl --disk-usage - Сколько занимает Docker (если он у тебя установлен):
docker system df 2>/dev/null - Сколько занимает apt cache:
du -sh /var/cache/apt - Топ-5 жирных подкаталогов в
/:sudo du -hd 1 / 2>/dev/null | sort -hr | head -5 - Ghost-файлы (deleted-but-open):
sudo lsof 2>/dev/null | grep '(deleted)' | head
macOS-различия
- macOS не использует systemd -> нет
journalctl. Логи черезlog show,log stream(Apple Unified Logging). - Docker Desktop на macOS использует виртуальную машину (Linux под HyperKit/Virtualization.framework).
/var/lib/dockerнаходится внутри VM, не на host.docker system pruneработает так же. apt clean-> на macOS используетсяbrew cleanup./var/logна macOS заметно меньше, чем на Linux production servers, потому что многие сервисы пишут в Apple Unified Log, не в файлы.
Главное
- Playbook при «диск 100%»:
df -h->df -i->du -hd 1 / | sort -hr-> идти вглубь -> cleanup. - Проверь ghost-файлы:
lsof | grep deleted—duих не видит,dfсчитает. - Топ виновников: Docker (
/var/lib/docker), systemd journal (/var/log/journal), app logs (/var/log/*), apt cache (/var/cache/apt), core dumps. - Безопасные cleanup-команды:
journalctl --vacuum-time=7d/--vacuum-size=2Gdocker system prune -a(--volumes— с осторожностью!)apt clean && apt autoremovefind /var/log/APP -name '*.log' -mtime +14 -delete
- НИКОГДА:
rm -rf /var/log/*,rm -rf /tmp/*,rm -rf /var/lib/docker(без stop daemon). - 5% root reserve в ext4 — emergency lever:
tune2fs -m 1временно. - Prevention: monitoring, retention config, scheduled cleanup, отдельные разделы.