Освобождаем место на диске после docker

Я большой фанат docker. Уже более чем 2 года я уверен в том, что этота технология изменит способ развертывания приложений. Не только web-приложений, но и инструментов командной строки, а, возможно, и графических приложений. Но такая точка зрения не отменяет кучи недостатков, с которыми я периодически сталкиваюсь.

Один из основных неприятных моментов, с которыми мне приходится постоянно сталкиваться, — это загромождение свободного места на диске. До недавнего времени я постоянно сталкивался с проблемами настройки окружений, потому что мне постоянно не хватало места на жестком диске, и я не мог понять, куда оно девается. Ниже я приведу несколько советов, как избежать хранения неиспользуемых docker-образов и предотвратить их появление.

Как свести занимаемое на диске место к минимуму

Во-первых, docker по-умолчанию не беспокоится о том, сколько места на диске занимают необходимые ему данные. Большинсво команд оставляют за собой кучу следов, копиируя или изменяя текущие данные образа и не удаляя предыдущую версию. Давайте рассмотрим наиболее популярные:

  • Команды docker pull и docker build создают новые образы. Каждое изменение создает новый слой и кешируется, использует aufs для хранения, таким образом увеличивая занимаемое на диске место. Но при этом предыдущая версия/слой никуда не удаляются, и остаются болтаться бесполезным грузом.

    Удалить такие неиспользуемые образы, у которых нет метки, можно выполнив в консоли:

    docker images --no-trunc | grep '<none>' | awk '{ print $3 }' | xargs -r docker rmi
    
  • Команда docker run оставляет запускавшийся контейнер в том состоянии, в котором он был остановлен. Это удобно, если вам понадобится просмотреть результат позже — проверить логи, или статус завершения.

    Но, в рамках занимаемого места на диске, это может быть дорого, особенно в ходе тестирования. Не забывайте использовать флаг docker run --rm, если контейнер, в котором выполняется процесс, вам больше не понадобится. Этот флаг не работает для контейнеров, запущенных в фоне (-d), так что в этом случае от следов запуска docker никуда не деться. Вычистить такие “мертвые” контейнеры поможет следующая команда:

    docker ps --filter status=dead --filter status=exited -aq | xargs docker rm -v
    
  • Команда docker rm не удаляет тома, созданные контейнером. Мне не понятно, почему не сделать это поведением по-умолчанию, но на данный момент для удаления контейнеров и связанных с ними томов необходимо использовать флаг -v.

Файловая система для хранения docker-томов

Docker для хранения файлов использует три способа:

  • По-умолчанию, все, что вы сохраняете на диск внутри контейнера, сохраняется как aufs-слой. Это не создает проблем, если вы регулярно удаляете устаревшие и неиспользуемые контейнеры и образы.
  • Если вы монтируете директорию с хостовой ОС (используя docker run -v /host/path:/container/path ...), то файлы сохраняются в файловой системе хостовой ОС, за ними не сложно следить и так же нет никаких проблем.
  • Третий вариант — docker-тома (volumes). Они представляют собой особые пути, которые указывают на определенную директорию в /var/lib/docker/volumes/ хостовой ОС. Многие образы используют тома для организации общего доступа к файлам между контейнерами (используя volumes-from опцию) или для постоянного хранения данных, даже после завершения работы оснвного процесса.

А так, как нет инструмента, который бы отображал список томов и их состояние, очень просто забыть о них, оставив на диске после завершения работы основного процесса или даже после удаления связанных контейнеров. Следующая команда проверяет все контейнеры (запущенные или нет) и сравнивает их с созданными томами, выводя пути только к тем томам, на которые не ссылается ни один контейнер.

#!/usr/bin/env bash

find '/var/lib/docker/volumes/' -mindepth 1 -maxdepth 1 -type d | grep -vFf <(
  docker ps -aq | xargs docker inspect | jq -r '.[]|.Mounts|.[]|.Name|select(.)'
)

Пошаговое выполнение этой команды включает в себя:

  • Выводит список всех созданных томов
  • Выводит список всех контейнеров и проверяет их, создавая JSON-массив со всеми записями
  • Форматирует вывод используя jq чтобы получить имена всех смонтированных томов
  • Исключает (grep -vFf) смонтированные тома из списка всех созданных томов

Запускать этот скрипт необходимо с правами суперпользователя, и в системе должна быть установлена jq утилита

Команда ничего не удаляет, но направив результаты на xargs -r rm -fr можно добавить эту функцию.

Заметка: docker 1.9 имеет новую систему управления томами, так что в этой версии все можно сделать проще:

docker volume ls -qf dangling=true | xargs -r docker volume rm

Итоги

Для очистки всех побочных продуктов работы с docker, сохраните себе следующий скрипт:

https://gist.github.com/mlebkowski/471d2731176fb11e81aa

#!/bin/bash
# удаляем завершенные контейнеры:
docker ps --filter status=dead --filter status=exited -aq | xargs -r docker rm -v
# удаляем неиспользуемые образы:
docker images --no-trunc | grep '<none>' | awk '{ print $3 }' | xargs -r docker rmi
# удаляем неиспользуемые тома (необходимы права суперпользователя):
find '/var/lib/docker/volumes/' -mindepth 1 -maxdepth 1 -type d | grep -vFf <(
  docker ps -aq | xargs docker inspect | jq -r '.[]|.Mounts|.[]|.Name|select(.)'
) | xargs -r rm -fr

Такое “техническое обслуживание” должно удалить весь ненужный мусор из файловой системы и оставить в docker-окружении только актуальные контейнеры, образы и тома.