Источник: Nuances of Programming
Общее потребление
Docker великолепен, и в этом нет сомнений. Пару лет назад он представил новый способ сборки, доставки и запуска любых рабочих нагрузок, демократизируя использование контейнеров и чрезвычайно упрощая управление их жизненным циклом.
Такой подход предоставил разработчику возможность запускать любые приложения, не загрязняя локальную машину. Однако, когда мы запускаем контейнеры, развертываем сложные стеки приложений, создаем собственные образы и извлекаем их, потребление памяти в файловой системе хоста может значительно увеличиться.
Если мы не очищали локальную машину в течение некоторого времени, то результаты этой команды могут удивить:
$ docker system df
Эта команда показывает использование диска Docker в нескольких категориях:
- Образы (Images): размер образов, извлеченных из реестра и созданных локально.
- Контейнеры (Containers): дисковое пространство, занимаемое слоями чтения-записи каждого из контейнеров, работающих в системе.
- Локальные тома (Local Volumes): в случае, если хранение осуществляется на хосте, но вне файловой системы контейнера.
- Кэш сборки (Build Cache): кэш, сгенерированный процессом сборки образа (касается BuildKit в Docker 18.09).
Из вывода выше видно, что большое количество дискового пространства можно восстановить. Поскольку оно не используется Docker, его можно вернуть хост-машине.
Использование диска контейнерами
Каждый раз, когда создается контейнер, в папке /var/lib/docker на хост-машине появляется несколько папок и файлов. Среди них:
- Папка /var/lib/docker/containers/ID (ID — уникальный идентификатор контейнера). Если контейнер использует драйвер логгирования по умолчанию, все логи будут сохранены в файле JSON внутри нее. Создание слишком большого количества логов может повлиять на файловую систему хост-машины.
- Папка в /var/lib/docker/overlay2, содержащая слой чтения-записи контейнера (overlay2 является предпочтительным драйвером хранилища в большинстве дистрибутивов Linux). Если контейнер сохраняет данные в своей собственной файловой системе, они будут храниться в /var/lib/docker/overlay2 на хост-машине.
Представим, что у нас есть совершенно новая система, в которой только что был установлен Docker.
Во-первых, запустим контейнер NGINX:
$ docker container run --name www -d -p 8000:80 nginx:1.16
Снова запустив команду df, мы увидим:
- один образ размером 126 Мбайт. Его загрузил NGINX: 1.16, когда мы запустили контейнер;
контейнер www, запускающийся из образа NGINX.
Поскольку контейнер запущен, а образ в данный момент используется, освобождаемого пространства пока нет. Так как его размер (2B) незначителен, и поэтому его нелегко отследить в файловой системе, создадим пустой файл размером 100 Мбайт в файловой системе контейнера. Для этого мы используем удобную команду dd из контейнера www.
$ docker exec -ti www \
dd if=/dev/zero of=test.img bs=1024 count=0 seek=$[1024*100]
Этот файл создается в слое чтения-записи, связанном с этим контейнером. Если мы снова проверим вывод команды df, то увидим, что он теперь занимает некоторое дополнительное дисковое пространство.
Где находится этот файл на хосте? Проверим:
$ find /var/lib/docker -type f -name test.img
/var/lib/docker/overlay2/83f177...630078/merged/test.img
/var/lib/docker/overlay2/83f177...630078/diff/test.img
Не вдаваясь глубоко в детали — этот файл был создан на слое чтения-записи контейнера, который управляется драйвером overlay2. Если мы остановим контейнер, используемое им дисковое пространство станет пригодным для восстановления. Посмотрим:
Как можно восстановить это пространство? Путем удаления контейнера, что приведет к удалению связанного с ним слоя чтения-записи.
Следующие команды позволяют нам удалить все остановленные контейнеры сразу и освободить место на диске, которое они используют:
Из вывода видно, что больше нет места, используемого контейнерами, и, поскольку образ больше не используется (контейнер не работает), пространство, которое он использует в файловой системе хоста, может быть восстановлено:
Примечание: если образ используется хотя бы одним контейнером, занимаемое им дисковое пространство не может быть освобождено.
Подкоманда prune, которую мы применяли выше, удаляет остановленные контейнеры. Если нам нужно удалить все — и запущенные, и остановленные, мы можем использовать одну из следующих команд (обе эквивалентны):
# Старая команда
$ docker rm -f $(docker ps -aq)
# Более современная
$ docker container rm -f $(docker container ls -aq)
Примечание: во многих случаях стоит использовать флаг --rm при запуске контейнера, чтобы он автоматически удалялся при остановке процесса PID 1, тем самым немедленно освобождая неиспользуемое пространство.
Использование диска образами
Пару лет назад несколько сотен Мбайт на образ было нормой. Ubuntu была около 600 Мбайт, а образы Microsoft .Net весили несколько гигабайт (это правда). В то время загрузка только пары таких образов могла сразу повлиять на дисковое пространство хост-машины, даже если слои между ними разделялись. Сегодня это не совсем так — базовые образы намного легче, но через некоторое время их накопление определенно окажет влияние, если не быть осторожным.
Существует несколько видов образов, которые не видны напрямую конечному пользователю:
- Промежуточные — те, что ссылаются на другие (дочерние) и не могут быть удалены.
- Висящие — это те, на которые больше нет ссылок. Они занимают некоторое место на диске и могут быть удалены.
Следующие команды отображают висящие образы в системе:
$ docker image ls -f dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 21e658fe5351 12 minutes ago 71.3MB
Чтобы удалить их, можно пойти долгим путем:
$ docker image rm $(docker image ls -f dangling=true -q)
Или использовать подкоманду prune:
Использование диска кэшем сборки
В релизе Docker 18.09 представлены усовершенствования процесса сборки с помощью BuildKit. Использование этого инструмента позволяет повысить производительность, управление хранилищем, функциональность и безопасность. Мы не будем подробно его описывать, а просто посмотрим, как его включить и как он влияет на использование диска.
Рассмотрим следующее фиктивное приложение Node.Js и связанный с ним Dockerfile:
Файл index.js определяет простой HTTP-сервер, который предоставляет конечную точку ‘/’ и отвечает строкой на каждый полученный запрос:
package.json определяет зависимости — используем expressjs, чтобы настроить HTTP-сервер:
Dockerfile определяет, как построить образ из приведенного выше кода:
Создадим образ как обычно и без включенного BuildKit:
$ docker build -t app:1.0 .
При проверке использования диска мы увидим только базовый (node:13-alpine был загружен в начале сборки) и конечный образ сборки (app: 1.0):
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 2 0 109.3MB 109.3MB (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
Теперь соберем версию образа 2.0 с BuildKit. Нужно просто установить DOCKER_BUILDKIT в 1:
$ DOCKER_BUILDKIT=1 docker build -t app:2.0 .
При повторной проверке использования диска видим, что был создан кэш сборки (Build Cache):
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 2 0 109.3MB 109.3MB (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 11 0 8.949kB 8.949kB
Для его удаления можно выполнить:
$ docker builder prune
WARNING! This will remove all dangling build cache.
Are you sure you want to continue? [y/N] y
Deleted build cache objects:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi
Total reclaimed space: 8.949kB
Total reclaimed space: 8.949kB
Очистка всего и сразу
В приведенных выше примерах каждая из команд контейнера, образа и тома предоставляет подкоманду prune для освобождения дискового пространства. Она доступна на системном уровне Docker, поэтому удаляет все сразу:
$ 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]
Выполнение этой команды время от времени для очистки диска — хорошая привычка.
Читайте также:
Перевод статьи Luc Juggery: Docker Tips: Clean Up Your Local Machine