Начну с обычного «Меня зовут Михаил и я поддерживаю форк заббикса, называется Glaber».
Основная суть форка – экономить время людей, пользующихся мониторингом.
Проект бесплатный, открытый, его можно скачать на гитлабе , есть пакеты под разнык linux , описание тут.
Расскажу про скорость применения изменений конфигурации.
Жизненная ситуация:
На одной из моих инсталляций на поддержке я увидел интересную настройку метрики: каждые 5 секунд проверялось наличие ежесуточного бекапа с глубиной проверки в 1 сутки.
Технически это не проблема, 16к значений в кеше – легко. Но, у меня возник вопрос – зачем?
Дело было так
Администрару нужно было создать метрику, и проверить ее работу.
Планируемый интервал проверки «1 раз в сутки» – сработает в течение суток, быстро не проверить, а задачу нужно сделать сейчас. Поэтому автор выставил минимальный интервал и забыл поменять обратно, после того, как убедился что работает, как надо.
Проблему усугубляла неработющая кнопка «проверить сейчас» для данного типа проверки. Это было связано с тем, что механизм не был предусмотрен мной в асинхронных поллерах агентов (Agent). Косяк, баг, нужно исправить (upd: уже исправлено). Но это не главное.
Главное то, что процесс обсуждения с пользователями дал в сотый раз понять: наличие задержки между изменением конфигурации и применением ее на сервере создает существенную UX проблему.
Пользователь, изменив метрику, не хочет (да и не должен) ждать. А бывает, что нужно ждать 10 минут или час, это раздражает и съедает время.
В стоковых настройках сервер применяет новую конфигурацию каждые 30 секунд, но часто этот интервал увеличивают до 300-3600 секунд на конфигурациях побольше. Ждем?
Как бы сделать, чтобы время ожидания было меньше 10 секунд?
Решение 0, «в лоб»
Перезагружать всю конфигурацию как можно чаще, каждую секунду.
Так себе вариант – из высоких нагрузок и внтуренних блокировок скорость мониторинга деградирует, хотя для маленьких инсталлов (100 хостов, 10к метрик) – вполне можно. Но у меня есть сотни тысяч хостов с количеством метрик за 6млн. Там загрузка конфига идет 7-8 минут. Не пойдет.
Подойдем с головой, поразбираемся с матчастью:
Смотрим код синхронизации, архитекуру.
Есть отдельный процесс «config syncer», который запускает процедуру синхронизации конфигурации из базы SQL в память сервера каждые N секунд (опция CacheUpdateFrequency в файле конфигурации).
Начало многообещающее – у него есть два режима работы — FULL sync и UPDATE sync.
Есть две группы функций для каждого типа объектов (айтемы, хосты, тригеры … таковых около десятка) – первая группа загружает из базы или полный набор строк или изменения, а вторая группа функций применяет полученный набор строк и создает новые объекты, пересоздает изменившиеся, удаляет удаленные.
Но есть одно «но» — при периодической синхронизации конфигурации в память сервера делается загрузка типа UPDATE, и при этом тратится столько же времени, как и на первоначальную INIT- синхронизацию.
Почему?
Копаем глубже – внутри процедур загрузки данных. И в INIT и в UPDATE синхронизациях загружаются целиком все данные. В том числе, при обновлении. В процессе UPDATE синхронизации делается промежуточный набор изменений, состоящий из трех видов строк – новые, изменённые, неизмененные. И по размеру и количеству операций это аналогично полной синхронизации на старте.
Это неоптимально, вот пример, синхронизация на инсталле с 6 млн метрик дает пики по загрузке CPU (похожие скачки есть и в потреблении памяти) из за такой синхронизации.
Копаю еще чуть глубже — смотрю DDL таблиц, актуальные данные. Становится понятно, что так приходится делать, потому что нет механизма, позволяющего реализовать инкрементальность обновлений: в данных нет отметок времени или признаков изменения строк и флага «удаленная строка».
Поэтому для удаления из памяти неактуальных данных нужно делать полную синхронизацию (или делать тайм-аут, но тогда какое-то время еще будет продолжаться съем удаленных метрик).
С таким решением я не согласен, нужно переделать.
Решение 1: «Чего долго думать, сейчас все по быстрому исправим»
Добавляю в коде статус строки «удалено» с отложенным удалением и временные метки. Но есть ограничение: лезть в DDL таблиц очень «дорого», так как группа функций синхронизации жестко повязана на нумерацию столбцов в ответе во всех процедурах обновления данных, примерно вот так:
А это примерно 7-8 тысяч строк кода.
Поэтому добавить, скажем, поле timestamp вариант плохой: нумерация cъедет и сломается при очередном апдейте. И пока хочется на уровне таблиц SQL иметь максимальную заббикс – совместимость.
Пробую влезть со своими данными в текущие таблицы:
Начинаю с самой тяжелой таблицы – items (по времени на загрузку – это около 15% времени на тестовой конфигурации). С профайлингом помогает встроенный механизм дампа длинных запросов в заббиксе:
Так как Glaber не хранит и не читает оперативные данные по item в SQL, я могу задействовать поле lastchange в таблице rtdata для сохранения времени правки.
Ввожу новый тип статуса ITEM_STATUS_DELETED для индикации удаленных метрик и отложенную чистку таких строк в housekeeper процессе.
В процедурах удаления и изменения айтемов (метрик) в API и в коде сервера добавляю обновление поля lastdata при изменении метрики, корректное игнорирование удаленных строк.
В процедуре синхронизации айтемов добавляю временное условие и запоминаю время последней синхронизации
Вуаля, запрос теперь занимает несколько миллисекунд на обновлении десятка-сотни айтемов и, возвращает, то, что поменялось.
Дальше – триггеры. Повторяю весь набор действий, замечаю очень похожий код синхронизации, прямо как у метрик. С триггерами опять «везет» – есть поле, куда записать время, хотя Glaber все еще обновляет его в SQL базе, но можно и отключить — это оперативная информация, нечего ей в базе делать.
Но начинает нарастать снежный ком изменений: с триггерами «вылезают» таблицы функций и тегов триггеров, долго грузятся теги айтемов, шаги препроцессинга. И … опять похожие процедуры обновления, но тут никаких полей под временные метки нет.
Еще нахожу, что выбранная логика частичной синхронизации не подходит для текущей реализации списков зависимостей триггеров. При полной синхронизации они пересоздаются с нуля. А как их корректно обновить при частичной?
В текущей реализации индексация в парах зависимостей триггеров типа id1->[id2,id3,id4]
есть только по id1. Если поменяется или будет удален id1
никаких проблем, а если мы id3
удалим, только полным перебором искать, не очень хорошо.
Приехали.
Делаю паузу на пару дней. Обдумываю. Новый год на дворе. Лыжи рядом.
И мне нужно масштабируемое решение, не зависящее от конкретной таблицы и ее DDL, чтобы 10-12 разных типов объектов поддерживались и легко добавлялись новые и с множественными зависимостями (индексами) что то нужно сделать, причем так, чтобы базы 10млн метрик обновлялись легко и быстро при инкрементальных обновлениях.
Решение 2
Возвращаюсь с чистой головой: просматриваю целиком все процедуры в dbsync.c. Выясняю что имеет место быть «копипаста», раз пятнадцать наверное. Нужно почистить, хотя бы из соображений лени: пятнадцать раз писать одно и то же а потом править в пятнадцати местах будет долго.
Решаю, что будет три класса загрузки из базы:
1. достаточно маленькие таблицы, которые можно целиком «старым» способом загружать.
2. Большие таблицы, пропорциональные количеству элементов – производные от метрик (items): триггера, функции, теги. Их нужно по-новому, инкрементально.
3. Таблицы-индексы, для реализации отношений между объектами. Сложность в том, что в базе и в памяти они индексированы по разным полям, пересоздание не готится, они тоже «большие», их нужно инкрементально обновлять, их пока откладываю на подумать.
Но сначала нужно “убраться».
Откатываю все из «Решения1». Все было не зря, уроки получены, обзор сделан.
Убираю всю копипасту процедур загрузки, пишу две универсальные процедуры: код модуля становится на тысячу строк короче оригинального кода, и улучшается структура и красота
Вот одна функция, (красное – дублированный код, убран). А сбоку справа – по тикеру с обзором кода можно оценить сколько все такого было:
Почему это важно? Потому что скорость изменений в системе пользователь-ИТ система определяет скорость получения нужного результата и нашу эффективность работы.
У условного Васи – администратора мониторинга будет возможность в один рабочий час сделать 20-40 циклов изменил – проверил – еще изменил…, а не 1-2, что будет сильно экономить «васям» время, ну и мне, как «васе» тоже.
С Новым годом.
p.s. Исправления по быстрой загрузке конфига доступны начиная с версии 2.7.1
две три – в релиз
Добавить комментарий