Сейчас существует достаточно много систем для хранения и обработки метрик (timeseries db), но ситуация с агентами (софтом, который собирает метрики) сложнее. Не так давно появился telegraf, но все равно выбор не велик.
При этом практически все облачные сервисы мониторинга разрабатывают свои агенты и мы не исключение. Мотивация достаточно простая — есть много специфичных требований, которые слабо вписываются в архитектуру существующих решений.
Основные наши специфичные требования:
- надежность доставки метрик в облако
- непростая логика плагинов: они взаимодействуют друг с другом
- диагностика: мы должны уметь понимать, почему агент не может собрать те или иные метрики
- агент должен потреблять как можно меньше ресурсов клиентского сервера
Под катом расскажу несколько аспектов разработки агента для сбора метрик.
Доставка метрик в облако любой ценой
Система мониторинга должна работать всегда, а особенно в случае проблем в инфраструктуре клиента. Мы начали с того, что все метрики в первую очередь записываются на диск сервера, на котором они были собраны (мы называем это spool). После этого агент сразу пытается отправить пачку метрик в коллектор и в случае успеха удаляет эту пачку с диска. Spool ограничен по размеру (500 мегабайт по-умолчанию), если он переполняется мы начинаем удалять самые "старые" метрики.
В этом подходе сразу же нашлась ошибка, мы не сможем работать, если диск на сервере заполнен. Для этой ситуации мы пытаемся сразу отправить метрики, если на диск записать не удалось. Целиком логику отправки решили не менять, так как мы хотим минимизировать время нахождения метрик только в памяти агента.
Если пачку метрик отправить не удалось, агент повторяет попытку. Проблема в том, что пачки метрик могут быть достаточно большие и однозначно выбрать таймаут достаточно сложно. Если сделаем его большим, можем получить задержку в доставке метрик из-за одного "залипшего" запроса. В случае же маленького таймаута большие пачки метрик перестанут пролезать. Дробить большие пачки на мелкие мы не можем по ряду причин.
Мы решили не использовать общий таймаут на запрос к коллектору. Мы задаем таймаут на установку tcp соединения и tls handshake, а "живость" соединения проверяет TCP keepalive. Этот механизм доступен практически на всех современных OS, а там где его нет (у нас есть клиенты с FreeBSD 8.x например:) нам приходится ставить большой таймаут на весь запрос.
У этого механизма есть 3 настройки (все временные интервалы задаются в секундах, для сервисов, которые чувствительны к задержкам это не очень подойдет):
- Keepalive time — через какое время после получения последнего пакета с данными в соединении начинать отправлять пробы
- Keepalive probes — количество неудачных проб, после которых считаем соединение мертвым
- Keepalive interval — интервал между пробами
Значения по умолчанию мало пригодны на практике:
$ sysctl -a |grep tcp_keepalive
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 75
Эти парамерты можно переопределить для любого соединения, наша цель — как можно раньше определить проблемное соединение и закрыть его. Например, такие настройки time=1, probes=3, interval=1 позволяют отловить проблемное соединение за 4 секунды.
Когда связь агента с коллектором пропадает полностью, мы ничего больше сделать не можем. Но достаточно часто мы встречали ситуацию, когда связь есть, но не работает DNS сервер, используемый сервером. Мы решили в случае ошибки DNS пробовать отрезолвить домен коллектора через google public DNS.
Плагины
Мы очень много внимания уделяем автоопределению сервисов на серверах клиентов, это позволяет клиентам быстрее внедрять наш мониторинг и помогает ничего не забыть настроить. Для этого большинству плагинов нужен список процессов, причем он нужен не один раз при старте агента, а постоянно, чтобы подхватывать новые сервисы. Мы получаем список процессов один раз за интервал, используем его для получаения метрик по процессам и отправляем в те плагины, которым он нужен для других задач.
Так же плагин может запускать другие плагины или дополнительные инстансы самого себя. Например, у нас есть плагин nginx, он раз в минуту получается список процессов, для каждого запущенного nginx он:
- определяет местонахождение его конфига
- читает конфиг и все вложенные конфиги
- находит все директивы log_format и access_log
- на основе log_format формирует регулярное выражение для парсинга лога
- для каждого access_log запускает экземпляр плагина logparser, который начинает парсить лог
Если добавляется/удаляется какой-то лог или меняется формат, настройки logparser меняются, недостающие экземпляры запускаются, а лишние останавливаются.
Logparser может принимать на вход не один файл, а glob. Но так как мы хотим парсить логи параллельно, glob периодически раскрывается и запускается нужное количество экземпляров этого же плагина.
Недавно появилось еще одно достаточно замороченное место — сниффер трафика, пока с ним взаимодействует только плагин mongodb, но планируем расширять:
- плагин mongodb по списку процессов находит запущенную монгу на сервере
- сообщает снифферу, что он хочет получать пакеты по конкретному TCP порту
- получает пакеты от сниффера, дополнительно парсит tcp payload и считает различные метрики
В итоге у нас получились не совсем плагины в привычном понимании, а просто некоторые модули, которые между собой могут взаимодействовать. Подобные сценарии было бы очень сложно встроить в какое-то готовое решение.
Диагностика агента
Поддержка первых клиентов для нас оказалась адом, приходилось долго переписываться, просить клиента запускать на сервере разные команды и присылать нам вывод. Чтобы не краснеть перед клиентами и ускорить время поиска проблем, мы привели в порядок лог агента и начали доставлять его к нам в облако в режиме реального времени.
Лог помогает быстро отловить большинство проблем, но общение с клиентами полностью не заменяет. Самые распространенные проблемы связаны с тем, что сервисы автоматом не находятся. Большинство клиентов используют достаточно стандартные конфиги, форматы логов итд, у них все работает как часы. Но как правило в компаниях, где работают опытные администраторы — возможности софта используются на полную и появляются кейсы, о которых мы даже не подозревали.
Например, недавно мы узнали о возможности конфигурирования постгреса через ALTER SYSTEM SET, которая порождает отдельный конфиг postgresql.auto.conf, переопределяющий различные значение основного конфига.
У нас создается ощущение, что со временем наш агент превращается в копилку знаний о том, как бывают устроены различные проекты:)
Оптимизация производительности
Мы постоянно следим за потреблением ресурсов нашим агентом. У нас есть несколько плагинов, которые могут существенно нагрузить сервер: logparser, statsd, sniffer. Для таких случаев мы стараемся делать различные бенчмарки, часто профилируем код на наших стендах.
Агент мы пишем на golang, а у него в комплекте есть профайлер, который можно включать под нагрузкой. Мы решили этим воспользоваться и научили агента периодически выплевывать в лог cpu профайл за минуту, это позволяет понять, как ведет себя агент под клиентской нагрузкой.
Так как мы снимает потребление ресурсов абсолютно всеми процессами на сервере, клиенты могут всегда видеть, сколько потребляет наш агент. Например, на одном из клиентских фронтенд серверов агент парсит лог примерно с 3.5k rps:
По случайности рядом с нашим агентом тестируется nginx-amlify, который парсит тот же лог:)
Итого
- мониторинговый агент не такая простая штука, как кажется (у нас уходит около половины времени на разработку и поддержку агента)
- для нас все усложняется тем, что все клиенты разные и по-разному настраивают свою инфраструктуру
- оглянувшись назад, нам понятно, что мы не смогли бы реализовать свои хотелки на чем-то готовом
- продолжаем строить свой велосипед:)
Автор: okmeter.io