Сегодня я проснулся по-раньше и решил поработать. Но когда я сел за компьютер, то оказалось, что весь системный диск забит! Об этом мне сообщила операционная система в виде окошка, а также вызов многих команд:
alias-finder:15: write failed: На устройстве не осталось свободного места
alias-finder:16: write failed: На устройстве не осталось свободного места
Зачистка
Я запустил баобаб:
sudo baobab
И увидел, что один из каталогов докера весит 45 Гб! Надо было скопировать путь к этому каталогу, чтобы дальше понять, что это: журнал или контейнер, и какой именно сервис. Но я этого не сделал. Зато сделал скриншот, который хотел использовать в дальнейшем расследовании.
Теперь нужно очистить диск. Судорожная попытка очистить /tmp и кеш apt практически ничего не дала. Докер продолжал забивать диск, и я наблюдал, как пропадают только что освобожденные мегабайты.
docker system prune
Практически нулевой результат. Я полез в интернет с вопросом: как по хешу определить, что это за докер контейнер. И прочитал довольного много интересного.
Docuum
Хорошее решение по очистке диска после докера. Как написано в мане, эта программа делает что-то похожее на
docker image prune --all --filter until=...
Однако, в отличие от встроенной очистки, использует не время создания образа, а время его последнего использования, чтобы определить, какие образы следует удалить.
Docuum is used by Airbnb on its fleet of 1.5k+ CI workers.
Чтобы это работало, программа запускается в режиме демона и получает события с помощью
docker events
Я сразу полез смотреть, что же делает эта не знакомая мне команда. А она пишет события по контейнера и сетям: запуск, остановка, удаление и так далее. Наверное еще и про образы, но я не проверял.
docker events --help
Usage: docker events [OPTIONS]
Get real time events from the server
BuildKit mode
There are now two components in Engine that can be used to build an image. Starting with the 18.09 release, Engine is shipped with Moby BuildKit, the new component for executing your builds by default.
Как-то непонятно написано: он что, включен по-умолчанию?
В общем, нужно обязательно разобраться с этим режимом. Будет полезно, когда я начну делать CI/CD цепочку на своем сервере.
На первый взгляд из вкусного:
--add-host strings Add a custom host-to-IP mapping (format: "host:ip")
Что-то вроде /etc/hosts для сборки.
--build-arg stringArray Set build-time variables
--build-context stringArray Additional build contexts (e.g., name=path)
Полезно, чтобы задать какие-то опции. Например, отдельно собирать образ для тестирования.
Зачистка: вторая попытка
Так как докер постоянно писал на диск, нужно было прекратить эту вакханалию! Все проекты подняты через docker-compose, и есть Makefile для управления этой командой. При запуске
make down
Система сообщила, что места нет, и выполнить команду не может. Я растерялся. Но потом вспомнил, что root пользователь имеет еще 5-10% свободного диска — это зарезервировано системой специально для таких случаев.
Я запустил снова команду make down, но уже под рутом, и сервис остановился. То же самое я проделал для всех остальных сервисов. Внезапно место освободилось.
Пост-фактум по хешу, который я зашотил, уже нельзя понять, что это было.
Как по хешу определить докер образ
Итак, какой-то специальной команды для этого нет. Хотя было бы полезно ее сделать. На SO предлагают поискать таким способом:
docker inspect c777cf06a6e3 | grep a1b2809
Разумеется, запущенных контейнеров может быть очень много. Поэтому их все нужно обойти в цикле.
Как-нибудь нужно отыскать какое-то готовое решение для этого или написать свое.
UPD. Кролик тоже кушает
После написания поста прочитал, что RabbitMQ при запуске в контейнере использует hostname как название каталога для хранения данных. Соответственно каждый рестарт docker-compose меняет hostname, что плодит однотипные каталоги:
Проблема лечится просто — добавить в docker-compose.yml строку:
hostname: rabbitmq
Я припоминаю, как регулярно грохаю через baobab каталоги mnesia от кучи микросервисов на своей локальной машине из-за разрастания их размеров mnesia. Возможно, это решит эту проблему.
UPD 2. Проблема снова повторилась
На этот раз причина была другая. Я нашел, что один из каталогов весит 16 Гб:
/var/lib/docker/overlay2/d58861045a9add33f7820998ea606c53228af366e4c50d647c67030254dc4a4a
Потом на SO нашел способ, как найти по этому названию контейнер:
docker inspect $(docker ps -qa) | jq -r 'map([.Name, .GraphDriver.Data.MergedDir]) | .[] | "\(.[0])\t\(.[1])"' | grep d58861045a9add33f7820998ea606c53228af366e4c50d647c67030254dc4a4a
# вывод:
/us-form-back-omni_kafka_1
Получилось, что это контейнер Кафки. Покопавшись еще, я выяснил, что внутри контейнера копится журнал в каталоге /opt/kafka_2.13-2.8.1/logs
После остановки контейнера место было освобождено.
UPD 3. Нажел огромный файл с журналом
Установил на сервер ncdu — аналог baobab, только в текстовом режиме. Решил посмотреть, что находится на диске и кушает много места. И с удивлением нашел в /var/lib/docker/containers/… JSON-файл размером в 18 Гб! Как потом выяснилось, это журнал контейнера Гитлабы. Сам контейнер работает начиная с 2023-09-16 — это я узнал из самого журнала.
А вот команда, которая покажет все размеры всех журналов:
du -ch /var/lib/docker/containers/*/*-json.log
В сумме вышло на 29 Гб.
После изучения этой дискуссии я добавил в docker-compose.yml следующее:
logging:
driver: json-file
options:
max-size: 10m
После чего через «up» перезапустил сервис Гитлаб. Старый контейнер был удален. Будем посмотреть за размерами журнала.