ADG Оставить заявку
Блог Системное администрирование 5 мин чтения

Zabbix 2.4 и Docker API: авто-обнаружение контейнеров вместо ручного прописывания хостов

Docker сломал классическую модель мониторинга: хосты живут минуты, IP меняется. Настроили LLD в Zabbix 2.4 через Docker API - новый контейнер сам появляется на дашборде.

Контекст момента

Zabbix 2.4 получил улучшенное Low-Level Discovery и поддержку мониторинга через Docker API

Классическая схема мониторинга в Zabbix работала десятилетиями: добавил хост вручную, прописал IP, назначил шаблон - готово. Этот подход отлично работает на стабильной инфраструктуре. Проблема в том, что Docker эту стабильность отменяет.

На одном из наших managed-проектов к этому лету появилось несколько десятков контейнеров. Не оркестрация уровня Kubernetes - просто несколько хостов, на каждом по десятку-полтора контейнеров, Docker Compose в качестве управления. Контейнеры пересоздаются при каждом деплое: новый IP из внутреннего диапазона, новый container ID, а старого хоста в Zabbix уже нет. Мониторинг дырявый - и дыры появляются именно тогда, когда что-то идёт не так.

Откуда берётся проблема

У контейнеров нет постоянного IP. Даже если Docker-сеть настроена с фиксированными адресами, при пересоздании контейнера адрес может смениться - особенно если кто-то делал docker-compose down && docker-compose up в произвольном порядке. Добавлять каждый контейнер как отдельный хост вручную - это работа на час в день, и без гарантий что ничего не пропустил.

Второй момент: контейнеры появляются и исчезают по логике приложения. Контейнер для одноразовой задачи, который живёт две минуты, в Zabbix-модели выглядит как упавший хост. Начинают сыпаться алерты о недоступности, команда привыкает их игнорировать - и пропускает настоящую проблему.

Zabbix LLD и Docker API

В Zabbix 2.4 улучшили механизм Low-Level Discovery - возможность автоматически находить объекты для мониторинга по правилу и создавать под них прототипы элементов данных и триггеров. Раньше LLD использовали в основном для сетевых интерфейсов и разделов диска. Но ничего не мешает написать внешний скрипт, который возвращает JSON с нужными объектами - в нашем случае с контейнерами.

Docker API доступен через Unix-сокет /var/run/docker.sock или по HTTP если включён TCP-порт. Запрос к /containers/json возвращает список запущенных контейнеров со всеми метаданными: имя, ID, образ, сеть, порты, статус. Это именно то что нужно для LLD.

Схема получилась такая:

Zabbix-агент на Docker-хосте
  -> внешний скрипт discovery.py
  -> Docker API (/var/run/docker.sock)
  -> JSON с макросами {#CONTAINER_NAME}, {#CONTAINER_ID}, {#IMAGE}
  -> LLD создаёт элементы данных и триггеры по прототипу

Скрипт discovery опрашивает Docker API и отдаёт Zabbix-агенту список контейнеров в формате LLD. Агент передаёт это Zabbix-серверу, сервер создаёт элементы по прототипам. Новый контейнер появился - скрипт его нашёл, Zabbix создал айтемы. Контейнер умер и не должен мониториться - ставим keep_lost_resources на разумное время (у нас 30 минут) и лишнего шума нет.

Что именно мониторим

По каждому контейнеру через тот же Docker API снимаем:

  • CPU - docker stats отдаёт процент от квоты, если она задана, или от всего хоста.
  • Память - memory_stats.usage и memory_stats.limit. Если лимит не выставлен, limit равен полной памяти хоста - это нормально, смотрим на usage.
  • Статус - docker inspect <id> возвращает State.Status и State.Running. Триггер на Running = false с учётом типа контейнера.
  • Сетевой трафик - networks.<net>.rx_bytes и tx_bytes из stats API. Полезно для контейнеров с активным I/O.

Отдельно с самого Docker-демона снимаем количество запущенных контейнеров, общее число образов и размер занятого места. Это уже уровень хоста, а не отдельных контейнеров.

Где споткнулись

Docker socket и права. Zabbix-агент работает от пользователя zabbix, у которого по умолчанию нет доступа к /var/run/docker.sock. Добавление в группу docker решает проблему, но это расширение привилегий - агент получает фактически root-доступ к Docker. Мы знаем об этом и на данный момент принимаем как компромисс на доверенных хостах клиента.

Ephemeral контейнеры. Задачи, которые живут меньше минуты, Zabbix успевает обнаружить и тут же потерять - появляются ложные алерты пока не истечёт keep_lost_resources. Решили на уровне именования: такие контейнеры в Compose называются с префиксом task_, в скрипте discovery фильтруем их по имени и не включаем в LLD.

Контейнеры без имени. Если контейнер запущен через docker run без --name, Docker даёт ему случайное имя типа jovial_wozniak. Эти имена бесполезны как идентификаторы в дашборде. Договорились с клиентом: все контейнеры в Compose-файлах явно именуются, запуск через голый docker run в продакшне запрещён административно.

Что в итоге

Мониторинг стал работать сам. Новый контейнер появляется на дашборде через интервал discovery - у нас 5 минут. Алертов о несуществующих хостах нет. Команда клиента видит состояние всего парка контейнеров без того чтобы кто-то вручную добавлял их после каждого деплоя.

Решение не идеальное - скрипты внешние, их надо поддерживать, Docker API между версиями меняется. Но оно работает прямо сейчас с Zabbix 2.4 и Docker 1.7, который стоит у клиента. Когда появится готовый шаблон от сообщества с нормальной поддержкой - перейдём, но ждать не стали.

Параллельно думаем о том же подходе для мониторинга самих образов - отслеживать что именно запущено, какой тег, чтобы видеть несоответствия между тем что задеплоено на разных хостах. Это уже следующий шаг.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.