no space left on device в Docker: что занимает место и как его безопасно освободить
Команда KAI Lab · · 8 мин чтения
Ошибка no space left on device значит, что на файловой системе, где Docker хранит данные, закончились свободные блоки или inode: ядро вернуло ENOSPC, и запись не удалась. Чаще всего место съедают старые образы и кэш сборки, остановленные контейнеры, тома и логи контейнеров, которые драйвер json-file по умолчанию не ротирует.
Ошибка приходит с разными префиксами: при docker pull, при docker build, при записи в лог или из самого приложения в контейнере. Заканчивается она одинаково: no space left on device — это текст кода ENOSPC из man 3 errno. Ниже — порядок поиска виновника и безопасная очистка, без rm -rf по каталогам Docker.
Шаг 1. Какая файловая система кончилась
Сначала найдите, где Docker хранит данные, и проверьте и место, и inode:
$ docker info --format '{{.DockerRootDir}}'
/var/lib/docker
$ df -h /var/lib/docker
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 40G 40G 0 100% /
$ df -i /var/lib/docker
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 2621440 412733 2208707 16% /Здесь кончились блоки: Use% 100%. Если бы IUse% был 100% при свободных гигабайтах, закончились бы inode — это разбирается ниже отдельно.
Проверьте и /var/lib/containerd. По документации Docker, при хранилище образов containerd, которое с Docker Engine 29.0 используется по умолчанию на новых установках, образы и слои контейнеров лежат там, а в /var/lib/docker остаются тома и настройки. На системах, обновлённых со старых версий, всё по-прежнему в /var/lib/docker/overlay2.
Шаг 2. Что занимает место внутри Docker
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 47 6 18.62GB 15.93GB (85%)
Containers 9 6 2.114GB 1.406GB (66%)
Local Volumes 14 4 9.87GB 6.2GB (62%)
Build Cache 312 0 11.4GB 11.4GBRECLAIMABLE — сколько можно освободить, не трогая работающие контейнеры. В этом примере больше всего дадут образы и кэш сборки. Подробности по каждому образу, контейнеру и тому показывает docker system df -v. Документация предупреждает, что команда обходит файловые системы всех объектов и на загруженном сервере может работать долго.
docker system df не показывает логи контейнеров, а они часто и есть главный виновник.
Шаг 3. Логи контейнеров
По умолчанию Docker пишет stdout и stderr контейнеров драйвером json-file в /var/lib/docker/containers/ID/ID-json.log. Документация Docker прямо говорит: по умолчанию ротации нет, параметр max-size равен −1, то есть без ограничения. Болтливый контейнер за несколько месяцев пишет десятки гигабайт.
$ sudo du -h /var/lib/docker/containers/*/*-json.log | sort -h | tail -3
1.1G /var/lib/docker/containers/9b2e...41c7/9b2e...41c7-json.log
3.8G /var/lib/docker/containers/51fd...0a9e/51fd...0a9e-json.log
27G /var/lib/docker/containers/3c7a...d18f/3c7a...d18f-json.log
$ docker inspect --format '{{.Name}} {{.LogPath}}' $(docker ps -aq) | grep 3c7a
/importer /var/lib/docker/containers/3c7a...d18f/3c7a...d18f-json.logДокументация предупреждает, что эти файлы предназначены только для демона Docker, и сторонние инструменты могут нарушить работу логирования. Поэтому правильный путь — включить ротацию и пересоздать контейнер: при удалении контейнера удаляется и его лог.
Ротация для всех новых контейнеров задаётся в /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Или драйвер local, который документация рекомендует, чтобы не исчерпать диск: он по умолчанию хранит до 100 МБ логов на контейнер, 5 файлов по 20 МБ, и сжимает их. После изменения перезапустите Docker. Настройка действует только на новые контейнеры, существующие нужно пересоздать, например docker compose up -d --force-recreate. В Compose лимит можно задать и для отдельного сервиса в секции logging.
Шаг 4. Очистка через prune
Docker не удаляет неиспользуемые объекты сам: для каждого типа есть своя команда prune. Порядок от безопасного к рискованному:
$ docker container prune --filter "until=24h"
$ docker image prune -a --filter "until=168h"
$ docker builder prune
$ docker system prunedocker container pruneудаляет остановленные контейнеры. Их слои тоже занимают место.docker image pruneбез флагов удаляет только «висячие» образы без тега. С-a— все образы, которые не использует ни один контейнер. Их придётся скачивать заново при следующем запуске.docker builder pruneчистит кэш сборки. На CI-серверах это часто самый большой пункт.docker system pruneделает всё сразу: остановленные контейнеры, неиспользуемые сети, висячие образы и кэш сборки. Тома по умолчанию не трогает.
С томами осторожнее: в них живут данные. docker volume prune удаляет только анонимные тома, не подключённые ни к одному контейнеру, а docker volume prune --all — и именованные. Перед этим посмотрите docker system df -v и убедитесь, что в томе нет базы, которую просто остановили.
Шаг 5. Контейнер пишет в свой слой
Если приложение пишет временные файлы, кэш или логи в файловую систему контейнера, растёт его записываемый слой. Размер видно в docker ps -s:
$ docker ps -s --format 'table {{.Names}}\t{{.Size}}'
NAMES SIZE
importer 14.2GB (virtual 14.9GB)
api 48.1kB (virtual 212MB)Первое число — слой самого контейнера, в скобках — вместе с образом. Данные, которые должны жить, выносите в том, временные — чистите в самом приложении.
Шаг 6. Закончились inode
Если df -i показывает IUse% 100%, место есть, но создать новый файл нельзя. Виноваты миллионы мелких файлов: кэш, сессии, распакованные зависимости в слоях. Найти каталог-рекордсмен помогает GNU du:
$ sudo du --inodes -x /var/lib/docker 2>/dev/null | sort -n | tail -5Очистка та же — prune и удаление мусора в приложении. Драйвер overlay2, по документации Docker, расходует inode экономнее старых драйверов, но от миллиона файлов в слое не спасает.
Шаг 7. Место не освобождается после удаления
Если после очистки df не изменился, файлы могли удалить, но процессы продолжают держать их открытыми, и место не освободится до закрытия. Проверка — sudo lsof +L1. Подробно этот случай и работа с df, du и inode разобраны в статье закончилось место на диске в Linux.
Если места мало в принципе
Когда /var/lib/docker живёт на маленьком корневом разделе, очистка лишь откладывает проблему. Данные Docker переносят на отдельный диск через параметр data-root:
$ sudo systemctl stop docker
$ sudo rsync -aHAX /var/lib/docker/ /mnt/docker-data/
$ echo '{ "data-root": "/mnt/docker-data" }' | sudo tee /etc/docker/daemon.json
$ sudo systemctl start dockerКоманда tee перезапишет daemon.json целиком: если там уже есть настройки, например ротация логов, добавьте data-root к ним вручную. Для хранилища containerd data-root не действует: его корень задаётся параметром root в /etc/containerd/config.toml. И никогда не удаляйте файлы в /var/lib/docker/overlay2 руками: документация Docker прямо запрещает трогать этот каталог, после такого образы и контейнеры ломаются.
Причины no space left on device: таблица
| Причина | Как узнать | Как исправить |
|---|---|---|
| Старые образы | в docker system df много RECLAIMABLE у Images | docker image prune -a с фильтром until |
| Кэш сборки | большая строка Build Cache | docker builder prune, настройка GC сборщика |
| Остановленные контейнеры | docker ps -a, RECLAIMABLE у Containers | docker container prune |
| Логи без ротации | большие *-json.log в /var/lib/docker/containers | max-size и max-file или драйвер local, пересоздать контейнер |
| Контейнер пишет в свой слой | большой SIZE в docker ps -s | том для данных, очистка временных файлов |
| Забытые тома | RECLAIMABLE у Local Volumes | docker volume prune после проверки содержимого |
| Кончились inode | IUse% 100% в df -i | найти каталог с мелкими файлами, чистить его |
| Удалённые, но открытые файлы | lsof +L1 | перезапустить процесс, который их держит |
| Маленький раздел | /var/lib/docker на корневом разделе | data-root на отдельном диске |
Как не допустить повторения
- Ротация логов для всех контейнеров в
daemon.jsonс первого дня. - Регулярная очистка на CI-раннерах и серверах сборки, например
docker system prune -af --filter "until=72h"по расписанию. - Алерт на заполненность диска и inode задолго до 100%: при 100% падают не только контейнеры, но и сам демон.
- Отдельный раздел под данные Docker, чтобы логи контейнеров не могли заполнить корневую файловую систему.
На нодах Kubernetes до no space left on device обычно не доходит: kubelet начинает выселять поды, когда на основном разделе остаётся меньше 10% места. Как это выглядит, мы разобрали в статье под Evicted. А если контейнер из-за нехватки места падает и поднимается снова, пригодится разбор контейнер Docker постоянно перезапускается.
Частые вопросы
Безопасно ли запускать docker system prune -a? Работающие контейнеры и их образы команда не тронет. Удалятся остановленные контейнеры, неиспользуемые сети, все образы без контейнеров и кэш сборки, а тома останутся, если не добавить --volumes. Риск в другом: остановленный на время контейнер исчезнет, а удалённые образы придётся скачивать заново.
Почему df показывает свободное место, а Docker пишет no space left on device? Три частые причины: кончились inode (df -i), заполнена другая файловая система, например /var/lib/containerd, или место кончилось не на хосте, а на томе или tmpfs, куда пишет приложение внутри контейнера.
Почему после prune место не освободилось? Логи работающих контейнеров prune не трогает. Тома без флагов тоже не удаляются. А файлы, удалённые, пока их держит процесс, занимают место до его перезапуска: проверьте lsof +L1.
Как ограничить логи всех контейнеров сразу? Задать log-driver и log-opts в /etc/docker/daemon.json и перезапустить Docker. Настройка применится к новым контейнерам, существующие нужно пересоздать. Как устроены слои, тома и драйверы логов, часто спрашивают на собеседовании по Docker.
Потренироваться на практике
В симуляторе DevOps KAI Lab есть отдельные месяцы про Linux и Docker: диски, inode, логи и контейнеры разбираются на тикетах от коллег в консоли, которая воспроизводит реальные выводы команд. Начать можно с бесплатного теста навыков DevOps.
Источники
- Docker. Prune unused Docker objects —
pruneдля контейнеров, образов, томов, сетей и кэша сборки. - Docker. docker system df — вывод и предупреждение о нагрузке
-v. - Docker. Configure logging drivers —
json-fileбез ротации по умолчанию, рекомендация драйвераlocal. - Docker. JSON File logging driver и Local file logging driver —
max-size,max-file, предупреждение не трогать файлы логов, 100 МБ по умолчанию уlocal. - Docker. Docker daemon configuration overview — каталог данных,
data-root, хранилище containerd в/var/lib/containerd. - Docker. OverlayFS storage driver —
/var/lib/docker/overlay2, расход inode, запрет трогать каталог вручную. - Docker. Build garbage collection —
docker builder pruneи политики очистки кэша. - errno(3) — ENOSPC «No space left on device»; df(1) — ключи
-hи-i. - Kubernetes. Node-pressure Eviction — пороги диска для выселения подов.