Русский
Русский
English
Статистика
Реклама

Grafana

Перевод Как мы потерпели неудачу, а затем преуспели в переходе на TypeScript

02.06.2021 18:06:54 | Автор: admin

К старту курса о Fullstack-разработке на Python, где также рассматривается TypeScript, мы перевели статью о миграции в Heap.io компании, которая предоставляет платформу аналитики продуктов, c языка CoffeeScript на TypeScript; TS в Heap.io начали использовать более 4 лет назад. Несмотря на широкое предпочтение TypeScript среди инженеров, миграция была медленной, а чёткого пути к 100 % кода TS не было.


На самом деле, если цель состояла в том, чтобы полностью переключиться на TS, мы двигались в неправильном направлении. Да, код на TS добавлялся, но количество кода на CoffeeScript росло быстрее. TypeScript и CoffeeScript нацелены на один и тот же рантайм Node.js, который, как ожидалось, должен был облегчить переход, но, несмотря на желание большого числа разработчиков перейти на TypeScript, мы не набрали большого импульса и не направлялись к будущему без CoffeeScript.

В начале 2019 года мы возобновили наши усилия по переходу с CoffeeScript на TypeScript. На этот раз мы решили: чтобы вдохнуть жизнь в нашу миграцию, от нас потребуется переосмысление стратегии преобразования существующего кода. Это привело нас к новому набору руководящих принципов. Следуя им, мы превратили то, что казалось трудноразрешимой задачей, в управляемый, хорошо понятный процесс и сумели значительно изменить форму кривых на графике:

Количество строк кода в разработкеКоличество строк кода в разработке

Миграция стека в равной степени касается и технологий, и людей

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

Как оказалось, более важные соображения лежат на стороне уравнения с людьми: как заставить наших коллег принять новую парадигму? Этот вопрос поднимает множество других. Например: как сделать разработчиков счастливыми и продуктивными в этой новой модели? Какие барьеры или трения мы можем устранить, чтобы преимущества новой парадигмы стали очевидными? Ответы на эти вопросы помогли нам разработать новый процесс миграции, которым сегодня очень гордимся.

Новый опыт разработки должен предлагать очевидное улучшение

Мы быстро поняли, что для того, чтобы ввести команду в новую парадигму, наши разработчики должны были чувствовать, что они будут более продуктивными, когда будут работать на TS. Если бы команда рассматривала это изменение просто как нейтральный сдвиг между синтаксисами, мы бы никогда не получили общее согласие. Если бы переключение не сделало каждый их день продуктивнее, инерция победила бы даже в случае предпочтения инженером типизированного кода.

Мы начали с определения областей кодовой базы, которые в случае преобразования приведут к существенному повышению производительности, зная, что стратегическое преобразование файлов будет привлекательнее произвольного преобразования. Например, наш слой доступа к данным (ORM) покрывает всё, и большинство файлов каким-то образом работают с ORM. Введение типов на уровне доступа к данным было множителем всей работы; почти каждый преобразованный в будущем файл выиграет от хорошо типизированных моделей баз данных и утилит.

Кроме того, мы сделали приоритетом оснастку и конфигурацию. Большинство разработчиков использовали один из нескольких редакторов, поэтому мы создали конфигурации редактора, которые будут работать сразу. Мы также добавили конфигурации отладки, облегчающие установку точек останова и пошаговое выполнение кода.

Наконец, мы сошлись на наборе согласованных правил линтинга, которые позволили нам писать код в едином стиле по всей организации и сделать переход более удобным.

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

Технические барьеры нужно ломать

Когда мы начали анализировать шаблоны внедрения TypeScript, стало ясно, что использование TypeScript для наших инженеров не было простым, им часто приходилось импортировать специальные утилиты (ts-node/register) или создавать промежуточные файлы CoffeeScript, которые не делали ничего, кроме импорта их эквивалентов TypeScript. Короче говоря, история взаимодействия языков существовала, но требовала много бойлерплейта и слишком много проб и ошибок.

Были ли эти трения основным блокирующим миграцию фактором? Трудно сказать, но мы знаем, что путь наименьшего сопротивления даёт большую силу, и, если мы хотим, чтобы люди переключались, нам нужно сделать TypeScript простым и очевидным выбором.

Чтобы добиться этого, мы отдавали приоритет усилиям, которые позволили бы разработчикам писать на TS в любом компоненте или сервисе. Будь то бэкенд, фронтенд, скрипты или задачи devops, мы хотели, чтобы наши инженеры могли писать код в TypeScript и чтобы он просто работал. В итоге мы прописали переменную среды NODE_OPTIONSс -r ts-node/register, чтобы существующие (использующие команду coffee для запуска файлов CoffeeScript) рабочие процессы также продолжали работать.

Преобразование должно быть простым, безопасным и автоматизированным

Миграция на другой язык может быть рискованной: то, что может показаться эквивалентным синтаксисом между CoffeeScript и ES6/TypeScript, на самом деле может вообще не быть эквивалентным. И разработчики могут рассматривать преобразование как хорошую возможность для рефакторинга, а это ещё хуже; переписывание делает рискованную миграцию ещё более рискованной.

Чтобы снизить риски, нам нужен был дисциплинированный процесс преобразования файлов без введения регрессий, а также нужно было помочь уйти от соблазна сделать больше, чем просто конверсия один языка в другой. Кроме того, процесс должен был быть быстрым.

Мы остановились на процессе из двух этапов это автоматическое преобразование файла CoffeeScript, за которым немедленно следует ручное добавление аннотаций основных типов и изменений, связанных с линтером. Ключевой момент состоял в том, чтобы противостоять искушению рефакторинга кода любым осмысленным способом. Такое преобразование становится механическим применением простых, безопасных правил, не влияющих на поведение во время выполнения.

Для первоначального преобразования мы использовали скрипт, преобразующий файл .coffee в файл .ts. В целях перехода от CoffeeScript к JavaScript ES6 Под капотом работал decaffeinate. Поскольку весь JavaScript ES6 является синтаксически правильным TypeScript, на выходе получался рабочий файл. (Мы обнаружили, что decaffeinate очень зрелый и надёжный инструмент.) В истории Git шаг преобразования представлен одним отдельным коммитом.

Однако работа ещё не была закончена. Мы используем TypeScript в строгом режиме, поэтому была отключена такая функция, как "implicit any". Мы использовали это окно преобразования как возможность создавать аннотации типов для элементов, где вывод типов был невозможен. Также мы избегали использования any в этой фазе, вместо этого выбрав более строгий неизвестный. Цель на этом этапе состояла в том, чтобы внести изменения, которые не приведут к изменению поведения во время выполнения. Мы не занимались никаким рефакторингом, а просто выполняли минимальный объём работы, чтобы привести код в состояние, в котором он компилировался, линтовался и проходил тесты.

Если зависимости модуля уже были преобразованы в TypeScript, усилий нужно было очень мало: о типах в основном заботились с помощью импортированных модулей. Это привело к эффекту снежного кома, когда по мере преобразования большего количества модулей преобразование в целом стало проще и безопаснее.

Этот второй шаг также прошёл отдельным коммитом; такой подход сильно упростил ревью: ревьюер мог легко увидеть, какие изменения были внесены после шага c decafeinate.

Весь процесс был задокументирован в руководстве по преобразованию машинописного текста. Любой разработчик кучи может преобразовать файл и открыть запрос на вытягивание, чтобы он был объединён всего за 5 минут.

#typescript: канал дискуссий и вопросов

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

Для этого мы создали канал #typescript в Slack и гарантировали, что застрявшие разработчики могут выйти из положения. Разработчики, управляющие миграцией, стали отвечать на вопросы и остановились на поиске общих проблем и камней преткновения. Если возникала одна и та же проблема, они знали, что нужно сделать лучше.

Разработчики должны знать, что на любые вопросы о языке и инструментах, которые у них есть, будут даны быстрые ответы. Мы решили, что чемпионы TypeScript должны отдать приоритет ответам, а не собственной работе. Хотя такой подход замедлил их работу, он также устранил ряд потенциально серьёзных препятствий в миграции.

Отслеживание прогресса

С самого начала мы знали, что массовая миграция за одну ночь невозможна и что, скорее всего, для завершения процесса потребуется год или больше. И это было прекрасно: мы решили, что прогресс важнее совершенства.

Отслеживание усилий во времени оказалось полезным. Таким образом возможно было понять, продолжается ли прогресс в миграции. Для визуализации количества строк мы воспользовались Grafana. Вот ещё одна визуализация, показывающая количество файлов во времени:

Уважающее инженеров руководство

К сожалению, одним из факторов, который в наибольшей степени способствует вашему успеху в подобных проектах находится чуть вне вашего контроля, является готовность руководства предоставить вам пространство для выполнения проекта. Проект миграции был направлен снизу вверх: он начался с того, что я представил план руководителям команд и руководителям инженерных подразделений, и после его утверждения нам предоставили свободу выбора наилучшего способа его реализации. Несмотря на то, что мы говорили о включающем сотни тысяч строк переходе, миграция была на 100 % внутренней.

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

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

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

Ситуации могут быть разными, и если вам не хочется ограничивать себя только одним подходом, владеть языками с разными системами типов, то вы можете обратить внимание на наш курс Fullstack-разработчик на Python, где, кроме Python и TS, студенты также изучают чистый JavaScript.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Новая версия нашего самописного плагина, который скачали 250 тысяч раз

03.02.2021 14:08:11 | Автор: admin

Привет, коллеги!

В феврале необязательно доставать чернила и плакать можно и радоваться.

Во-первых, Grafana заапрувила новую версию нашего плагина для мониторинга Kubernetes: KubeGraf v.1.5.0 доступен для инсталляции.

Во-вторых, оказалось, что за полтора года с момента выхода первой версии плагин скачали четверть миллиона раз!

Если вы хотите узнать, чем плагин стал лучше и удобнее в новой версии добро пожаловать под кат.

Маленький дисклеймер:

наш продукт является переработанной версией официального плагина от Grafana (который не поддерживается уже около двух лет). Об истории его создания мы уже писали здесь, на Хабре. Напомню ключевые особенности KubeGraf:

  • интеграция с k8s-api для построения карты ваших приложений, сгруппированных по неймспейсам / нодам-кластера + привязка к конкретным podам/сервисам;

  • сводная страница со всеми ошибками / предупреждениями о работе нод и приложений кластера;

  • возможность инсталляции плагина с облачными k8s-провайдерами через авторизацию с помощью bearer-tokena.

А что нового в пятом релизе? Много разного и полезного:

  • совместимость с последними версиями Grafana;

  • добавлена информация и визуализация лимитов по cpu/memory, добавлена индикация при превышении requested и приближению к limit, а также индикация того, что для какого-либо приложения не настроены requests/limits;

  • в дашбордах по мониторингу deployments/daemonsets/statefulsets в разделе Templating теперь выводятся только те namespaceы, в которых содержатся ресурсы данного типа;

  • в дашборде мониторинга конкретных podов исправлено отображение потребления cpu/memory;

  • таблица с алертами и предупреждениями отсортирована в соответствии с severity (info/warning/critical);

  • доработана инструкция по установке плагина и исправлены k8s-манифесты, необходимые для его установки (добавлены namespaceы);

+ мелкие доработки интерфейсов и навигации.

Пользуйтесь!

Кстати

Кстати, в качестве вспомогательной утилиты в процессе разработки нашего плагина мы создали helm-чарт, с помощью которого вы можете установить любую версию плагина из репозитория с его исходным кодом (например, используя определенный тег), не дожидаясь её появления в grafana-plugins-repository.

Все так же ждём ваших звездочек, issue и pull requests в нашем репозитории.

Обсудить плагин можно в нашем телеграм-чате или в Slack.

Подробнее..

GrafanaZabbix Визуализация работы производственной линии

27.08.2020 20:07:22 | Автор: admin

В этой статья я хочу поделиться опытом использования open source систем Zabbix и Grafana для визуализации работы производственных линий. Информация может быть полезна тем, кто ищет быстрый способ визуального отображения или аналитики собранных данных в проектах промышленной автоматизации или IoT. Статья не является подробным руководством, это скорее концепция системы мониторинга, основанная на открытом программном обеспечении для производственного предприятия.


Инструментарий


Zabbix его мы используем давно для мониторинга ИТ инфраструктуры завода. Система оказалось настолько удобной и универсальной, что мы стали заводить в нее данные с производственных линий, датчиков и контроллеров. Это нам позволило собрать все данные метрик в одном месте, сделать простые графики расхода ресурсов и производительности оборудования, но очень не хватало аналитики и красивых графиков.
Grafana это мощнейший инструмент для аналитики и визуализации данных. Большое количество плагинов позволяют забирать данные из различных источников (zabbix, clickhouse, influxDB), обрабатывать их на лету (считать среднее значение, сумму, разницу и т.д.) и рисовать всевозможные графики (от простых линий, спидометров, таблиц до сложных схем).
Draw.io сервис, позволяющий в онлайн редакторе нарисовать от простой блок схемы до плана помещений. Есть много готовых шаблонов и нарисованных объектов. Данные можно экспортировать во все основные графические форматы или xml.


Собираем все вместе


Статей как установить и настроить Grafana и Zabbix написано много, я расскажу про основные моменты конфигурации.
На Zabbix сервере создается узел сети (host), которому будут принадлежать элементы данных (item) с метриками от наших датчиков. Имена узлов и элементов данных желательно продумать заранее и сделать максимально структурированными, так как к ним мы будем обращаться из графаны через регулярные выражения. Такой подход удобен тем, что можно одним запросом получать данные с группы элементов.


Для настройки графаны потребуется установить дополнительные плагины:


  • Zabbix by Alexander Zobnin (alexanderzobnin-zabbix-app) интеграция с zabbix
  • natel-discrete-panel плагин для дискретной визуализации на горизонтальном графике
  • pierosavi-imageit-panel плагин для отображения данных поверх своей картинки
  • agenty-flowcharting-panel плагин для динамической визуализации схемы из draw.io

Сама интеграция с заббиксом настраивается в графане, пункт меню Configuration\Data sources\Zabbix. Там нужно указать адрес api zabbix сервера, у меня это http://zabbix.local/zabbix/api_jsonrpc.php, и логин с паролем для доступа. Если все сделано правильно, при сохранении настроек будет сообщение с номером версии api: zabbix API version: 5.0.1


Создаем Dashboard


Вот тут начинается та самая магия графаны и ее плагинов.


Плагин natel-discrete-panel
У нас есть данные о статусах двигателей на линиях (работает = 1, не работает =0). При помощи графика discrete мы можем нарисовать шкалу, на которой будет видно: статус двигателя, сколько он проработал минутах/часах или % и как часто запускался.


image
Визуализация статусов двигателей


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


Плагин pierosavi-imageit-panel
Imageit удобно использовать когда у вас уже есть нарисованная схема или план помещения, на которую вы хотите нанести данные с датчиков. В настройках визуализации нужно указать url адрес к картинке и добавить нужные вам элементы sensor. Элемент появляется на картинке и его можно разместить в нужном месте мышкой.


image
Схема печи с метриками температуры и давления


Плагин agenty-flowcharting-panel
Про создание визуализации FlowCharting я хотел бы рассказать более подробно, так как это невероятно функциональный инструмент. Он позволяет сделать динамическую мнемосхему, элементы которой будут реагировать на значения метрик (менять цвет, положение, название итд).


Получение данных


Создание любого элемента визуализации в графане начинается с запроса данных из источника, в нашем случае это zabbix. При помощи запросов нужно получить все метрики, которыми мы хотим воспользоваться на схеме. Реквизиты метрик это имена элементов данных в заббиксе, можно указать как отдельную метрику, так и множество с фильтрацией через регулярное выражение. В моем примере поле Item содержит выражение: /(^линия 1)|(наличие)|(кабачок)/ это означает: отобрать все метрики, имя которых строго начинается с линия 1 или содержит слово наличие или содержит слово кабачок


image
Пример настройки запроса данных о двигателях первой линии и наличии сырья


Преобразование данных


Исходные данные могут быть не всегда в том виде, в котором нам нужно их отобразить. Например, у нас есть ежеминутные данные о весе продукта в емкости (кг), и нам нужно отобразить скорость заполнения в т/час. Я это делаю следующим образом: беру данные о весе и преобразую их функцией графаны delta, которая считает разницу между значениями метрики, так текущий вес превращается в кг/мин. Затем умножаю на 0.06 для приведения результата в тонны/час. Так как метрика веса используется в нескольких запросах, я указываю для нее новый псевдоним (setAlias) и буду его использовать в правиле визуализации.


image
Пример использования параметра delta и множителя и переименования метрики в запросе


Вот еще пример преобразования данных: мне нужно было подсчитать кол-ва замесов (начало цикла = пуск двигателя). Метрика считается на основе статуса двигателя "линия 1 насос откачки из бака 1 (статус)". Преобразование: данные исходной метрики меняем функцией delta (разница значений), таким образом в метрике будет значение +1 для пуска двигателя, -1 для остановки и 0 когда двигатель не меняет свой статус. Затем убираю все значения меньше 1 и суммирую их. В результате получается кол-во пусков двигателя.


image
Пример преобразования данных из текущего статуса в кол-во пусков


Теперь про саму визуализацию


В настройках отображения есть кнопка Edit Draw, она запускает редактор, в котором можно нарисовать схему. У каждого объекта на схеме есть свои параметры. Например, если указать настройки шрифта в редакторе они будут применяться к визуализации данных в графане.


image
Так выглядит редактор в Draw.io


После сохранения схемы, она появится в графане и можно будет создавать правила изменения элементов.
В параметрах () мы указываем:


  • Options задается имя правила (Rule name), название или псевдоним метрики, данные которой будут использоваться (Apply to metrics). Тип агрегации данных (Aggregation) влияет на конечный результат метрики, так Last означает, что будет выбрано последнее значение, avg среднее значение за период, выбранный в верхнем правом углу.
  • Thresholds параметр пороговых значений, описывает логику применения цвета, то есть выбранный цвет будет применяться к элементам на схеме в зависимости от данных метрики. В моем примере при значении метрик 0 статус Ok цвет будет зеленый, при значении >1 статус Critical и цвет будет красный.
  • Color/Tooltip Mappings и Label/Text Mappings выбор элемента схемы и сценария его поведения. В первом сценарии объект будет закрашен, во втором на нем будет текст с данными из метрики. Для выбора объекта на схеме нужно нажать знак цепи и кликнуть мышкой на схеме.

image
На этом примере я закрашиваю насос и его стрелку красным цветом если он работает и зеленым если нет


При помощи плагина flowcharting мне удалось нарисовать схему всей линии, на которой:
1) меняется цвет агрегатов в соответствии с их статусом
2) есть сигнализация отсутствия продукта в емкостях
3) отображается настройка частот двигателей
4) скорость заполнения/сброса первого бака
5) подсчитывается кол-во циклов работы линии (замесы)


image
Визуализация работы производственной линии


Результат


Самым сложным для меня было получить данные с контроллеров. Благодаря универсальности заббикса в плане получения данных и гибкости графаны за счет плагинов, потребовалось всего пара дней для создания комплексного экрана мониторинга производственной линии. Визуализация дала возможность просмотра графиков и статистики состояний, плюс легкий доступ через веб всем заинтересованным все это позволило быстро выявлять узкие места и неэффективное использование агрегатов.


Заключение


Мне очень понравилась связка Zabbix+Grafana и я рекомендую обратить на нее внимание, если вам нужно быстро обработать данные с контроллеров или датчиков без программирования или внедрения сложных коммерческих продуктов. Безусловно, это не заменит профессиональные SCADA системы, но будет достаточено как инструмент централизованного мониторинга всего производства.

Подробнее..

Мониторим Спортмастер как и чем

03.09.2020 20:17:52 | Автор: admin
О создании системы мониторинга мы задумались на этапе формирования продуктовых команд. Стало понятно, что наше дело эксплуатация в эти команды никак не попадает. Почему так?

Дело в том, что все наши команды построены вокруг отдельных информационных систем, микросервисов и фронтов, поэтому общее состояние здоровья всей системы в целом команды не видят. Например, они могут не знать, как какая-то небольшая часть в глубоком бэкенде влияет на фронтовую часть. Круг их интересов ограничивается системами, с которыми интегрирована их система. Если же команда и её сервис А почти никак не связан с сервисом Б, то такой сервис для команды почти невидим.



Наша же команда, в свою очередь, работает с системами, которые очень сильно интегрированы между собой: между ними множество связей, это весьма большая инфраструктура. И от всех этих систем (которых у нас, к слову, огромное количество), зависит работа интернет-магазина.

Вот и получается, что наш отдел не относится ни к одной команде, а находится немного в стороне. Во всей этой истории наша задача понимать в комплексе, как работают информационные системы, их функциональность, интеграции, ПО, сеть, железо, и как все это связано между собой.

Платформа, на которой функционируют наши интернет-магазины, выглядит так:

  • front
  • middle-office
  • back-office

Как бы нам ни хотелось, но не бывает такого, чтобы все системы работали гладко и безукоризненно. Дело, опять же, в количестве систем и интеграций при таком, как у нас, какие-то инциденты это неизбежность, несмотря на качество тестирования. Причем как внутри какой-то отдельной системы, так и в плане их интеграции. И нужно следить за состоянием всей платформы комплексно, а не какой-нибудь отдельной её части.

В идеале наблюдение за состоянием здоровья всей платформы нужно автоматизировать. И мы пришли к мониторингу как к неизбежной части этого процесса. Изначально он был построен только для фронтовой части, при этом собственные системы мониторинга по слоям были и есть у сетевиков, администраторов ПО и аппаратного обеспечения. Все эти люди следили за мониторингом только на своем уровне, комплексного понимания тоже ни у кого не было.

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

В конечном счете такие разрозненные истории влияют на весь фронтенд, на пользователей и нашу основную бизнес-функцию интернет-продажи. Так как мы не входим в команды, а занимаемся эксплуатацией всех ecommerce-приложений в составе интернет-магазина, мы взяли на себя задачу по созданию комплексной системы мониторинга ecommerce-платформы.

Структура системы и стек


Мы начали с того, что выделили несколько слоёв мониторинга для наших систем, в разрезе которых нам потребуется собирать метрики. И все это нужно было объединить, что мы и сделали на первом этапе. Сейчас по этому этапу мы дорабатываем максимально качественный сбор метрик по всем нашим слоям, чтобы выстраивать корреляцию и понимать, как системы влияют друг на друга.

Отсутствие комплексного мониторинга на начальных этапах запуска приложений (так как мы начали строить его, когда большая часть систем была в эксплуатации) привело к тому, что у нас образовался значительный технический долг по настройке мониторинга всей платформы. Сосредотачиваться на настройке мониторинга какой-то одной ИС и детально прорабатывать мониторинг для неё мы не могли себе позволить, так как остальные системы остались бы на какое-то время без мониторинга. Для решения этой задачи мы определили список самых необходимых метрик оценки состояния информационной системы по слоям и начали его внедрять.

Поэтому слона решили есть по частям.

Наша система складывается из:

  • hardware;
  • операционной системы;
  • software;
  • UI-части в приложении мониторинга;
  • бизнес-метрики;
  • приложения интеграции;
  • информационной безопасности;
  • сети;
  • балансировщика трафика.



В центре этой системы собственно мониторинг. Чтобы в общем понимать состояние всей системы, надо знать, что происходит с приложениями на всех этих слоях и в разрезе всего множества приложений.

Так вот, про стек.



Используем ПО с открытым исходным кодом. В центре у нас Zabbix, который мы используем в первую очередь как систему алертинга. Всем известно, что он идеально подходит для мониторинга инфраструктуры. Что здесь имеется в виду? Как раз те низкоуровневые метрики, которые есть у каждой компании, которая содержит свой ЦОД (а у Спортмастера свои ЦОДы) температура сервера, состояние памяти, рейда, метрики сетевых устройств.

Мы интегрировали Zabbix с мессенджером Telegram и Microsoft Teams, активно используемыми в командах. Zabbix покрывает слой фактической сети, железа и частично ПО, но это не панацея. Мы эти данные обогащаем из некоторых других сервисов. Например, по уровню аппаратного обеспечения мы напрямую по API коннектимся в нашей системе виртуализации и забираем данные.

Что ещё. Помимо Zabbix мы используем Prometheus, который позволяет мониторить метрики в приложении динамической среды. То есть, мы можем получать метрики приложения по HTTP endpoint и не переживать по поводу того, какие метрики в нее загружать, а какие нет. На основании этих данных можно прорабатывать аналитические запросы.

Источники данных для остальных слоев, например, бизнес-метрик, у нас делятся на три составляющие.

Во-первых, это внешние бизнесовые системы, Google Analytics, собираем метрики из логов. Из них мы получаем данные по активным пользователям, конверсии и всему прочему, связанному с бизнесом. Во-вторых, это система UI-мониторинга. О ней следует рассказать более подробно.

Когда-то мы начинали с мануального тестирования и оно перерасло в автотесты функционала и интеграций. Из него мы и сделали мониторинг, оставив только основной функционал, и завязались на маркеры, которые максимально стабильны и не часто меняются со временем.

Новая структура команд подразумевает, что вся деятельность по приложениям замыкается на продуктовых командах, поэтому чистым тестированием мы заниматься перестали. Вместо этого мы из тестов сделали UI-мониторинг, написанный на Java, Selenium и Jenkins (используется как система запуска и генерации отчетов).

У нас было много тестов, но в итоге мы решили выходить на главную дорогу, верхнеуровневую метрику. И если у нас будет много специфических тестов, то будет сложно поддерживать актуальность данных. Каждый последующий релиз будет значительно ломать всю систему, а мы только и будем заниматься ее починкой. Поэтому мы завязались на совсем фундаментальные вещи, которые редко меняются, и мониторим только их.

Наконец, в-третьих, источником данных является централизованная система логирования. Для логов используем Elastic Stack, а потом эти данные можем затягивать в нашу систему мониторинга по бизнес-метрикам. В дополнение ко всему этому работает наш собственный сервис Monitoring API, написанный на Python, который опрашивает по API любые сервисы и забирает в Zabbix данные из них.

Еще один незаменимый атрибут мониторинга визуализация. У нас она строится на основе Grafana. Среди прочих систем визуализации она выделяется тем, что на дашборде можно визуализировать метрики из разных источников данных. Мы можем собрать верхнеуровневые метрики интернет-магазина, например, количество заказов, оформленных за последний час, из СУБД, метрики производительности ОС, на которой запущен этот интернет-магазин, из Zabbix, а метрики инстансов этого приложения из Prometheus. И все это будет на одном дашборде. Наглядно и доступно.

Отмечу про безопасность мы сейчас допиливаем систему, которую впоследствии будем интегрировать с глобальной системой мониторинга. На мой взгляд, основные проблемы, с которыми сталкивается e-commerce в сфере информационной безопасности, связаны с ботами, парсерами и брутфорсом. За этим нужно следить, потому что они все это может критично повлиять как на работу наших приложений, так и на репутацию с точки зрения бизнеса. И выбранным стеком мы эти задачи успешно покрываем.

Ещё важный момент уровень приложений собирается Prometheusом. Сам он он у нас тоже интегрирован с Zabbix. И ещё у нас есть sitespeed, сервис, который позволяет нам соответственно смотреть такие параметры, как скорость загрузки нашей страницы, боттлнеки, отрисовка страницы, загрузка скриптов и прочее, он тоже по API интегрирован. Так метрики у нас собираются в Zabbix, соответственно, алертим мы также оттуда. Все алерты пока уходят на основные способы отправки (пока это email и telegram, ещё подключили недавно MS Teams). В планах прокачать алертинг до такого состояния, чтобы умные боты работали как сервис и предоставляли информацию по мониторингу всем желающим продуктовым командам.

Для нас важны метрики не только отдельных информационных систем, но и общие метрики по всей инфраструктуре, которую используют приложения: кластеры физических серверов, на которых крутятся виртуалки, балансировщики трафика, Network Load Balancer-ы, сама сеть, утилизация каналов связи. Плюс метрики по нашим собственным цодам (у нас их несколько и инфраструктура довольно значительных размеров).



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

И с помощью метрик мы видим тенденцию потребления ресурсов нашими информационными системами. И уже на их основании можем что-то планировать. На уровне виртуализации мы собираем данные и видим информацию по доступному количеству ресурсов в разрезе ЦОДов. А уже внутри ЦОДа видна и утилизация, и фактическое распределение, потребление ресурсов. Причем как со standalone-серверами, так и виртуальными машинами и кластерами физических серверов, на которых все эти виртуалки бодро крутятся.

Перспективы


Сейчас у нас готово ядро системы в целом, но осталось достаточно моментов, над которыми еще предстоит работать. Как минимум это слой информационной безопасности, но важно также добраться до сети, развить алертинг и решить вопрос с корреляцией. Слоев и систем у нас много, на каждом слое еще множество метрик. Получается матрёшка в степени матрёшки.

Наша задача в конечном счете сделать правильные алерты. Например, если случилась проблема с аппаратной частью, опять же, с виртуальной машиной, а там было важное приложение, и сервис был никак не зарезервирован. Мы узнаем, что виртуальная машина умерла. Затем будут алертить бизнес-метрики: пользователи куда-то пропали, конверсии нет, UI в интерфейсе недоступен, ПО и сервисы тоже умерли.

При таком раскладе мы получим спам из алертов, а это уже не укладывается в формат правильной системы мониторинга. Встает вопрос корреляции. Поэтому в идеале наша система мониторинга должна сказать: Ребята, у вас физическая машина умерла, а вместе с ней вот это приложение и такие метрики, с помощью одного алерта вместо того, чтобы яростно засыпать нас сотней алертов. Она должна сообщить о главном о причине, что способствует оперативности устранения проблемы за счёт её локализации.

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

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

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

Помимо мониторинга по системам, которые мы эксплуатируем, и сбора метрик, которые на нашей стороне расцениваются как важные, система мониторинга позволяет собирать данные для продуктовых команд. Они могут влиять на состав метрик в рамках информационных систем, которые мониторятся у нас.

Наш коллега может прийти и попросить добавить какую-нибудь метрику, которая окажется полезной и для нас, и для команды. Или, например, команде может быть недостаточно тех базовых метрик, которые у нас есть, им нужно отслеживать какую-то специфическую. В Grafana мы создаём пространство для каждой команды и выдаём права админа. Также, если команде нужны дашборды, а они сами не могут/не знают как это сделать, мы им помогаем.

Так как мы вне потока создания ценностей команды, их релизов и планирования, мы постепенно приходим к тому, что релизы всех систем бесшовные и их можно выкатывать ежедневно, не согласовывая при этом с нами. А нам важно отслеживать эти релизы, потому что потенциально они могут повлиять на работу приложения и что-то сломать, а это критично. Для управления релизами мы используем Bamboo, откуда по API получаем данные и можем видеть, какие релизы в каких информационных системах вышли и их статус. И самое важное в какое время. Маркеры о релизах мы накладываем на основные критичные метрики, что визуально является весьма показательным в случае проблем.

Таким образом мы можем видеть корреляцию между новыми релизами и возникающими проблемами. Основная идея в том, чтобы понимать, как работает система на всех слоях, быстро локализовать проблему и так же быстро ее исправить. Ведь часто бывает так, что больше всего времени занимает не решение проблемы, а поиск причины.

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

Итак, система запущена и успешно работает с начала весны и показывает вполне реальный профит. Конечно, это не ее финальная версия, мы будет внедрять еще множество полезностей. Но прямо сейчас, при таком большом количестве интеграций и приложений, без автоматизации мониторинга на самом деле не обойтись.

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

Перевод Представлена Grafana Tempo, широко масштабируемая распределенная система трассировки

29.10.2020 16:15:50 | Автор: admin


Grafana Labs с гордостью представляет простую в эксплуатации, масштабируемую, рентабельную, распределенную систему трассировки: Tempo. Она разработана в качестве надежного хранилища, оптимизированного для поиска идентификаторов, единственная ее зависимость объектное хранилище (GCS/S3).


Зачем?


Мы в Grafana Labs разочарованы нашей распределенной системой трассировки с субвыборкой. Найти определенную трассировку в общем случае не сложная задача, но нашим инженерам неоднократно приходилось искать конкретные результаты трассировки.


Нам хотелось бы, чтобы система трассировки могла отвечать на следующие вопросы: Почему этот клиентский запрос работал медленно?, Плавающая ошибка опять проявилась. Могу ли я увидеть точную трассировку?. Стало ясно, что нужна полная выборка на все 100%, но нам не хотелось возиться с кластерами Elasticsearch или Cassandra, где такое возможно.


В дополнение мы определили, что наш backend для трассировки не делает индексирование наших трассировок. Мы можем обнаружить трассировки в журналах или экземплярах. Зачем платить за индексирование и трассировок, и журналов, и параметров? Все, что было нужно механизм худо-бедно обеспечивающий хранилище трассировка-по-идентификатору. Так мы и создали Tempo.


Tempo на данный момент умеет принимать, записывать и хранить в течение 14 дней полные пути чтения наших производственных и тестовых окружений, а также окружений для разработки. Он каждые сутки потребляет порядка 170.000 интервалов в секунду, группирует их и записывает в GCS.


Внедрение в Grafana


Связывание журналов и трассировок


Loki и прочие источники данных могут настраиваться на создание ссылок из идентификаторов трассировки в строках журнала. Но почему это ограничивает поисковые способности уже существующего backend для трассировки? Используя журналы, вы можете искать по пути, коду возврата, задержке, пользователю, ip, а также чему угодно, что размещено в строке журнала с одним и тем же идентификатором.


Например, есть такая строка:


path=/api/v1/users status=500 latency=25ms traceid=598083459f85afab userid=4928

Все эти поля теперь представляются в Tempo в виде индекса идентификаторов трассировок, пригодного для поиска. Вы уже вложили деньги и время в вашу систему журналирования. Используйте же ее и для поиска трассировок!


Связывание параметров и трассировок


Наконец-то! Экземпляры с открытым исходным кодом! На данный момент трассировки могут быть найдены напрямую из параметров.


С помощью журналов вы можете найти точную трассировку, основанную на полях журнала. Экземпляры дают вам возможность поиска трассировки по шаблону. Вы можете получить ссылки на трассировки, основанные на ваших запросах параметров, встроенных напрямую в ваши графики Grafana. Сделайте запрос к Prometheus с вашими перцентилями 99, 500 кодами ошибок, избранными endpoints, после чего ваши трассировки станут актуальными примерами по шаблону, который вы просматриваете.


Связывание трассировок и всего остального


Экземпляры и журналы для обнаружения и Tempo для ммм хранения всего без необходимости беспокоиться об оплате. Давайте шагнем чуть далее и добавим новые пути связывания ваших данных для наблюдения. А что насчет связывания трассировки прямо в журналах? С помощью Grafana Cloud Agent вы можете пометить ваши трассировки, журналы и параметры целостными метаданными, из которых затем будут сделаны корреляции, ранее не возможные.


Оператор после прыжка от экземпляра к трассировке теперь может перейти прямиком к журналам взбрыкнувшего сервиса!


Трассировка позволяет немедленно определить, какой элемент вашего пути запроса привел к ошибке, а журналы помогут вам определить, почему.


Погнали!


Хотите увеличить число захватываемых и хранимых трассировок в обмен на небольшие затраты в довесок к вашей существующей системе? Готовы использовать журналы и экземпляры для значительного повышения гибкости поиска в вашем распределенном backend для трассировки? Перехватывает дыхание от бесшовного внедрения в Grafana связки ваших параметров, журналов и трассировок? Тогда, наверное, пришло время переключиться на новый backend и помочь вашим операторам поддерживать Tempo.


От редакции: Приглашаем на курс Слёрма Мониторинг и логирование инфраструктуры в Kubernetes. Курс от основ до продвинутого уровня для быстрого ввода в эксплуатацию мониторинга и логирования инфраструктуры.

Подробнее..

Перевод Вышел Loki 2.0

04.11.2020 12:10:51 | Автор: admin


Преобразовывайте свои журналы прямо во время запроса, настраивайте уведомления с Loki.


Прошел примерно год после выпуска Loki 1.0, мы за это время заметили большой всплеск внедрений в компаниях (например, Grofers и Paytm Insider), использующих как облачную версию Grafana, так и размещенную на своих мощностях. В то же время мы приложили много усилий для улучшения производительности, используя оптимизацию и распараллеливание запросов. В последнем выпуске 1.6.0 мы продолжили рефакторинг кода для достижения еще большей производительности, а также добавили небольшие новые функции к языку запросов, например, бинарные операции.


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


Это и есть причина, по которой мы решили значительно улучшить Loki, поэтому рады представить Loki 2.0. В этой версии мы добавили новейший набор функций языка запросов, позволяющий извлекать метки во время запроса, убирая старые ограничения.


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


Давайте посмотрим детальнее на новые функции!


Извлечение меток из строк журналов во время запроса


Давайте посмотрим на новые дополнения к языку запросов в версии 2.0!



С оранжевыми пунктами вы уже должны быть знакомы. Выборка содержимого журнала с помощью |= != |~ и !~, а также преобразование ваших журналов в параметры с использованием rate и count_over_time уже работали в Loki 1.x. Все остальное, выделенное белым цветом новое в версии 2.0, мне кажется, что будет лучше рассмотреть, как оно работает детальнее на примерах, так что погнали!


Разбор


Это начальная точка: мы извлекаем метки во время запроса. Например, у нас есть поставщик журнала:



Посмотрите, какие метки были возвращены:



Давайте немножко поменяем запрос и посмотрим на результат, возвращаемый по этому журналу:



Обратите внимание на метки...



Вот так вот просто каждая пара ключ=значение из logfmt стала меткой!


Не используете logfmt? Тогда мы идем к вам!


Регулярное выражение:



JSON:



Примечание. Для JSON мы убираем вложенные объекты, используя подчеркивания для слияния элементов. Посмотрите на объект responce на картинке выше. Мы хотим в дальнейшем расширить возможности обработки JSON, но для этого потребуется собственный язык, так что мы для начала выбрали более простое решение.

Это покрывает первый этап, извлечение содержимого журнала в метки во время запроса. Давайте посмотрим, что это нам дает.


Выборка


В Loki 2.0 доступны более мощные возможности выборки. Используя расширенные существующие выражения для выборки, вы теперь можете отсеять данные, полученные на предыдущем этапе разбора. Давайте посмотрим на пример, показывающий только те строки журнала, где извлеченный параметр duration больше 1 секунды с помощью query_type=filter:



Обратите внимание на умную обработку Loki выходных типов в некоторых контекстах, в этом случае идет разбор duration в стиле Golang.


Выборка является важной частью обработки ошибок в новых функциях. Если что-то пошло не так с обработкой строки, Loki назначит новую метку __error__ с именем ошибки. Это можно использовать для выбора журналов и анализа причин:



В вышеприведенном случае такие строки не JSON, так что они не были обработаны как JSON на этапе json. Теперь, когда вы узнали причину, можно легко и непринужденно исключить такие строки:



Форматирование


Упростить работу всем, кто смотрит журналы, стало еще легче. Loki 2.0 может перезаписать ваши строки журнала так, как они показаны, а также с его помощью можно сделать так:



Превращаем в такое:



Метки label_format и line_format используют синтаксис шаблонов Golang, позволяя вам выбрать связанные строки журнала для отображения. С этим также связаны другие дополнительные возможности, например, способность вертикального выравнивания или обрезания содержимого журнала.


Графики


Вам это, возможно, покажется более впечатляющим, чем новые возможности выборки. У вас появилась возможность отображения ваших журналов в виде параметров. Многие новые функции группировки параметров могут быть использованы на этапе unwrap, на котором извлеченные метки будут превращены во временные ряды, с которыми вы сможете использовать следующие функции:


  • sum_over_time
  • avg_over_time
  • stddev_over_time
  • stdvar_over_time
  • max_over_time
  • min_over_time
  • quantile_over_time

Давайте рассмотрим пример использования, чтобы отобразить 99 перцентиль параметра request_time из журналов NGINX, сгруппированных по целевому серверу:



А теперь берем тот же запрос и группируем по клиентскому IP:



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


Еще пара примеров:




В последнем примере вы можете видеть такое: duration(duration). Так можно указать Loki разбирать значения duration в стиле Golang (там добавляются единицы, например s или ms). Скоро будет доступна поддержка и других дополнительных типов, например kb, mb и прочих.


Создание уведомлений из любого запроса


Все эти новые функции очень впечатляют, особенно, если они используются совместно с еще одним большим улучшением в 2.0: возможностью создания уведомлений прямиком из ваших журналов в Loki и их отправки в Prometheus Alertmanager.



Ранее для такого вам надо было настроить Loki в качестве источника данных для Prometheus, после чего указать Grafana использовать его для создания уведомлений. В Loki 2.0 мы внедрили распределенный механизм оценки правил, так что вы можете написать любой запрос и создать уведомление с использованием знакомого синтаксиса Prometheus. Эти уведомления потом отправляются в Prometheus Alertmanager, развернутый отдельно. Процесс создания и отправки уведомлений стал простым!



Удалено отдельное хранилище индексов


И последняя впечатляющая новость о Loki 2.0 удаление пометки "экспериментальный" с типа индекса boltdb-shipper!



В Loki 1.5 мы представили новый индекс boltdb-shipper. С новым индексом вы могли запустить Loki поверх любого объектного хранилища, а сейчас вам уже больше не нужно отдельное выделенное хранилище (DynamoDB, Bigtable, Cassandra и другие), а также все дополнительные, связанные с ним, затраты! В 2.0 эта функция готова к промышленному использованию. В Grafana Labs мы для себя уже переместили в этом направлении все наши кластера.


Дополнительная информация


Нет возможности запустить свой Loki, либо просто хотите попробовать все это в действии? Активируйте испытательный период в 30 дней в Grafana Cloud! В облаке вы получите экземпляр Loki, связанный с Grafana и Alertmanager с новым интерфейсом правил уведомлений, с которым вы легко запустите процесс сбора журналов и создания уведомлений за несколько минут!


Для начала работы обратитесь к странице выпуска, также есть исчерпывающее руководство для обновления вашего кластера Loki. Там есть несколько вещей, на которые стоит обратить внимание!


А еще у нас есть шикарнейшее видео от Ward Bekker с исследованием и обзором новых функций в 2.0.



От редакции: Подробнее о работе с Loki можно узнать на курсе Слёрма Мониторинг и логирование инфраструктуры в Kubernetes. Курс от основ до продвинутого уровня для быстрого ввода в эксплуатацию мониторинга и логирования инфраструктуры.

Подробнее..

Интеграция Росплатформы с grafanaprometheus через consul

18.12.2020 02:10:01 | Автор: admin


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

Былой опыт


Ранее несколько лет назад был 5 летний опыт работы с СУБД Oracle в среде RISC-овой архитектуры на базе IBM, c их очень хорошей юникс подобной ОС AIX c своим прекрасным инструментом smitty, и все это еще разворачивалось на аппаратной виртуализации PowerVM, где можно настраивать балансировку на базе двух VIOS и т.д.



За всем этим набором как-то надо было следить, особенно за БД, и у всех этих программ были свои средства мониторинга, но вдохновлял меня на тот момент самый красивый и имеющий дашборды для всех этих компонентов, инструмент под названием spotlight от компании Quest.


Дашборд Spotlight

Прошло время пришлось работать с другими технологиями, как и многие попал в течение тенденций в сторону открытого ПО, где проприетарный spotlight конечно не очень котируется. Избалованный всякими простыми, платными удобностями, сквозь негативные эмоции, пользовался средствами от разработчиков свободного ПО и иногда ностальгировал по spotlight. Например Zabbix как-то не особо привлекал к себе внимание, но как только услышал про Grafana с Prometheus, само название меня уже заинтересовало, а когда увидел дашборды то, вспомнил про Spotlight. Хотя наверно и в Zabbix можно добиться такого же эффекта или даже использовать его через тот же Grafana, но изначально как я понял без особой персонализации красивых дашбордов там нет, с таким же наборов возможностей, впрочем как и в простом Prometheus.

Соблазнительно-таинственный grafana+prometheus



Конечно главнее всего полезность мониторинга, а не его красота, но для админа и инженеров всяких кластерных систем это все рано или поздно становится рутиной, и хочется тоже как-то чувствовать современный приятный дизайн, превращая эту рутину в интересную красивую игру. Чем больше я узнавал про Grafana плюс Prometheus, тем больше меня это привлекало, особенно своими названиями, красивыми дашбордами с графиками, бесплатностью, и даже не смотря на весь на мой взгляд геморой, в реализации этой связки со ложными системами на микросервисной архитектуре, с которым мне пришлось столкнутся.



Но кто работает с открытым ПО для них эти трудности привычное дело, и они даже получают удовольствие от этого садомазохизма, где даже самые простые вещи необходимо делать самому, своими ручками. Что противоречит например таким понятиям как: клиент недолжен парится, что там скрывается под капотом, главное простота, удобство, предоставляющие надежность, быстрый оперативный результат при выполнении задач клиента и т.д. Поэтому дальнейшее описание интеграции у меня язык не поворачивается назвать удобной и простой, а красота как говорится требует жертв, но для неизбалованных любителей linux это все просто как два пальца об асфальт)



На самом деле для Grafana и Prometheus уже есть много разработанных готовых дашбордов и экспортеров метрик от различных систем, что делает его простым и удобным для использования, но когда идет речь о чем-то своеобразном то, необходимо немного заморачиваться и хорошо если разработчики постарались, написали метрики к своим сервисам, а бывает что приходится самому варганить эти метрики. Тогда уже может невольно прийти желание использовать другие средства мониторинга, например как в Росплатформе если недостаточно встроенных предлагается Zabbix, это лучше чем возится с разработкой метрик каждого сервиса под Prometheus.

Мониторинг Росплатформы



В Росплатформе конечно есть еще и свои встроенные средства мониторинга, например в веб UI для SDS vstorage или для гипервизора с виртуальными средами или для таких сервисов на экспорт как s3, iscsi.


Главный дашборд SDS-а(Р-хранилище) Росплатформы


Дашборд одной из нод кластера Росплатформы(в Р-хранилище)


Дашборд Росплатфомы для s3 в Р-хранилище


Мониторинг виртуальной машины Росплатформы(В Р-управлении виртуализации)

Есть даже CLI мониторинг SDS(Р-хранилища) сердце Росплатформы #vstorage c имякластера top

Мониторинг SDS(Р-хранилища) Росплатформы через CLI

Но когда необходим более детальный мониторинг по каждому сервису/службе то, тут уже необходимо что-то другое, а в Росплатформе как в инфраструктурной экосистеме, сервисов немало. И разработчики как оказалось работают в этом направлении и можно даже увидеть результат их деятельности, если в развернутом кластере Росплатформы посмотреть на сервисы SDS через команду #netstat tunap | grep имясервиса, где имя сервиса например cs служба чанк сервера.
И мы можем увидеть вывод:



Где есть адрес 0.0.0.0 и порт 37548 который можно прослушать через команду
#curl localhost: 37548/metrics




И мы увидим целую кучу метрик, которые как раз подходят для Prometheus и для рисования графиков с привлекательным интерфейсом в Grafana.



По мимо этих просто чудо метрик есть конечно возможность использовать обычные доступные всем экспортеры для Prometheus, потому что под капотом Росплатформы модифицированный гипервизор kvm-qemu плюс libvirt и операционная система на базе linux ядра. Разработчики Росплатформы также планируют добавить в свой репозиторий эти экспортеры с модификацией под новую версию, но можно не дожидаясь воспользоваться уже сейчас выше описанными. В этой статье попробую описать все таки как можно работать с чудо метриками отдельных сервисов так как это оказалось более сложнее чем просто готовые экспортеры.



В общем сама Grafana умеет только рисовать, а для сбора метрик она использует различные сборщики, одним из которых является выше описанный Prometheus. Он конечно сам умеет рисовать графики и имеет свой веб ui, но это далеко от Grafana.


Веб ui от Prometheus


Дашборд Grafana

Модный Prometheus как и Grafana, тоже может только видеть метрики там где они лежат, или забирать их через экспортеры, в которых описывают места вывода метрик. Все это конечно можно настроить в конфиге Prometheus, и в экспортере добавить адреса(цели), по которым будут транслироваться метрики.

Service Discovery



Изначально мне показалось, что всего этого будет достаточно чтобы замутить свой мониторинг на базе этих прекрасных инструментов, но не тут-то было. Дело в том, что например для микросервисов SDS-а нет готовых экспортеров, для каждой его службы, да и вряд ли появятся, так как сервис работающий для одного диска может появляться и исчезать, если этот диск например заменить или добавить новые диски, или сервисы s3/iscsi при масштабировании могут плодится и т.д. И что получается каждый новый сервис прописывать в экспортере или в конфиге Prometheus, где для каждого свой уникальный порт?

Можно конечно написать целую программу под это дело, но это уже другая история, и хочется как-то менее рутинным и более легким путем. Покапавшись в гугле узнал, что есть еще программы service discover и одна из самых популярных для Prometheus это Сonsul.



Насмотревшись про него видео и изучив его возможности оказалось, что в нем можно просто зарегистрировать/ отрегистрировать сервисы с их портами для последующей передачи Prometheus, но сам он конечно ничего не ищет, как это описывают на первый взгляд в многих статьях и документации этого инструмента. То есть искать он может разными способами (DNS, HTTP API, RPC) уже у себя внутри среди зарегистрированных в нем сервисах.

В результате можно вернутся к нашей команде #netstat, и выполнять эту команду через Ansible или написать скрипт под планировщик задач с помощью которого будут сканироваться наши сервисы netstat-ом. Далее каждый найденный сервис наш скрипт будет регистрировать в Сonsul командой
#curl --request PUT --data @services.json localhost:8500/v1/agent/service/register

Где файл services.json это описание сервиса в этом формате:
 {  "services":[{  "name":"cs",  "tags":["csid=1026"],  "address":"127.0.0.1",  "port":33074},{  "name":"mds",  "address":"127.0.0.1",  "tags":["mdsid=2"],  "port": 9100}]}

В данном примере описываются два сервиса это чанк сервер cs и служба метаданных SDS Росплатформы mds.
Отрегистрировать также можно устроить с помощью одного и того же скрипта, который будет проверять доступность метрик от этого сервиса по его порту и в случае пустого ответа выкидывать этот сервис из Consul по команде:
#curl --request PUT http://127.0.0.1:8500/v1/agent/service/deregister/my-service-id


Есть конечно еще путь эмулировать API Consul, чтобы Prometheus думал, что он обращается к Consul, а на самом деле к ngnix, где ему подкладывал бы в формате json список сервисов этот же скрипт. Но это уже опять другая история, близкая к разработке. Можно оставить сам консул, который идет в виде отдельно выполняемого файла, в связи с чем его можно расположить на SDS для отказоустойчивости вместо его кластерной настройки, которую также можно осуществить, но это усложняет инструкцию и выходит за рамки этого описания.

Далее после того как у нас запущен Consul с необходимыми зарегистрированными сервисами, надо установить и настроить Prometheus. Можно это сделать в виртуальной среде, а на каждой ноде только его экспортер. Например в Росплатформе он уже предустановлен в контейнере vstorage-ui управления SDS-ом(Р-хранилище), остается только установить экспортеры на ноды и прописать их в конфиге Prometheus.

В его конфиге также можно прописать правила выборки метрик далее адрес и порт Consul, и регулярные выражения с метками для фильтрации нужных значений.
После этого мы можем устанавливать Grafana можно на этом же узле или даже на клиенте на своем ноутбуке, а можно как в моем варианте в гостевой машине, где в настройках Grafana указать сборщик данных Prometheus с адресом на наш установленный и его портом.

Если пройти в раздел explore то, можно проверить нашу работу, нажав на кнопку метрики, где у вас появится меню/список с разделами метрик.



Установка настройка Consul





В выше описанной краткой инструкции я опустил настройку конфигурационного файла Prometheus,
но для начала установим и запустим сам Consul на одной из нод кластера Росплатформы(Р-виртуализации):
Можно скачать его следующей командой
#wget  https://releases.hashicorp.com/consul/1.9.1/сonsul_1.9.1_linux_amd64.zip

Распаковываем его
# unzip сonsul_1.9.1_linux_amd64.zip

B сразу можно запустить проверить
#./consul v

Для начала чтобы не заморачиваться со автоскриптом по поиску и регистрации сервисов служб SDS-а Росплатформы в Consul, описанным выше, попробуем просто создать папку с прописанными службами в файле json.
#mkdir consul.d

И внутри этой папки создадим файл
#vi services.json

Со следующим содержимом
{  "services":[{  "name":"cs",  "tags":["csid=1026"],  "address":"127.0.0.1",  "port":33074},{  "name":"mds",  "address":"127.0.0.1",  "tags":["mdsid=2"],  "port": 9100}]}

Где 1026 это id службы чанк сервера, которую можно увидеть по команде
#vstorage c имя_вашего_кластера list-services



По ней также можно увидеть mdsid

Порты можно посмотреть через #netstat tunap | grep cs или mds в строке с адресом 0.0.0.0 с протоколом tcp.
После этого можно проверить запустить наш Consul
#consul agent -dev -enable-script-checks -config-dir=./consul.d

На экран будут выводится сообщения, можно это окно закрыть consul продолжит работать в фоновом режиме, для его перезагрузки можно воспользоваться командой
#consul reload

Можно проверить работу Consul через команду
#curl localhost:8500/v1/catalog/services

Он должен вывести наши зарегистрированные сервисы



И можно еще проверить каждый сервис:


Установка настройка Prometheus





Теперь можно установить Prometheus прям на ноду чтобы пока не возится с Prometheus в vstorage-ui
#wget https://github.com/prometheus/prometheus/releases/download/v2.23.0/prometheus-2.23.0.linux-amd64.tar.gz#mkdir /etc/Prometheus#mkdir /var/lib/Prometheus#tar zxvf prometheus-2.23.0.linux-amd64.tar.gz#cd prometheus-*.linux-amd64#cp prometheus promtool /usr/local/bin/#cp -r console_libraries consoles prometheus.yml /etc/Prometheus#useradd --no-create-home --shell /bin/false Prometheus#chown -R prometheus:prometheus /etc/prometheus /var/lib/Prometheus#chown prometheus:prometheus /usr/local/bin/{prometheus,promtool}

Как запустить и прописать в автозапуск в виде сервиса смотрим здесь
Редактируем наш конфиг файл Prometheus:
#vi /etc/systemd/system/prometheus.service

global:  scrape_interval:     1m  evaluation_interval: 1malerting:  alertmanagers:  - static_configs:    - targets:      - localhost:9093rule_files:- /var/lib/prometheus/rules/*.rules- /var/lib/prometheus/alerts/*.rules  - job_name: consul    honor_labels: true    consul_sd_configs:    - server: '127.0.0.1:8500'  #адрес и порт Consul       datacenter: 'dc1'   # к какому датацентру Consul относится - опционально      scheme: http  # по какому протоколу/схеме взаимодействие    relabel_configs:    - source_labels: [__address__]      regex: (.*)[:].+      target_label: instance      replacement: '${1}'    - source_labels: [__meta_consul_service]      target_label: 'job'    - source_labels: [__meta_consul_node]      target_label: 'node'    - source_labels: [__meta_consul_tags]      regex: ',(?:[^,]+,){0}([^=]+)=([^,]+),.*'      target_label: '${1}'      replacement: '${2}' 

Здесь
Нам в помощь дока про конфиг, а в самом примере здесь некоторые строки с комментарием.
Теперь можно запустить Prometheus проверить его работоспособность
#systemctl start prometheus.service#systemctl status prometheus.service

Пройти через браузер по адресу адрес_ноды_где_установлен_Prometheus:9090


И потом пройти в меню status -> targets



И провалится например по ссылке 127.0.0.1:33074 /metrics где мы увидим наши метрики от службы чанк сервера


К каждой строке есть комментарий

Установка настройка Grafana





Далее устанавливаем grafana
Я установил у себя на ноутбуке на windows 10 и зашел через браузер по адресу localhost:3000
Далее подключился к серверу к ноде с установленным Prometheus


Теперь проходим в меню manage и создаем наш новый дашборд.


Выбираем добавить новую панель

Можно ее назвать например memory use, для того чтобы попробовать отобразить использование памяти сервера нашей выше описанной службы чанк сервер.
На вкладе query выбрать из выпадающего списка datasource Prometheus, который мы ранее настроили на наш сервер(Р-виртуализации) Росплатформы с прослушивающим портом 9090.
Далее в поле metrics мы должны вставить метрику, ее можно подобрать из списка всех метрик по описанию после слова HELP.



Находим process_swap_bytes использование swap в байтах. Еще можно взять process_resident_memory_bytes из комментария видно, что это использование памяти сервера.
И дополнительно взять process_swapin_delay_seconds задержка при передачи памяти swap в резидентную память.
В Grafana в дашборде можно создать переменную:



После этого редактируем панель


  • 1. Название панели memory use.
  • 2. Выбираем data sources в нашем случае это Prometheus.
  • 3. Добавляем описание например общий объем памяти и памяти подкачки, занятой CS, а также процент времени, затраченного на ожидание передачи памяти swap в резидентную память.
  • 4. Пишем первый запрос с именем метрики process_swap_bytes{job=cs,csid=$cs}, где указываем службу cs и переменную его id.
  • 5. Имя определения.
  • 6. Разрешение.




Добавляем еще query и прописываем туда аналогично как мы прописывали для swap,
Только в поле напротив metrics где B будет process_resident_memory_bytes{job=cs,csid="$cs"}, а в С будет instance:process_swapin_delay_seconds:rate5m{job=cs,csid="$cs"}


Здесь настраиваем цвет и шкалу графика


В результате должен получится вот такой график



На этом пока все, надеюсь это как-то поможет тем, кто интересуется настройкой своего мониторинга на базе Grafana и Prometheus плюс Consul для Росплатформы или других похожих систем.



Полезные ссылки:



Подробнее..

Перевод Сервер Prometheus и TLS

15.01.2021 16:07:39 | Автор: admin


Prometheus теперь поддерживает TLS и базовую аутентификацию для HTTP эндпоинтов.


Скрейпинг таргетов через HTTPS вместо HTTP поддерживается уже давно. Метрики можно собирать с поддержкой HTTPS, аутентификации по клиентским сертификатам и базовой аутентификации.


В прошлом году Node Exporter стал первым официальным экспортером, который нативно предоставляет метрики по HTTPS. Все подробности в предыдущем посте. На этой неделе (прим. переводчика: статья вышла 6 января 2021 года) мы встречаем Prometheus 2.24.0. В последнее время Prometheus радует нас крутыми новшествами это и TLS, и backfilling (обратное заполнение, тоже в версии 2.24) и даже переход на современный пользовательский интерфейс на React.


В этом посте мы расскажем о TLS и базовой аутентификации.


Здесь можно узнать больше о модели безопасности Prometheus и о том, как пожаловаться на уязвимости.


Если вы обнаружили в Prometheus, официальном экспортере или библиотеке какую-нибудь критическую уязвимость, не рассказывайте об этом публично, а сообщите нам, чтобы мы все исправили.


API для администрирования и управления жизненным циклом


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


При этом нужно будет защитить порт Prometheus, например, с помощью аутентификации.


Защита доступа к Prometheus


Раньше между клиентами и сервером Prometheus для защиты обычно ставили обратный прокси:



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


Если ваш сценарий попроще или нужно защитить трафик между обратным прокси и Prometheus, теперь у вас есть встроенные средства для защиты входящего HTTP-трафика.



Как настроить TLS


Посмотрим, как это работает на практике, на примере Prometheus на Linux.


Настройка рабочего каталога


Мы будем работать в отдельном каталоге:


$ mkdir ~/prometheus_tls_example$ cd ~/prometheus_tls_example

Создание TLS-сертификатов


Для начала создадим самоподписанный TLS-сертификат.


$ cd ~/prometheus_tls_example$ openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout prometheus.key -out prometheus.crt -subj "/C=BE/ST=Antwerp/L=Brasschaat/O=Inuits/CN=localhost" -addext "subjectAltName = DNS:localhost"

Здесь localhost это имя хоста для сервера Prometheus.


Создается два файла: prometheus.crt и prometheus.key.


Веб-конфигурация Prometheus


Скачиваем Prometheus v2.24.0, распаковываем, переносим сертификаты, которые создали выше:


$ cd ~/prometheus_tls_example$ wget https://github.com/prometheus/prometheus/releases/download/v2.24.0/prometheus-2.24.0.linux-amd64.tar.gz$ tar xvf prometheus-2.24.0.linux-amd64.tar.gz$ cp prometheus.crt prometheus.key prometheus-2.24.0.linux-amd64$ cd prometheus-2.24.0.linux-amd64

Сейчас нужно создать новый файл конфигурации. Мы не будем настраивать TLS и аутентификацию в основном файле конфигурации prometheus.yml. Это позволит нам перечитывать отдельный файл конфигурации при каждом запросе, чтобы на лету подхватывать новые учетки и сертификаты.


Создадим файл web.yml с конфигурацией TLS:


tls_server_config:  cert_file: prometheus.crt  key_file: prometheus.key

Запускаем сервер Prometheus, указывая --web.config.file в командной строке:


$ ./prometheus --web.config.file=web.yml[...]enabled and it cannot be disabled on the fly." http2=truelevel=info ts=2021-01-05T13:27:53.677Z caller=tls_config.go:223 component=webmsg="TLS is enabled." http2=true

Если мы видим это сообщение, сервер Prometheus запущен с поддержкой TLS.


Примечание: Все параметры TLS можно менять динамически, но если уж мы включили TLS, то без перезапуска Prometheus его не отключишь.


Подробности об этом дополнительном файле конфигурации смотрите в документации.


Проверка конфигурации TLS вручную


В curl проверим конфигурацию TLS. В новом терминале запустим пару команд для теста:


$ cd ~/prometheus_tls_example$ curl localhost:9090/metricsClient sent an HTTP request to an HTTPS server.$ curl --cacert prometheus.crt https://localhost:9090/metrics[...]

Вместо --cacert prometheus.crt можно передать -k, чтобы пропустить проверку
сертификата в curl.


Конфигурация скрейпа


Настроить TLS выборочно не получится если он включен, он распространяется на все эндпоинты. Это значит, что собственные метрики Prometheus тоже будет извлекать через TLS, поэтому настроим использование HTTPS.


Изменим задание prometheus в файле prometheus.yml:


global:  scrape_interval:     15s  evaluation_interval: 15sscrape_configs:  - job_name: 'prometheus'    scheme: https    tls_config:      ca_file: prometheus.crt    static_configs:    - targets: ['localhost:9090']

Для tls_config и scheme установим https. Полный список параметров клиента tls_config см. в конфигурации Prometheus.


Перечитаем конфигурацию Prometheus:


$ killall -HUP prometheus

Откроем https://localhost:9090/targets локально в браузере и увидим https://localhost:9090/metrics в списке таргетов.


Таргет имеет статус UP? Ура! Мы настроили TLS для сервера Prometheus и теперь собираем метрики с шифрованием.


Как настроить базовую аутентификацию


Давайте пойдем еще дальше и затребуем имя пользователя и пароль. TLS здесь не обязателен, но крайне рекомендуется (настраивать его мы уже умеем).


Веб-конфигурация


Для начала создадим хэш паролей (с помощью bcrypt). Для этого используем команду htpasswd (пакет apache2-utils или httpd-tools есть в дистрибутиве; если это не продакшен, можно найти генераторы bcrypt онлайн).


$ htpasswd -nBC 10 "" | tr -d ':\n'New password:Re-type new password:$2y$10$EYxs8IOG46m9CtpB/XlPxO1ei7E4BjAen0SUv6di7mD4keR/8JO6m

Для примера возьмем пароль inuitsdemo.


Добавим пользователя в файл веб-конфигурации Prometheus web.yml:


tls_server_config:  cert_file: prometheus.crt  key_file: prometheus.keybasic_auth_users:  prometheus: $2y$10$EYxs8IOG46m9CtpB/XlPxO1ei7E4BjAen0SUv6di7mD4keR/8JO6m

Примечание: В этом файле prometheus это имя пользователя.


Если Prometheus еще запущен, введите пароль для доступа к веб-интерфейсу по адресу https://127.0.0.1:9090, иначе на странице targets для таргета будет отображаться ошибка 401 Unauthorized.



Конфигурация Prometheus


Изменим prometheus.yml, чтобы поскрейпить имя пользователя и пароль.


global:  scrape_interval:     15s  evaluation_interval: 15sscrape_configs:  - job_name: 'prometheus'    scheme: https    basic_auth:      username: prometheus      password: inuitsdemo    tls_config:      ca_file: prometheus.crt    static_configs:    - targets: ['localhost:9090']

Перезагрузим конфигурацию Prometheus сигналом SIGHUP:


$ killall -HUP prometheus

Если все работает, Prometheus снова откроет страницу targets.



Promtool


У Prometheus есть свой инструмент командной строки promtool, которым теперь можно проверять и файлы веб-конфигурации:


$ ./promtool check web-config web.ymlweb.yml SUCCESS

Используйте любой инструмент автоматизации для удобного обновления файлов web.yml.


Grafana


Grafana поддерживает все необходимые функции для подключения к серверу Prometheus. Можно указать CA (наш prometheus.crt) или пропустить проверку сертификатов.



Заключение


Это только общий обзор. В продакшене нужно будет использовать подходящий CA и продумать еще много деталей. Можно настроить клиентские сертификаты, но из соображений безопасности эта функция считается экспериментальной. Со временем поддерживаемая версия TLS может меняться, а с ней и поведение функций.


В следующие несколько месяцев мы планируем развернуть эту поддержку HTTPS по всем официальным экспортерам Prometheus и другим проектам, например, Alertmanager, Pushgateway.


Мы за безопасный мониторинг.


От редакции: Подробнее о работе с Prometheus можно узнать на курсе Слёрма Мониторинг и логирование инфраструктуры в Kubernetes. Сейчас курс находится в разработке и его можно купить по цене предзаказа.


Полезные ссылки


Prometheus 2.24.0
Модель безопасности Prometheus
Документация по конфигурации TLS-сервера (для Prometheus)
Документация по конфигурации TLS-клиента (для сервера Prometheus)

Подробнее..

Вот это скорость! Как мы подружили наш UBA-модуль с ClickHouse и что из этого вышло

02.02.2021 10:16:21 | Автор: admin
В прошлом году мы выпустили мажорную версию своего продукта Solar Dozor 7. В новую версию нашей DLP-системы вошел модуль продвинутого анализа поведения пользователей UBA. При его создании мы попробовали разные базы данных, но по совокупности критериев (о них скажем ниже) в итоге остановились на ClickHouse.

Освоить ClickHouse местами было непросто, многое стало для нас откровением, но главное преимущество этой СУБД затмевает все её недостатки. Как вы поняли из заголовка, речь о скорости. По этому параметру ClickHouse оставляет далеко позади традиционные коммерческие базы данных, которые мы в своих продуктах, в том числе в Solar Dozor, тоже используем.

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


Кадры из мультфильма Турбо (2013 год)

О модуле UBA и его архитектуре


Принцип работы модуля UBA в том, что он анализирует активность сотрудников в реальном времени, проверяя корпоративную почту, и формирует личный профиль устойчивого поведения пользователя. В случае отклонений Solar Dozor сообщает о них, чтобы можно было вовремя заметить нехарактерные действия сотрудников.

Все данные, которые нужны UBA-модулю, лежат в ClickHouse. Его мы ставим заказчику на ту же машину, куда инсталлируем и сам Solar Dozor. В базе храним не письма, а метаданные сообщений, то есть кто, когда, с кем переписывался, какова тема письма, какие вложения оно содержит и т. д. Время хранения можно настраивать, нам для расчетов нужен 90-дневный период. Поддержкой UBA-модуля занимаемся мы, частично это могут делать и админы клиента. В любом случае задача разработчиков максимально автоматизировать администрирование БД.

Идем дальше. Основную работу делает UBA-сервер, который мы писали на Clojure. Он обрабатывает данные, лежащие в ClickHouse, и производит расчеты. UBA-сервер запоминает последнюю дату для обработанных данных и периодически проверяет, есть ли в ClickHouse более свежие данные, чем в расчете. Если есть, то старые результаты удаляются и производится перерасчет.

Еще один компонент системы модуль Indexer, написанный на Scala. Он умеет работать с разными источниками данных. В случае с UBA у Indexer две задачи. Первая вытаскивать метаданные писем пользователей из основной БД и отправлять их в ClickHouse. Вторая выступать механизмом буферизации и грузить данные пачками. Когда перейдем к подробностям работы с ClickHouse, я расскажу об этом подробнее.

Для визуализации информации мы активно используем Grafana как при разработке, так и при добавлении новых фич в готовый продукт. Конечно, мы хотим перетащить все в свой интерфейс, но пока от этой палочки-выручалочки отказаться сложно. На некоторые графики в Grafana мы потратим пару часов, а разработчику понадобилась бы неделя, чтобы сделать то же самое стандартными средствами. Обычно это непозволительная роскошь. Поэтому, если дизайнеры не очень сильно придираются к цвету графика или ширине надписи, Grafana это спасение, ведь время у разработчиков расписано по минутам. Хотя это дополнительный софт, и в случае с некоторыми нашими клиентами иногда все упирается в сертификацию.

Сейчас вокруг ClickHouse появляются дополнительные средства разработки. В частности, мы используем Metabase это удобное средство прототипирования и быстрого моделирования расчетов.

Скорость. Я скорость!


Сначала несколько слов о том, какими критериями мы руководствовались, выбирая СУБД для UBA-модуля. Нас интересовали:

  • стоимость лицензии;
  • скорость обработки;
  • минимальный объем хранения;
  • скорость разработки;
  • минимальное администрирование;
  • минимальные требования к железу.


По первым четырем пунктам ClickHouse уверенно обошел конкурентов, и мы остановились на нем. Поговорим о главном преимуществе ClickHouse, ну, кроме того, что он бесплатный :-)

Скорость, скорость и еще раз скорость. Это главное, почему мы взяли его на борт. С ClickHouse вы сможете обрабатывать миллион записей в секунду на относительно скромных мощностях. Традиционные базы для такого требуют куда более серьезного железа. Скорость работы объясняется несколькими причинами:

  • Принципиально иной метод хранения данных ClickHouse делает их компрессию, архивирует и хранит по колонкам. Соответственно, нагрузка на диск снижается.
  • В ClickHouse распараллеливание задач на несколько потоков не требует дополнительных усилий. СУБД использует все доступные процессоры сервера без вмешательства админа. Если в случае с традиционной базой для этого придется серьезно заморочиться, то ClickHouse делает это по умолчанию. Нам, наоборот, приходится его ограничивать, чтобы остальным сервисам что-то осталось.
  • ClickHouse использует специальные инструкции процессора SSE, AVX, благодаря чему быстро перелопачивает большие объемы данных в оперативке. Тут логика простая: будучи созданным недавно, ClickHouse рассчитан на новое железо и его новые возможности.


Благодаря этому UBA-модуль может быстро строить аналитику по сообщениям пользователя и искать отклонения. В традиционных базах данных несколько миллионов записей будут обрабатываться достаточно долго, в ClickHouse при правильно написанном запросе это можно сделать за секунды.

ClickHouse дает преимущества и на этапе разработки благодаря его быстроте мы можем постоянно проводить эксперименты по обработке данных и проверять гипотезы. И не тратим на это кучу времени.

Какую сборку выбрать


ClickHouse разработали в Яндексе изначально для своих нужд. Но тогда собственные сборки они не выпускали. Это делала сторонняя иностранная компания Altinity.

Многие наши заказчики российские компании, поэтому нам такой вариант не очень подходил, и мы стали собирать ClickHouse сами. Брали исходный код от Яндекса и генерили бинарные пакеты. Сказать, что были сложности, значит, ничего не сказать. Приключений хватало, на них тратилась куча ресурсов и времени. И при этом мы получали утечку памяти, которой в сборках от Altinity не было. По мере работы ClickHouse потреблял все больше памяти. В результате она кончалась, и тот падал. Поэтому мы решили уйти от самостоятельной сборки. Теперь проще есть бинарники от самого Яндекса, в крайнем случае можно взять вариант от Altinity.

Обновления выходят довольно часто, но с ними нужно быть осторожными. В каждой новой версии ClickHouse есть новые фишки, исправления, но, как следствие, и новые баги. Разработчики Яндекса не очень переживают, если новая версия может привести к проблемам с совместимостью. Да, для нас это проблема, а они могут не заморачиваться и добавлять новые функции, исходя из своих потребностей и не тестируя все возможные сочетания функций. Но по мере внедрения ClickHouse в большем количестве крупных компаний отношение разработчиков Яндекса к клиентам постепенно смягчается.

Изначально ClickHouse был NoSQL-СУБД, но теперь он понимает SQL. Это тоже добавило нам немного проблем. Мы использовали прежний вариант, а потом некоторые старые команды поменяли свой смысл. (Оффтоп: лучше не забывать выносить код SQL-запросов из основного кода приложения. Иначе потребуется доработка исходника при самых тривиальных изменениях. Если этого не сделать на этапе разработки, то в нашем случае при необходимости исправить тот или иной запрос у клиента придется ждать выхода новой версии продукта).

Поэтому при обновлении мы берем недавнюю версию и начинаем на ней тестироваться. И если ошибки возникают, мы можем поискать более новую или более старую версию, где их не будет. Из-за этого процесс затягивается. В любом случае тестируйте свой код при обновлении ClickHouse, даже если оно минорное. Тут нужно соотносить выгоду от новых фишек с рисками и с объемом тестирования.



Эгоист и альтруист одновременно


С традиционными базами данных история простая: настроили использовать 20 Гб памяти они будут их использовать и никому не отдадут. С ClickHouse все иначе. Резервировать память под него не получится. Он будет использовать все, что найдет. Но и умеет делиться ClickHouse отдает память, если в данный момент она ему не нужна. С одной стороны, эта особенность позволяет нам развернуть на одной машине несколько сервисов. А с другой стороны, нам приходится ограничивать ClickHouse, так как другие модули Solar Dozor тоже хотят кушать, а он делится памятью только тогда, когда самому не надо. Поэтому прописываем такие параметры:

<max_memory_usage>
<max_memory_usage_for_user>
<max_memory_usage_for_all_queries>

<max_bytes_before_external_sort>
<max_bytes_before_external_group_by>

Число потоков max_threads также влияет на потребление. Поэтому можно поколдовать и с этим параметром. Он определяет параллельность работы ClickHouse. Если уменьшим ее в два раза, то и потребление памяти при параллельных операциях тоже снизится в два раза.

Как я уже сказал, обычно клиент выделяет нам под Solar Dozor одну машину, на ней, кроме UBA, установлены и все остальные модули. Поэтому у истории с бескорыстным ClickHouse есть и обратная сторона. Другой софт может сожрать всю отданную память, и тому уже ничего не достанется, придет OOM Killer. Конечно, было бы хорошо резервировать под ClickHouse определенный объем памяти, но пока такой функции нет.

О правильном секционировании и удачной сортировке


Мы секционировали табличку с метаданными сообщений (кто, кому, когда, с какой темой, какие вложения, размер) по дням. Вообще, для ClickHouse это очень мелкое дробление, но у нас большой объем данных. Поэтому в наших условиях день это оптимальная единица и с точки зрения структуры данных, и с точки зрения администрирования системы.



Таблицу расчетов мы секционировали по месяцам. Понятно, что их объем меньше, чем объем самих данных, поэтому тут агрегация по месяцам удобнее.

Общее правило простое: не нужно делать слишком много секций, даже если кажется, что ваш случай точно требует мелкого дробления данных. Больше секций больше файлов на диске, а значит, операционная система будет тупить. Если нам нужно что-то быстро отобразить в интерфейсе, вытаскивать большой объем данных из одного файла быстрее, чем из множества.

С сортировкой своя история. Данные столбца физически хранятся в отдельном файле в сжатом виде в порядке, указанном в параметре сортировки. При хорошем сжатии файл получается небольшим, и чтение с диска занимает минимальное время. Если данные отсортировать правильно, то одинаковые значения будут храниться рядом, а алгоритмы компрессии ClickHouse хорошо сожмут их. Данные всех столбцов должны быть физически отсортированы в одном и том же порядке. Поэтому, если в таблице много столбцов, надо выбирать, по какому набору столбцов нужно сделать сортировку. Если скомпоновать и сжать эти данные неправильно, интерфейс или пакетная обработка будут тормозить.

И несколько слов о запросах. Их условия должны содержать поле секционирования. Оптимизатор запросов в ClickHouse пока далек от того, что есть в других базах (например, PostgreSQL и Oraсle). Он не понимает зависимости данных между столбцами. Чтобы минимизировать объемы данных, считываемых с диска, нужно явно указывать, какие диапазоны данных нужны для запроса. Условие запроса для этого должно содержать границы данных по условию секционирования. В идеале чтобы каждый запрос доставал данные из одной секции, то есть указываем: сходи в конкретный день и ищи только там.

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

Любитель есть большими порциями


Если до этого вы работали только с традиционными базами данных, придется перестраиваться. С ClickHouse не прокатит каждый раз вставлять по строчке: он не любит частых вставок. Если у нас много источников данных и каждый вставляет по одной строчке, то ClickHouse становится плохо. То есть, например, он выдерживает 100 вставок в секунду. Вы спросите: Как же так? 100 вставок и все?.. А где же миллион в секунду, о котором говорили? Оказывается, ClickHouse сделан так, что он может пережить 100 вставок в секунду, а в каждой вставке при этом 10 000 строк. Вот вам и тот самый миллион.

Вообще, для себя мы выработали правило: данные нужно вставлять не чаще чем раз в 10 секунд. Так мы получаем необходимую скорость и стабильную работу.

Но нужна промежуточная буферизация, которая накопит этот миллион. Этим у нас как раз занимается Indexer, который я уже упоминал.



В Clickhouse есть такая фишка, как буферные таблицы, но это полумера. С одной стороны, они позволяют волшебным образом исправить ситуацию с частыми вставками без переделки серверного кода. На нашем железе в буферную таблицу можно вставлять в 10 раз чаще, чем в обычную. Но при этом память приходится распределять вручную. Для каждой буферной таблицы нужно назначить заранее определенное число и размер буферов. И использовать эту память под что-то другое будет нельзя. Хорошо бы угадать с этими параметрами сразу, иначе эти таблицы придется пересоздавать.

Поэтому самый правильный вариант буферизация в приложении, что мы и сделали на этапе разработки. Несмотря на эти танцы с бубном, оно того стоит: в традиционной базе данных вставить даже 1000 строк в секунду, скорее всего, не получится даже на очень хорошем железе.

Про мутации


В самых первых версиях ClickHouse не было команд изменения данных. Можно было загрузить табличку и удалить. Потом добавили механизм изменения данных, который называется мутацией. И это еще одна штука, которую нужно понять и принять.

В традиционных базах данных вставил строчку, удалил строчку сразу видишь результат. Тут все иначе. В ClickHouse пишешь команду удаления, она ставится в очередь мутаций, и через некоторое время будет выполнена. То есть по факту это выглядит так: вот данные есть, и сейчас они еще есть, до сих пор есть, а теперь раз и исчезли. Данные могут измениться в любой момент и по другой причине: например, в результате оптимизации или дедупликации число строк может уменьшиться. В ClickHouse нет физических первичных и уникальных ключей. Поэтому в таблицах всегда есть дубли (а если нет, то будут). Данные могут никогда не схлопнуться и/или никогда не дедуплицироваться. Кстати, опция FINAL не работает, если есть незаконченные мутации.

За скорость приходится платить дополнительными телодвижениями разработчиков в коде надо предусмотреть обработку задержек на выполнение мутации, чтобы пользователя это никак не коснулось. В частности, нужно писать код (запросы, логику обработки данных) так, чтобы не падать, если данные не соответствуют идеальному представлению о них.



У нас исходные данные, которые хранятся в ClickHouse, не удаляются и не меняются (за исключением добавления новых данных и удаления старых по партициям). Меняются только расчеты. Мы пересчитываем результаты при появлении новых данных, и у нас могут измениться алгоритмы расчетов при установке новой версии. Все это не требует построчных изменений в данных (операции update и delete). Поэтому в нашем случае очень длинных очередей мутаций не бывает.

Не злоупотребляйте словарями


В ClickHouse есть такой интерфейс словари, куда можно положить любую команду, которая вытащит список из какого-то источника. Например, если нужно залезть во внешний файл со списком сотрудников организации или в Active Directory.

Словари нужны для таких меняющихся данных, как должность, телефон, семейное положение сотрудника и т. п. Если их корректное отображение критично, то словарь необходим. Все, что не меняется, нужно класть в обычные таблицы ClickHouse. Наиболее удобным для нас оказался словарь в JSON, получаемый через http-запрос, самый естественный и самый надежный. Пишем команду сходить на такой-то сервер и взять то, что нам нужно.

В других БД все это сделать сложнее, надо писать специальную программу, которая будет доставать списки из внешних источников. В ClickHouse никаких дополнительных действий не нужно. Он достанет данные из внешнего источника и будет работать с ними как с таблицей.

Словари нужно создавать, поддерживать, дебажить. Но надо помнить, что каждый словарь дополнительная зависимость от внешнего источника и потенциальное место возникновения проблемы, если при запросе окажется, что с источником что-то не так.

Как повысить надежность?


В отличие от традиционных баз данных ClickHouse не гарантирует сохранность всех данных на все 100%. Вообще, для решения этой задачи существуют специальные механизмы транзакций, откатов изменений и восстановления после сбоев. В ClickHouse все это либо не реализовано, либо сделано в минимальном объеме. Это тоже своего рода плата за скорость. Впрочем, если сильно заморочиться, то можно повысить надежность. Но придется строить кластер систему из нескольких серверов, на которые мы установим ClickHouse и сервис для распределенных систем ZooKeeper. Будем делать бэкапы, репликацию данных. Очевидно, что это потребляет дополнительные ресурсы, место на дисках, производительность и т. д.

Тут надо обратить внимание на три момента.
  1. Проектирование
    Если спроектировать кластер неправильно, отказ любого компонента может привести к отказу всего кластера. В каждом конкретном случае выбор схемы будет разным. И нужно понимать, от каких аварий конкретная схема защищает, а от каких нет. Но это тема для отдельной статьи.
  2. Обслуживание
    Все процедуры надо четко описать и протестировать. Ну и вообще, не забывать про золотое правило: работает не трогай!
  3. Тестирование изменений на идентичном стенде
    Любые изменения и обновления надо проверять не на уже работающей системе, а на тестовой. Потому что, если что, смотри пункт 2.

В общем, средства повышения надежности совершенно точно понижают производительность, а если накосячить, то и саму надежность.

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

В любом случае тут все упирается в ваши возможности: стоимость железа, сетевой инфраструктуры, обучения персонала и т. д. нужно соизмерять с реальными потребностями в высокой доступности. Если таких ресурсов нет, может быть, повышенная надежность не так уж и нужна? Понятно, что ClickHouse история не про банковские или другие критичные данные.

И напоследок: проверьте железо заранее


Казалось бы, железо без SSE 4.2 сейчас почти не встретить. Но однажды нам достался мощнейший сервер с процессором, который эти инструкции не поддерживает. А для ClickHouse поддержка SSE 4.2 одно из системных требований. Оказалось, что закончился старый проект, железо хорошее, не выбрасывать же.

Яндекс рекомендует выделять под ClickHouse отдельный сервер как минимум с 30 Гб оперативки. В наших условиях никто не даст под БД отдельное железо. Как я уже говорил, на одном сервере у заказчиков крутится весь Solar Dozor со всеми его модулями и их компонентами. Но, если все настроить правильно, полет пройдет нормально.

В общем, в нашем случае ClickHouse оказался хорошей альтернативой традиционным СУБД. Ему не нужны серьезные мощности, чтобы показывать впечатляющие результаты. ClickHouse делает более удобным процесс разработки, но к нему нужно привыкнуть, подружиться с его особенностями и реально оценивать уровень его возможностей.

Автор: Леонид Михайлов, ведущий инженер отдела проектирования Solar Dozor
Подробнее..

Зонтичная Grafana скрещиваем Zabbix и Microsoft SCOM

18.03.2021 18:13:59 | Автор: admin
Если у вас есть Grafana и несколько систем мониторинга, то почему бы не визуализировать все имеющиеся данные и статусы в едином интерфейсе?

image

Покажем на примере нашего тестового стенда как скрестить Zabbix и SCOM в единой Grafana и сделать сервисный мониторинг (с точки зрения здоровья сервисов). Подробности и скриншоты под катом.

На скриншоте ниже информационные системы компании. Здесь электронная почта, DNS, Active Directory, Sharepoint и другие. Каждый квадрат это агрегированный статус информационной системы. Ниже мы покажем за счет чего так получается. Обращаем внимание, что различные системы могут быт охвачены различными системами мониторинга. В нашем случае это Zabbix и SCOM.

image

Начнём c Zabbix. Там есть возможность создавать группы узлов. Каждому узлу соответствует набор триггеров. Что мы делаем дальше? Ищем наихудший статус триггера на узле и отдаём его значение в специальный элемент данных. Следом проводим аналогичное действие по агрегированию наихудшего статуса триггера для группы узлов и получаем агрегированный статус здоровья группы.

image

Уже после этого можно привязывать статусные элементы данных к плиткам в Grafana. На скриншоте ниже компоненты информационной системы Электронная почта. Каждая плитка это агрегированный статус здоровья сервисов системы. Каждая плитка это ссылка, можно провалиться и увидеть на каком узле проблема. Обратите внимание, что такая агрегация позволяет выводить вложенные события на каждом уровне (они под плитками).

image

Если проваливаться дальше, то можно дойти до уровня узла, где будут его метрики производительности. При желании, там можно сделать плитку с агрегированным статусов виртуализации, а дальше и сетевых устройств. Подход зависит от задач. С Zabbix разобрались, переходим к SCOM.

Интеграция Grafana со SCOM реализована при помощи SQL-запросов в базы OperationsManager и OperationsManagerDW. Первая для кратковременного хранения, вторая для долговременного. При помощи SQL-запроса получим список узлов, которые находятся на мониторинге в SCOM.

image

Настроим переменные, которые будут определять фильтры в Grafana и помогут в дальнейшем автоматизировать появление здесь новых узлов.

image

Другими SQL-запросами в SCOM можно получить статусы узлов (аналогично Zabbix) и список событий. Таким образом, нажав на плитку Active Directory на уровне информационных систем, можно перейти на представление с доменнных контроллеров и соответствующими событиями по ним.

image

Ну, а дальше посмотреть на значения отдельных метрик узла.

image

Ещё можно использовать вот такое представление с событиями одновременно для Zabbix и Microsoft SCOM.

image

Спасибо за внимание! Надеюсь, было интересно.

А ещё у нас есть:

Описание комплексного решения по мониторингу на открытых системах

Zabbix под замком: включаем опции безопасности компонентов Zabbix для доступа изнутри и снаружи

Добавляем CMDB и географическую карту к Zabbix


Если вас интересуют услуги внедрения, администрирования и поддержки Zabbix, вы можете оставить заявку в форме обратной связи на нашем сайте.
Подробнее..

Рефакторинг пет проекта докеризация, метрики, тесты

17.02.2021 20:19:33 | Автор: admin

Всем привет, я php разработчик. Я хочу поделиться историей, как я рефакторил один из своих телеграм ботов, который из поделки на коленке стал сервисом с более чем 1000 пользователей в очень узкой и специфической аудитории.

Предыстория

Пару лет назад я решил тряхнуть стариной и поиграть в LineAge II на одном из популярных пиратских серверов. В этой игре есть один игровой процесс, в котором требуется "поговорить" с ящиками после смерти 4 боссов. Ящик стоит после смерти 2 минуты. Сами боссы после смерти появляются спустя 24 +/- 6ч, то есть шанс появится есть как через 18ч, так и через 30ч. У меня на тот момент была фуллтайм работа, да и в целом не было времени ждать эти ящики. Но нескольким моим персонажам требовалось пройти этот квест, поэтому я решил "автоматизировать" этот процесс. На сайте сервера есть RSS фид в формет XML, где публикуются события с серверов, включая события смерти босса.

Задумка была следующей:

  • получить данные с RSS

  • сравнить данные с локальной копией в базе данных

  • если есть разница данных - сообщить об этом в телеграм канал

  • отдельно сообщать если босса не убили за первые 9ч сообщением "осталось 3ч", и "осталось 1,5ч". Допустим вечером пришло сообщение, что осталось 3ч, значит смерть босса будет до того, как я пойду спать.

Код на php был написан быстро и в итоге у меня было 3 php файла. Один был с god object классом, а другие два запускали программу в двух режимах - парсер новых, или проверка есть ли боссы на максимальном "респе". Запускал я их крон командами. Это работало и решало мою проблему.

Другие игроки замечали, что я появляюсь в игре сразу после смерти боссов, и через 10 дней у меня на канале было около 50 подписчиков. Так же попросили сделать такое же для второго сервера этого пиратского сервиса. Задачу я тоже решил копипастой. В итоге у меня уже 4 файла с почти одинаковым кодом, и файл с god object. Потом меня попросили сделать то же самое для третьего сервера этого пиратского сервиса. И это отлично работало полтора года.

В итоге у меня спустя полтора года:

  • у меня 6 файлов, дублируют себя почти полностью (по 2 файла на сервер)

  • один god object на несколько сотен строк

  • MySQL и Redis на сервере, где разместил код

  • cron задачи, которые запускают файлы

  • ~1400 подписчиков на канале в телеграм

Я откладывал месяцами рефакторинг этого кода, как говориться "работает - не трогай". Но хотелось этот проект привести в порядок, чтобы проще было вносить изменения, легче запускать и переносить на другой сервер, мониторить работоспособность и тд. При этом сделать это за выходные, в свое личное время.

Ожидаемый результат после рефакторинга

  1. Отрефакторить код так, чтобы легче было вносить изменения. Важный момент - отрефакторить без изменения бизнес логики, по сути раскидать god object по файлам, сам код не править, иначе это затянет сроки. Следовать PSR-12.

  2. Докеризировать воркера для удобства переноса на другой сервер и прозрачность запуска и остановки

  3. Запускать воркера через supervisor

  4. Внедрить процесс тестирования кода, настроить Codeception

  5. Докеризировать MySQL и Redis

  6. Настроить Github Actions для запуска тестов и проверки на code style

  7. Поднять Prometheus, Grafana для метрик и мониторинга работоспособности

  8. Сделать докер контейнер, который будет отдавать метрики на страницу /metrics для Prometheus

  9. Сделать докер образ для бота телеграм, который будет отдавать срез по всем статусам 4 боссов в данный момент командами боту в личку

Важное замечание. Все эти шаги выполнялись не совсем в том порядке, как я их описываю в этом туториале. Сделал все требуемое за выходные плюс пара вечеров после работы. Так же в целях не было сделать проект "идеальным", не совершать "революции", а дать возможность проекту плавно эволюционировать. Большая часть пунктов из плана давала возможность развивать проект.

Шаг 1. Рефакторинг приложения

Одним из требований было не потратить на это недели, поэтому основные классы я решил сделать наследниками Singleton

<?phpdeclare(strict_types=1);namespace AsteriosBot\Core\Support;use AsteriosBot\Core\Exception\DeserializeException;use AsteriosBot\Core\Exception\SerializeException;class Singleton{    protected static $instances = [];    /**     * Singleton constructor.     */    protected function __construct()    {        // do nothing    }    /**     * Disable clone object.     */    protected function __clone()    {        // do nothing    }    /**     * Disable serialize object.     *     * @throws SerializeException     */    public function __sleep()    {        throw new SerializeException("Cannot serialize singleton");    }    /**     * Disable deserialize object.     *     * @throws DeserializeException     */    public function __wakeup()    {        throw new DeserializeException("Cannot deserialize singleton");    }    /**     * @return static     */    public static function getInstance(): Singleton    {        $subclass = static::class;        if (!isset(self::$instances[$subclass])) {            self::$instances[$subclass] = new static();        }        return self::$instances[$subclass];    }}

Таким образом вызов любого класса, который от него наследуются, можно делать методом getInstance()

Вот так, например, выглядел класс подключения к базе данных

<?phpdeclare(strict_types=1);namespace AsteriosBot\Core\Connection;use AsteriosBot\Core\App;use AsteriosBot\Core\Support\Config;use AsteriosBot\Core\Support\Singleton;use FaaPz\PDO\Database as DB;class Database extends Singleton{    /**     * @var DB     */    protected DB $connection;    /**     * @var Config     */    protected Config $config;    /**     * Database constructor.     */    protected function __construct()    {        $this->config = App::getInstance()->getConfig();        $dto = $this->config->getDatabaseDTO();        $this->connection = new DB($dto->getDsn(), $dto->getUser(), $dto->getPassword());    }    /**     * @return DB     */    public function getConnection(): DB    {        return $this->connection;    }}

В процессе рефакторинга я не менял саму бизнес логику, оставил все "как было". Цель было именно разнести по файлам для облегчения изменения правок, а так же для возможности потом покрыть тестами.

Шаг 2: Докеризация воркеров

Запуск всех контейнеров я сделал через docker-compose.yml

Конфиг сервиса для воркеров выглядит так:

  worker:    build:      context: .      dockerfile: docker/worker/Dockerfile    container_name: 'asterios-bot-worker'    restart: always    volumes:      - .:/app/    networks:      - tier

А сам docker/worker/Dockerfile выглядит так:

FROM php:7.4.3-alpine3.11# Copy the application codeCOPY . /appRUN apk update && apk add --no-cache \    build-base shadow vim curl supervisor \    php7 \    php7-fpm \    php7-common \    php7-pdo \    php7-pdo_mysql \    php7-mysqli \    php7-mcrypt \    php7-mbstring \    php7-xml \    php7-simplexml \    php7-openssl \    php7-json \    php7-phar \    php7-zip \    php7-gd \    php7-dom \    php7-session \    php7-zlib \    php7-redis \    php7-session# Add and Enable PHP-PDO ExtenstionsRUN docker-php-ext-install pdo pdo_mysqlRUN docker-php-ext-enable pdo_mysql# RedisRUN apk add --no-cache pcre-dev $PHPIZE_DEPS \        && pecl install redis \        && docker-php-ext-enable redis.so# Install PHP ComposerRUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer# Remove CacheRUN rm -rf /var/cache/apk/*# setup supervisorADD docker/supervisor/asterios.conf /etc/supervisor/conf.d/asterios.confADD docker/supervisor/supervisord.conf /etc/supervisord.confVOLUME ["/app"]WORKDIR /appRUN composer installCMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

Обратите внимание на последнюю строку в Dockerfile, там я запускаю supervisord, который будет мониторить работу воркеров.

Шаг 3: Настройка supervisor

Важный дисклеймер по supervisor. Он предназначен для работы с процессами, которые работают долго, и в случае его "падения" - перезапустить. Мои же php скрипты работали быстро и сразу завершались. supervisor пробовал их перезапустить, и в конце концов переставал пытаться поднять снова. Поэтому я решил сам код воркера запускать на 1 минуту, чтобы это работало с supervisor.

Код файла worker.php

<?phprequire __DIR__ . '/vendor/autoload.php';use AsteriosBot\Channel\Checker;use AsteriosBot\Channel\Parser;use AsteriosBot\Core\App;use AsteriosBot\Core\Connection\Log;$app = App::getInstance();$checker = new Checker();$parser = new Parser();$servers = $app->getConfig()->getEnableServers();$logger = Log::getInstance()->getLogger();$expectedTime = time() + 60; // +1 min in seconds$oneSecond = time();while (true) {    $now = time();    if ($now >= $oneSecond) {        $oneSecond = $now + 1;        try {            foreach ($servers as $server) {                $parser->execute($server);                $checker->execute($server);            }        } catch (\Throwable $e) {            $logger->error($e->getMessage(), $e->getTrace());        }    }    if ($expectedTime < $now) {        die(0);    }}

У RSS есть защита от спама, поэтому пришлось сделать проверку на секунды и посылать не более 1го запроса в секунду. Таким образом мой воркер каждую секунду выполняет 2 действия, сначала проверяет rss, а затем калькулирует время боссов для сообщений о старте или окончании времени респауна боссов. После 1 минуты работы воркер умирает, и его перезапускает supervisor

Сам конфиг supervisor выглядит так:

[program:worker]command = php /app/worker.phpstderr_logfile=/app/logs/supervisor/worker.lognumprocs = 1user = rootstartsecs = 3startretries = 10exitcodes = 0,2stopsignal = SIGINTreloadsignal = SIGHUPstopwaitsecs = 10autostart = trueautorestart = truestdout_logfile = /dev/stdoutstdout_logfile_maxbytes = 0redirect_stderr = true

После старта контейнеров супервизор стартует воркера автоматически. Важный момент - в файле основного конфига /etc/supervisord.confобязательно нужно указать демонизация процесса, а так же подключение своих конфигов

[supervisord]nodaemon=true[include]files = /etc/supervisor/conf.d/*.conf

Набор полезных команд supervisorctl:

supervisorctl status       # статус воркеровsupervisorctl stop all     # остановить все воркераsupervisorctl start all    # запустить все воркераsupervisorctl start worker # запустить один воркера с конфига, блок [program:worker]

Шаг 4: Настройка Codeception

Я планирую в свободное время по чуть-чуть покрывать unit тестами уже существующий код, а со временем сделать еще и интеграционные. Пока что настроил только юнит тестирование и написал пару тестов на особо важную бизнес логику. Настройка была тривиальной, все завелось с коробки, только добавил в конфиг поддержку базы данны

# Codeception Test Suite Configuration## Suite for unit or integration tests.actor: UnitTestermodules:    enabled:        - Asserts        - \Helper\Unit        - Db:              dsn: 'mysql:host=mysql;port=3306;dbname=test_db;'              user: 'root'              password: 'password'              dump: 'tests/_data/dump.sql'              populate: true              cleanup: true              reconnect: true              waitlock: 10              initial_queries:                - 'CREATE DATABASE IF NOT EXISTS test_db;'                - 'USE test_db;'                - 'SET NAMES utf8;'    step_decorators: ~

Шаг 5: Докеризация MySQL и Redis

На сервере, где работало это приложение, у меня было еще пара других ботов. Все они использовали один сервер MySQL и один Redis для кеша. Я решил вынести все, что связано с окружением в отельный docker-compose.yml, а самих ботов залинковать через внешний docker network

Выглядит это так:

version: '3'services:  mysql:    image: mysql:5.7.22    container_name: 'telegram-bots-mysql'    restart: always    ports:      - "3306:3306"    environment:      MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}"      MYSQL_ROOT_HOST: '%'    volumes:      - ./docker/sql/dump.sql:/docker-entrypoint-initdb.d/dump.sql    networks:      - tier  redis:    container_name: 'telegram-bots-redis'    image: redis:3.2    restart: always    ports:      - "127.0.0.1:6379:6379/tcp"    networks:      - tier  pma:    image: phpmyadmin/phpmyadmin    container_name: 'telegram-bots-pma'    environment:      PMA_HOST: mysql      PMA_PORT: 3306      MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}"    ports:      - '8006:80'    networks:      - tiernetworks:  tier:    external:      name: telegram-bots-network

DB_PASSWORD я храню в .env файле, а ./docker/sql/dump.sql у меня лежит бекап для инициализации базы данных. Так же я добавил external network так же, как в этом конфиге - в каждом docker-compose.yml каждого бота на сервере. Таким образом они все находятся в одной сети и могут использовать общие базу данных и редис.

Шаг 6: Настройка Github Actions

В шаге 4 этого туториала я добавил тестовый фреймфорк Codeception, который для тестирования требует базу данных. В самом проекте нет базы, в шаге 5 я ее вынес отдельно и залинковал через external docker network. Для запуска тестов в Github Actions я решил полностью собрать все необходимое на лету так же через docker-compose.

name: Actionson:  pull_request:    branches: [master]  push:    branches: [master]jobs:  build:    runs-on: ubuntu-latest    steps:      - name: Checkout        uses: actions/checkout@v2      - name: Get Composer Cache Directory        id: composer-cache        run: |          echo "::set-output name=dir::$(composer config cache-files-dir)"      - uses: actions/cache@v1        with:          path: ${{ steps.composer-cache.outputs.dir }}          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}          restore-keys: |            ${{ runner.os }}-composer-      - name: Composer validate        run: composer validate      - name: Composer Install        run: composer install --dev --no-interaction --no-ansi --prefer-dist --no-suggest --ignore-platform-reqs      - name: PHPCS check        run: php vendor/bin/phpcs --standard=psr12 app/ -n      - name: Create env file        run: |          cp .env.github.actions .env      - name: Build the docker-compose stack        run: docker-compose -f docker-compose.github.actions.yml -p asterios-tests up -d      - name: Sleep        uses: jakejarvis/wait-action@master        with:          time: '30s'      - name: Run test suite        run: docker-compose -f docker-compose.github.actions.yml -p asterios-tests exec -T php vendor/bin/codecept run unit

Инструкция onуправляет когда билд триггернётся. В моем случае - при создании пулл реквеста или при коммите в мастер.

Инструкция uses: actions/checkout@v2 запускает проверку доступа процесса к репозиторию.

Далее идет проверка кеша композера, и установка пакетов, если в кеше не найдено

Затем в строке run: php vendor/bin/phpcs --standard=psr12 app/ -nя запускаю проверку кода соответствию стандарту PSR-12 в папке ./app

Так как тут у меня специфическое окружение, я подготовил файл .env.github.actionsкоторый копируется в .env Cодержимое .env.github.actions

SERVICE_ROLE=testTG_API=XXXXXTG_ADMIN_ID=123TG_NAME=AsteriosRBbotDB_HOST=mysqlDB_NAME=rootDB_PORT=3306DB_CHARSET=utf8DB_USERNAME=rootDB_PASSWORD=passwordLOG_PATH=./logs/DB_NAME_TEST=test_dbREDIS_HOST=redisREDIS_PORT=6379REDIS_DB=0SILENT_MODE=trueFILLER_MODE=true

Из важного тут только настройки базы данных, которые не должны отличаться от настроек базы в этом окружении.

Затем я собираю проект при помощи docker-compose.github.actions.ymlв котором прописано все необходимое для тестирвания, контейнер с проектом и база данных. Содержимое docker-compose.github.actions.yml:

version: '3'services:  php:    build:      context: .      dockerfile: docker/php/Dockerfile    container_name: 'asterios-tests-php'    volumes:      - .:/app/    networks:      - asterios-tests-network  mysql:    image: mysql:5.7.22    container_name: 'asterios-tests-mysql'    restart: always    ports:      - "3306:3306"    environment:      MYSQL_DATABASE: asterios      MYSQL_ROOT_PASSWORD: password    volumes:      - ./tests/_data/dump.sql:/docker-entrypoint-initdb.d/dump.sql    networks:      - asterios-tests-network##  redis:#    container_name: 'asterios-tests-redis'#    image: redis:3.2#    ports:#      - "127.0.0.1:6379:6379/tcp"#    networks:#      - asterios-tests-networknetworks:  asterios-tests-network:    driver: bridge

Я закомментировал контейнер с Redis, но оставил возможность использовать его в будущем. Сборка с кастомным docker-compose файлом, а затем тесты - запускается так

docker-compose -f docker-compose.github.actions.yml -p asterios-tests up -ddocker-compose -f docker-compose.github.actions.yml -p asterios-tests exec -T php vendor/bin/codecept run unit

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

Шаг 7: Настройка Prometheus и Grafana

В шаге 5 я вынес MySQL и Redis в отдельный docker-compose.yml. Так как Prometheus и Grafana тоже общие для всех моих телеграм ботов, я их добавил туда же. Сам конфиг этих контейнеров выглядит так:

  prometheus:    image: prom/prometheus:v2.0.0    command:      - '--config.file=/etc/prometheus/prometheus.yml'    restart: always    ports:      - 9090:9090    volumes:      - ./prometheus.yml:/etc/prometheus/prometheus.yml    networks:      - tier  grafana:    container_name: 'telegram-bots-grafana'    image: grafana/grafana:7.1.1    ports:      - 3000:3000    environment:      - GF_RENDERING_SERVER_URL=http://renderer:8081/render      - GF_RENDERING_CALLBACK_URL=http://grafana:3000/      - GF_LOG_FILTERS=rendering:debug    volumes:      - ./grafana.ini:/etc/grafana/grafana.ini      - grafanadata:/var/lib/grafana    networks:      - tier    restart: always  renderer:    image: grafana/grafana-image-renderer:latest    container_name: 'telegram-bots-grafana-renderer'    restart: always    ports:      - 8081    networks:      - tier

Они так же залинкованы одной сетью, которая потом линкуется с external docker network.

Prometheus: я прокидываю свой конфиг prometheus.yml, где я могу указать источники для парсинга метрик

Grafana: я создаю volume, где будут храниться конфиги и установленные плагины. Так же я прокидываю ссылку на сервис рендеринга графиков, который мне понадобиться для отправки alert. С этим плагином alert приходит со скриншотом графика.

Поднимаю проект и устанавливаю плагин, затем перезапускаю Grafana контейнер

docker-compose up -ddocker-compose exec grafana grafana-cli plugins install grafana-image-rendererdocker-compose stop  grafana docker-compose up -d grafana

Шаг 8: Публикация метрик приложения

Для сбора и публикации метрик я использовал endclothing/prometheus_client_php

Так выглядит мой класс для метрик

<?phpdeclare(strict_types=1);namespace AsteriosBot\Core\Connection;use AsteriosBot\Core\App;use AsteriosBot\Core\Support\Singleton;use Prometheus\CollectorRegistry;use Prometheus\Exception\MetricsRegistrationException;use Prometheus\Storage\Redis;class Metrics extends Singleton{    private const METRIC_HEALTH_CHECK_PREFIX = 'healthcheck_';    /**     * @var CollectorRegistry     */    private $registry;    protected function __construct()    {        $dto = App::getInstance()->getConfig()->getRedisDTO();        Redis::setDefaultOptions(            [                'host' => $dto->getHost(),                'port' => $dto->getPort(),                'database' => $dto->getDatabase(),                'password' => null,                'timeout' => 0.1, // in seconds                'read_timeout' => '10', // in seconds                'persistent_connections' => false            ]        );        $this->registry = CollectorRegistry::getDefault();    }    /**     * @return CollectorRegistry     */    public function getRegistry(): CollectorRegistry    {        return $this->registry;    }    /**     * @param string $metricName     *     * @throws MetricsRegistrationException     */    public function increaseMetric(string $metricName): void    {        $counter = $this->registry->getOrRegisterCounter('asterios_bot', $metricName, 'it increases');        $counter->incBy(1, []);    }    /**     * @param string $serverName     *     * @throws MetricsRegistrationException     */    public function increaseHealthCheck(string $serverName): void    {        $prefix = App::getInstance()->getConfig()->isTestServer() ? 'test_' : '';        $this->increaseMetric($prefix . self::METRIC_HEALTH_CHECK_PREFIX . $serverName);    }}

Для проверки работоспособности парсера мне нужно сохранить метрику в Redis после получения данных с RSS. Если данные получены, значит все нормально, и можно сохранить метрику

        if ($counter) {            $this->metrics->increaseHealthCheck($serverName);        }

Где переменная $counter это количество записей в RSS. Там будет 0, если получить данные не удалось, и значит метрика не будет сохранена. Это потом понадобится для alert по работе сервиса.

Затем нужно метрики опубликовать на странице /metric чтобы Prometheus их спарсил. Добавим хост в конфиг prometheus.yml из шага 7.

# my global configglobal:  scrape_interval:     5s # Set the scrape interval to every 15 seconds. Default is every 1 minute.  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.  # scrape_timeout is set to the global default (10s).scrape_configs:  - job_name: 'bots-env'    static_configs:      - targets:          - prometheus:9090          - pushgateway:9091          - grafana:3000          - metrics:80 # тут будут мои метрики по uri /metrics

Код, который вытащит метрики из Redis и создаст страницу в текстовом формате. Эту страничку будет парсить Prometheus

$metrics = Metrics::getInstance();$renderer = new RenderTextFormat();$result = $renderer->render($metrics->getRegistry()->getMetricFamilySamples());header('Content-type: ' . RenderTextFormat::MIME_TYPE);echo $result;

Теперь настроим сам дашборд и alert. В настройках Grafana сначала укажите свой Prometheus как основной источник данных, а так же я добавил основной канал нотификации Телеграм (там добавляете токен своего бота и свой chat_id с этим ботом)

Настройка GrafanaНастройка Grafana
  1. Метрика increase(asterios_bot_healthcheck_x3[1m]) Показывает на сколько метрика asterios_bot_healthcheck_x3 увеличилась за 1 минуту

  2. Название метрики (будет под графиком)

  3. Название для легенды в пункте 4.

  4. Легенда справа из пункта 3.

  1. Правило, по которому проверяется метрика. В моем случае проверяет что за последние 30 секунд проблем не было

  2. Правило, по которому будет срабатывать alert. В моем случае "Когда сумма из метрики А между сейчас и 10 секунд назад"

  3. Если нет данных вообще - слать alert

  4. Сообщение в alert

Выглядит alert в телеграм так (помните мы настраивали рендеринг картинок для alert?)

Alert в ТелеграмAlert в Телеграм
  1. Обратите внимание, alert заметил падение, но все восстановилось. Grafana приготовилась слать alert, но передумала. Это то самое правило 30 секунд

  2. Тут уже все упало больше чем на 30 секунд и alert был отправлен

  3. Сообщение, которое мы указали в настройках alert

  4. Ссылка на dashboard

  5. Источник метрики

Шаг 9: Телеграм бот

Настройка телеграм бота ничем не отличается от настройки воркера. Телеграм бот по сути у меня это еще один воркер, я запустил его при помощи добавления настроек в supervisor. Тут уже рефакторинг проекта дал свои плоды, запуск бота был быстрым и простым.

Итоги

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

Ссылки на проекты

Подробнее..

Recovery mode Сила дашбордов

30.09.2020 18:23:32 | Автор: admin

Всем привет! Меня зовут Егор Иванов, и я специалист по автоматизации тестирования. Довольно долгое время до этого я проработал в различных компаниях из сферы BI. Я обожаю визуализацию данных и считаю, что без нее невозможно строить рабочие процессы и уж тем более процессы в тестировании. Поэтому хочу, чтобы ее использовали как можно больше людей, так как визуализация данных очень важна, а в виде дашбордов она еще и прекрасна.

Надеюсь, материал будет полезен и для тех, кто уже использует дашборд, возможно, вы увидите новое применение для этого инструмента. А те, кто незнаком с ним, познакомятся и, возможно, также начнут его использовать.

Многие из нас видят дашборд каждый день. Он пришел к нам из транспорта это приборная панель автомобиля.

Слева - дашборд автомобиля, справа - информационный дашборд в ITСлева - дашборд автомобиля, справа - информационный дашборд в IT

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

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

Информационные дашборды тоже очень важны, и их надо накладывать на любой процесс в вашей команде, чтобы понимать, с какой скоростью вы движетесь и достаточно ли у вас ресурсов.

Определение дашборда

Дашборд это визуальное представление важной информации. Данные в нем сгруппированы и расположены таким образом, чтобы их изменения можно было легко и быстро отслеживать. Обратите внимание, что здесь заключается главное требование к дашборду информацию должно быть легко отслеживать. Когда вы будете создавать дашборд, учитывайте это и делайте так, чтобы дашборд был понятен, а не чтобы это была просто панель со вставленными визуализаторами.

Еще важно разделять дашборды по типам. Я предлагаю учитывать только два:

  • Операционный дашборд предназначен для быстрого предоставления пользователям критически важной информации. Помогает пользователям быть быстрыми, активными и эффективными.

  • Аналитический дашборд помогает пользователям наилучшим образом оценить данные, проанализировать тенденции и принять решение.

Перед созданием дашборда старайтесь определять ту цель, для которой вы его строите.

Наш первый дашборд

Чтобы познакомить вас с одним из наших первых дашбордов, расскажу про процессы, информацию по которым он отображает.

Это процессы, в которых участвует моя команда группа интеграционного тестирования. Чем мы занимаемся? ЮMoney имеют микросервисную инфраструктуру, и когда у нас с каждой фичи срезается релиз, на ней проводятся регрессионные тесты, а потом он попадает к нам, чтобы мы проверили, насколько реально этот релиз может попасть на продакшен.

Мы имеем в своем арсенале набор тестовых схем, которые копируют прод. На этих схемах всегда свежие сервисы. И мы накатываем этот сервис на наши схемы, прогоняем на нем тесты. Если всё ОК, этот релиз идет дальше.

Как всё это выглядит с нашей стороны? В Jira прилетает тикет на проведение интеграционных тестов. Чтобы посмотреть тикет, есть канбан-панель, и флоу подразумевает четыре статуса: открытый, в работе, в ожидании, закрытый. В работе это когда гоняются тесты. В ожидании это если что-то пошло не так и требуется ручное вмешательство.

Еще у нас есть такой инструмент, как Autorun, который и проводит весь процессинг тикета. Подробнее про это можно прочитать в другой нашей статье тут.

После того, как Autorun берет в работу тикет в Jira, ему нужно выделить отдельный стенд для запуска. Это позволит не прогонять на одном стенде одновременно несколько тестов и заблокировать другой стенд, на котором уже проводятся работы. Для этого у нас есть инструмент под названием Locker.

Autorun обращается к этому инструменту, чтобы получить схему. У Locker тоже есть UI. То есть стенд может быть заблокирован или доступен, и с каким-то комментарием. Если есть комментарий, стенд блокируется.

После того, как Autorun сходит к Locker, он обращается к еще одному инструменту Pinger, чтобы понять, насколько схема живая. С точки зрения UI-интерфейса, Pinger это тоже мини-дашборд, на котором представлены все наши сервисы и их статусы: зеленые, если всё в порядке, или красные, если что-то не так. Autorun через API запрашивает у сервиса информацию. Если стенд не в порядке, он сообщает нам об этом и не запускает на нем тесты.

Если Autorun находит доступный для работы стенд, он запускает тесты в Jenkins, и обычно по результату задача улетает с вердиктом, что все хорошо.

Но иногда что-то может пойти не так. Для таких случаев у нас есть дежурный кто-то один из нашей команды. Он должен разобраться, что именно не так. Раньше для этого ему приходилось идти по всем трем UI, а в случае с Locker и Pinger еще и по пяти тестовым стендам, чтобы посмотреть, все ли хорошо на всех них. Это занимало время, а хотелось понимать причину сразу же.

Что мы для этого сделали? Конечно же, построили дашборд. Первым дашбордом была обычная HTML-страничка, которая скриптом ходила в API инструментов, запрашивала у них самую важную информацию и отображала ее.

Что представляла из себя самая важная информация? Из Jira мы брали только количество задач в каждом из статусов, из Pinger только красные компоненты, из Locker статусы схем и пояснение причины лока. То есть фактически мы ничего нового не изобрели, просто сделали общий UI и назвали его дашборд дежурного, а пользу он принес большую. Скорость понимания того, что происходит, увеличилась в разы. Мы вывели этот дашборд на телевизор в кабинете, и теперь на вопрос, что сейчас происходит, может ответить даже человек, который случайно зашел в кабинет. И для этого ему не надо проводить никаких манипуляций с остальными инструментами.

Также в голове появилась визуальная картинка, которая показывает что все идет хорошо. Выглядит она как серый дашборд с нулями:

Он значит, что со всеми стендами все отлично, новых задач нет и можно идти на обед

Все бы это хорошо, но на каждый случай дашборд не напишешь. Нужен был инструмент для масштабирования и отображения различных данных. И мы решили использовать то, что у нас уже было, Grafana.

Многие с этим инструментом знакомы, зачастую он используется для отображения состояния продукта его нагрузки либо состояния стендов. А мы хотели визуализировать всю информацию, которая нам поступает.

На самом деле, для построения дашбордов есть множество решений, но некоторые из них входят в целые BI-платформы наподобие ClickView, а некоторые облачные решения, например Google Data Studio, нам не подходят. А Grafana у нас уже была и использовалась в команде мониторинга.

На всякий случай повторю, что такое Grafana.

Это инструмент для построения дашбордов на основе различных источников от PostgreSQL до Google Sheets. В нашем случае источником был Graphite. Чем он нам был удобен? У нас не было готового хранилища знаний, где лежали бы все нужные данные. Мы сами пушим данные. Соответственно, Graphite удобное хранилище для таких временных метрик.

Как происходит отсылка этих метрик? В формате StatsD мы отправляем их в Telegraf. Формат такой: название метрики, ее тип и значение. Telegraf за 30 секунд агрегирует метрики в зависимости от типа, который мы передали, и потом отсылает их в хранилище Graphite.

На самом деле, мы даже не особо переживаем и отправляем метрики по UDP, потому что на каждом нашем хосте есть инстанс Telegraf и до него 100% дойдет. Плюс у нас не настолько критичные технические метрики, которые мы отображаем в дашбордах, чтобы переживать, если они не дойдут.

Метрики StatsD бывают четырех типов, которые покрывают полную потребность:

  • g (Gauge) в течение 30 секунд Telegraf отправляет только значение последней метрики, которую получил;

  • с (Count) все полученные за интервал данные будут сложены, то есть Telegraf суммирует все, что к нему пришло;

  • s (Set) отправляет уникальные значения, пришедшие за это время;

  • ms (Timer) отправляет множество разных метрик по таймеру (среднее время выполнения, count, max, min и т.д.).

Сами метрики отсылаются так же просто. Если у вас Java, Java StatsD Client в нем создаем сам клиент и через него отправляем метрики. Всё. Отправку из Java мы используем как раз в наших инструментах, содержащих данные, которые надо отослать. То есть Autorun может отправить данные о количестве пришедших задач. Состояние схем может отправлять Pinger.

import com.timgroup.statsd.StatsDClient;import com.timgroup.statsd.NonBlockingStatsDClient;public class Foo {private static final StatsDClient statsd =         new NonBlockingStatsDClient("my.prefix", "statsd-host", 8125);    public static final void main(String[] args) {    statsd.incrementCounter("bar");    statsd.recordGaugeValue("baz", 100);    statsd.recordExecutionTime("bag", 25);    }    }

https://github.com/tim-group/java-statsd-client

Также метрику можно легко отправить через sh. Это удобно, если мы отправляем метрики из Jenkins, из любой другой CI. В нашем случае это Jenkins.

echo "my.prefix.bar:1|c" | nc -w 0 -u statsd-host 8125echo "my.prefix.baz:25|g" | nc -w 0 -u statsd-host 8125

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

Дашборд визуализатор метрик

Самым первым у нас был дашборд по метрикам нашей команды. Так как основной процесс у нас это интеграционное тестирование, мы взяли метрики, относящиеся к этому процессу:

  • количество релизов;

  • автоматически пройденные релизы, которые прошли без участия дежурного и по которым есть тесты;

  • поломанные релизы, в которых была найдена ошибка;

  • пропущенные релизы, для которых была проверена только корректность развертывания сборки в тестовой среде (тестов нет).

Мы решили посмотреть, что будет, если построить такой дашборд и смотреть на него каждый день. Какие плюсы мы от этого получили?

Самое интересное мы поняли, что у нас неправильно считаются метрики. Все они высчитывались по формулам и отправлялись руководству раз в спринт. При ежедневном просмотре мы заметили, что где-то стало выскакивать больше 100%, а где-то поняли: Ага, были релизы без тестов, а у нас из-за этого почему-то процент просел, хотя этого не должно было произойти. Выяснилось, что у нас ошибка в формулах, и это удалось заметить благодаря тому, что мы просто стали смотреть за данными.

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

В целом сам процесс стал для нас прозрачнее. Поэтому можно сказать, что дашборд это способ продуктивной коммуникации между членами команды.

Дашборд мотиватор

Помимо того, что все стали видеть проблемы, все захотели, чтобы в их дежурство было 100% AutoPass. В наше дежурство было много релизов, и мы их хорошо выдержали. Так появилась мотивация устанавливать рекорды.

Мы проверили, насколько дашборд может быть реальным инструментом для мотивации. И нашли проблему, где нам не хватает мотивации в проведении code review. Самописный бот назначает ревьюеров, коллег из моей команды, и мы проводим ревью тестов и наших инструментов для автоматизации. При этом ревьюер не один, иногда даже не два, а одного approve обычно достаточно. То есть если один человек поставил approve, значит, все хорошо. Тут может получиться так, что кто-то вообще никогда не ревьюит и надеется, что это сделают другие. Чтобы это понять, мы построили дашборд и посмотрели, что изменилось.

Дашборд по активностям на ревьюДашборд по активностям на ревью

Новый дашборд показывал активности ревьюеров: comments, approve, needs work. Такая визуализация меня сильно замотивировала. Я желтенький. По рисунку видно, что я более-менее часто ставлю approve, но, например, комментирую не так много. После этого я старался ревьюить активнее.

Если раньше у нас б в pull request чаще всего стоял один approve, то сейчас более чем в 90% случаях он стоит как минимум от двух человек. И это не случайные approve раз один поставил, то и я тоже, а осознанные.

Дашборд для анализа

Еще дашборд можно использовать для анализа. Мы тестировщики и любим анализировать именно тесты.

Лично я больше всего люблю анализировать время выполнения тестов. Иногда кажется: Ага, у нас тесты что-то очень долго выполняются Но как понять на самом деле, долго они выполняются или нет, если ты не знаешь, как они выполнялись вчера, позавчера или в течение года и как это соотносится с количеством тестов? На этот вопрос также сложно ответить без визуализации.

Вот простой пример дашборда, который показывает, что ваше восприятие может быть обманчиво.

Анализ времени выполнения тестовАнализ времени выполнения тестов

Выполнение наших тестов, которые включают в себя компиляцию и само исполнение тестов, в целом не менялось. По индикатору мы не вышли за целевое время. При этом даже тесты по фронтенду, наоборот, стали быстрее. Но время компиляции увеличилось. И получилось, что тесты могли бы проводиться гораздо быстрее достаточно только вернуть ту компиляцию, которая была еще пару месяцев назад. Этот дашборд был создан недавно именно для того, чтобы ответить на вопрос: все ли у нас хорошо со временем прогона тестов?

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

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

Дашборд для экономии времени

Например, у нас есть такая активность разбор того, насколько хорошо у нас прогнались утренние тесты на подготовку данных. Есть такой механизм мы заранее подготавливаем тяжелые тестовые данные. То есть пользователи с выпущенными карточками. Это занимает время, и в тест это загружать тяжеловато. Поэтому пул таких пользователей мы готовим утром. Данные готовятся на наших пяти схемах (тестовых стендах, которые участвуют в приемках). Плюс это не один и не два тестовых запуска. И утром по результатам всех этих прогонов прилетает целая куча писем: На такой-то схеме прошло так-то, На такой-то схеме прошло так-то. Точнее, раньше прилетали все эти письма и было тяжеловато.

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

Либо увидеть, что все плохо, сильно расстроиться и идти разбираться, почему так случилось.

На этом примеры заканчиваются. Надеюсь, они показали, что дашборды могут быть не только про то, как идет бизнес и что происходит в команде. Они могут показывать и руководителю, и всем остальным состояние самой команды, разных процессов в ней и просто быть удобным инструментом для решения различных задач.

Небольшое резюме

При внедрении дашборда мы получаем:

  • инструмент для объективной оценки происходящего,

  • экономию времени при анализе,

  • дополнительную мотивацию,

  • новую информацию, которая раньше была незаметна,

  • множество данных для анализа в будущем.

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

В заключение добавлю прекрасную цитату Джона Тьюки, американского математика, статистика, одного из основателей визуализации данных и статистического анализа:

Изображение приобретает особую значимость, если оно позволяет углядеть в нем то, что мы не предполагали увидеть.

Подробнее..

Визуализация аналитики APIM Gravitee в Grafana

30.05.2021 10:22:52 | Автор: admin

Бесспорно, интерфейс Gravitee представляет достаточно наглядные и удобные средства визуализации работы шлюзов Gravitee. Но в любом случае, возникает потребность предоставить доступ к этим инструментам службе мониторинга, владельцам или потребителям API и при этом они могут находится вне закрытого контура, в котором расположен менеджер API. Да и иметь всю доступную информацию по различным API на одном экране всегда удобнее.
Видеть происходящее на шлюзах, при этом не вдаваясь в особенности пользовательского интерфейса Gravitee, а администраторам - не тратить время на создание пользователей и разделение ролей и привилегий внутри Gravitee.
На Хабре уже была пара статей посвященных APIM Gravitee, тут и тут. По этому, в своей заметке, буду подразумевать, что читатель уже знаком с процессом установки/настройки APIM Gravitee и Grafana, рассмотрю только процесс настройки их интеграции.

Почему нельзя пойти простым путём?

По умолчанию, хранилищем для аналитики Gravitee является ElasticSearch. Информация накапливается в четырёх различных индексах, с посуточной разбивкой:

  • gravitee-request-YYYY.MM.DD - здесь хранится информация по каждому запросу (аналог access.log в nginx). Это наша основная цель;

  • gravitee-log-YYYY.MM.DD - здесь уже хранится более подробная информация о запросе (при условии, что включена отладка, см. рисунок ниже). А именно полные заголовки запросов и ответов, а также полезная нагрузка. В зависимости от настроек, логироваться может как обмен между потребителем и шлюзом, так и/или шлюзом и поставщиком API;

    Экран включения/отключения расширенного логированияЭкран включения/отключения расширенного логирования
  • gravitee-monitor-YYYY.MM.DD - этот нас не интересует;

  • gravitee-health-YYYY.MM.DD - этот нас не интересует.

И казалось бы, что может быть проще: подключай ElasticSearch в качестве источника данных в Grafana и визуализируй, но не всё так просто.
Во первых, в индексе хранятся только идентификаторы объектов, т.е. человеко-читаемых имён поставщиков и потребителей, вы там не увидите. Во вторых, получить полную информацию соединив данные из двух источников непосредственно в интерфейсе Grafana, крайне проблематично. Gravitee хранит информацию о настройках и статистику своей работы в разных местах. Настройки, в MongoDB или PostgreSQL, по сути статическая информация. Таким образом в одном месте у нас (в терминах Grafana) - таблица, в другом - временной ряд.

B как же быть?

Большим преимуществом СУБД PostgreSQL является богатый набор расширений для работы с внешними источниками данных, в том числе и с ElasticSearch (тут). Благодаря этому интеграция сводится к тому, что Grafana общается с единственным источником данных - СУБД PostgreSQL, которая в свою очередь получает данные из ElasticSearch и обогащает их информацией и делает читаемой для администратора или любого другого бенефициара.
Схематически это будет выглядеть следующим образом (рисунок ниже).

Схема взаимодействия модулей GraviteeСхема взаимодействия модулей Gravitee

Ну что же, за дело!

Все ниже описанные действия актуальны для следующей конфигурации: CentOS 7, APIM Gravitee 3.6, СУБД PostgreSQL 11, ElasticSearch 7.+

Начнём с интеграции PostgreSQL и ElasticSearch. Сам процесс интеграции достаточно прост и делится на следующие шаги:

  1. Устанавливаем расширение multicorn11 и если не установлен pip, то ставим и его:

    yum install multicorn11 python3-pip
    
  2. Далее из pip-репозитория, устанавливаем библиотеку python3 для работы с ElasticSearch:

    pip3 install pg_es_fdw
    
  3. Далее, переходим к настройке PostgreSQL. Подключаемся целевой БД и добавляем расширение multicorn и подключаем необходимую библиотеку:

    GRANT USAGE on FOREIGN DATA WRAPPER multicorn TO gatewaytest;GRANT USAGE ON FOREIGN SERVER multicorn_es TO gatewaytest;
    
     CREATE EXTENSION multicorn; CREATE SERVER multicorn_es FOREIGN DATA WRAPPER multicorn  OPTIONS (wrapper 'pg_es_fdw.ElasticsearchFDW');
    
  4. Выдаём права, непривилегированному пользователю. В нашем случае это logreader:

    GRANT USAGE on FOREIGN DATA WRAPPER multicorn TO logreader;GRANT USAGE ON FOREIGN SERVER multicorn_es TO logreader;
    
  5. Для удобства, создадим отдельную схему logging, владельцем которой будет наш пользователь logreader:

    CREATE SCHEMA logging AUTHORIZATION logreader;
    
  6. Создадим родительскую таблицу, к которой мы будем подключать новые индексы и удалять не актуальные:

    CREATE TABLE logging.requests (  id varchar(36),  "@timestamp" timestamp with time zone,  api varchar(36),  "api-response-time" int,  application varchar(36),  custom json,  endpoint text,  gateway varchar(36),  "local-address" varchar(16),  method int,  path text,  plan varchar(36),  "proxy-latency" int,  "remote-address" varchar(16),  "request-content-length" int,  "response-content-length" int,  "response-time" int,  sort text,  status int,  subscription varchar(36),  uri text,  query TEXT,  score NUMERIC) PARTITION BY RANGE("@timestamp");
    

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

  7. Для подключения и отключения индексов, создадим небольшой shell-скрипт и будем запускать его раз в сутки через cron:

    #!/bin/shNEWPART=${1:-$(date +'%Y.%m.%d')}OLDPART=$(date --date='14 days ago' +'%Y.%m.%d')curl http://gateway.corp/testpsql gateway -U logreader -c "CREATE FOREIGN TABLE logging.\"requests_${NEWPART}\"PARTITION OF logging.requests   FOR VALUES FROM ('${NEWPART} 00:00:00') TO ('${NEWPART} 23:59:59')SERVER multicorn_esOPTIONS (host 'els-host',  port '9200',  index 'gravitee-request-${NEWPART}',  rowid_column 'id',  query_column 'query',  query_dsl 'false',    score_column 'score',  sort_column 'sort',  refresh 'false',  complete_returning 'false',  timeout '20',  username 'elastic-ro',  password 'Sup3rS3cr3tP@ssw0rd');"    psql gateway -U gatewaydev -c "drop foreign table logging.\"requests_${OLDPART}\""
    

    Немного пояснений:

    • NEWPART - текущая дата, для формирования имени партиции , при подключении нового индекса из ElasticSearch;

    • OLDPART - дата истекшего, неактуально индекса, здесь это 14 дней (определяется исходя из настроек ES Curator). Удалять партиции, ссылающиеся на несуществующие - обязательно. В противном случае запросы, к родительской таблице, будут прерываться с ошибками;

    • Вызов 'curl http://gateway.corp/test', необходим для того, что бы создавался индекс текущего дня, так как он создаётся в момент первого обращения к любому поставщику API. Если его не создать, то это будет приводить к ошибке, описанной выше. Такая проблема больше актуальна для тестовых стендов и стендов разработки;

    • Затем, создаём партицию на индекс текущего дня;

    • И на последнем шаге - удаляем неактуальный индекс.

    • Проверяем что всё работает

      TABLE logging.requests LIMIT 1;
      

      Если всё правильно, то должны получить похожий результат

    -[ RECORD 1 ]-----------+-------------------------------------id                      | 55efea8a-9c91-4a61-afea-8a9c917a6133@timestamp              | 2021-05-16 00:00:02.025+03api                     | 9db39338-1019-453c-b393-381019f53c72api-response-time       | 0application             | 1custom                  | {}endpoint                | gateway                 | 7804bc6c-2b72-497f-84bc-6c2b72897fa9local-address           | 10.15.79.29method                  | 3path                    | plan                    | proxy-latency           | 2remote-address          | 10.15.79.27request-content-length  | 0response-content-length | 49response-time           | 2sort                    | status                  | 401subscription            | uri                     | /testquery                   | score                   | 1.0
    

Рисуем графики

И вот мы подошли к тому, ради чего всё и делалось - визуализируем статистику Gravitee. Благодаря тому, что для доступа к аналитике используется единая точка входа, а именно СУБД PostgreSQL, это даёт дополнительные возможности. Например, выводить статическую информацию: количество поставщиков, количество потребителей и их статусы; количество и состояние подписок; параметры конфигурации для поставщика и многое другое, наряду с динамическими данными.
В том числе хотелось бы отметить, что у поставщиков и потребителей имеется раздел Metadata, которые можно заполнять кастомными данными и так же выводить в дашборды Grafana.

Вот тут:

Раздел Metadata в GraviteeРаздел Metadata в Gravitee

А вот так это можно отобразить в Grafana:

Вариант отображения Metadata в GrafanaВариант отображения Metadata в Grafana
SELECT  name "Наименование",  value "Значение"FROM  metadataWHERE  reference_id='${apis}'

Пример комплексного экрана

Вариант комплексного экранаВариант комплексного экрана

APIs (статика) - общее количество поставщиков и количество активных.

SELECT COUNT(*) AS "Всего" FROM apis;SELECT COUNT(*) AS "Активных" FROM apis WHERE lifecycle_state='STARTED';

Для Applications, запросы составляются по аналогично, только из таблицы applications

API Hits - количество вызовов по каждому поставщику. Тут уже немного по сложнее

SELECT  date_trunc('minute',"@timestamp") AS time,  apis.name,ee с Grafana  COUNT(*)FROM  logging.requests alJOIN  apis ON al.api = apis.idWHERE  query='@timestamp:[$__from TO $__to]'GROUP BY 1,2

Average response time by API - среднее время ответа, по каждому поставщику считается аналогичным способом.

SELECT  date_trunc('minute',"@timestamp") AS time,  apis.name,  AVG(al."api-response-time")FROM  logging.requests alJOIN  apis ON al.api = apis.idWHERE  query='@timestamp:[$__from TO $__to]'GROUP BY 1,2

Еще один интересный показатель Hits, by gateways, это равномерность распределения запросов по шлюзам. Считается так:

SELECT  date_trunc('minute',"@timestamp") as time,  al."local-address",  COUNT(*)FROM  logging.requests alWHERE  query='@timestamp:[$__from TO $__to]'GROUP BY 1,2
График распределения запросов по шлюзамГрафик распределения запросов по шлюзам

Заключение

Приведённое выше решение, по моему субъективному мнению, нисколько не уступает стандартным средствам визуализации APIM Gravitee, а ограничено лишь фантазией и потребностями.
Учитывая то, что Grafana, обычно является центральным объектом инфраструктуры мониторинга, то преимущества такого решения очевидны: более широкий охват, более высокая плотность информации и простая кастомизация визуальных представлений.

P.S.

В ближайшее время, планируется ещё статья по интеграции Gravitee с ActiveDirectory. Процесс достаточно прост, но как всегда, есть нюансы.

Конструктивная критика, пожелания и предложения приветствуются!

Подробнее..

Мониторинг Tarantool логи, метрики и их обработка

28.12.2020 18:17:25 | Автор: admin

Tarantool это платформа in-memory вычислений с гибкой схемой данных. На её основе можно создать распределённое хранилище, веб-сервер, высоконагруженное приложение или, в конце концов, сервис, включающий в себя всё вышеперечисленное. Но какой бы ни была ваша промышленная задача, однажды настанет момент, когда её решение придётся мониторить. В этой статье я хочу дать обзор существующих средств для мониторинга приложения на базе Tarantool и пройтись по основным кейсам работы с ними.


Мониторинг Tarantool


Я работаю в команде, которая занимается разработкой, внедрением и поддержкой готовых решений на основе Tarantool. Для вывода наших приложений в эксплуатацию на контуре заказчика было необходимо не только разобраться в текущих возможностях мониторинга, но и доработать их. Большая часть доработок в результате вошла в те или иные стандартные пакеты. Данный материал является текстовой выжимкой этого опыта, и может пригодиться тем, кто решит пройти по той же тропе.


Настройка логов в Tarantool


Базовое конфигурирование и использование логов


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


Каждое сообщение лога имеет свой уровень детализации. Уровень логирования Tarantool характеризуется значением параметра log_level (целое число от 1 до 7):


  1. SYSERROR.
  2. ERROR сообщения log.error(...).
  3. CRITICAL.
  4. WARNING сообщения log.warn(...).
  5. INFO сообщения log.info(...).
  6. VERBOSE сообщения log.verbose(...).
  7. DEBUG сообщения log.debug(...).

Значение параметра log_level N соответствует логу, в который попадают сообщения уровня детализации N и всех предыдущих уровней детализации < N. По умолчанию log_level имеет значение 5 (INFO). Чтобы настроить этот параметр при использовании Cartridge, можно воспользоваться cartridge.cfg:


cartridge.cfg( { ... }, { log_level = 6, ... } )

Для отдельных процессов настройка производится при помощи вызова box.cfg:


box.cfg{ log_level = 6 }

Менять значение параметра можно непосредственно во время работы программы.


Стандартная стратегия логирования: писать об ошибках в log.error() или log.warn() в зависимости от их критичности, отмечать в log.info() основные этапы работы приложения, а в log.verbose() писать более подробные сообщения о предпринимаемых действиях для отладки. Не стоит использовать log.debug() для отладки приложения, этот уровень диагностики в первую очередь предназначен для отладки самого Tarantool. Не рекомендуется также использовать уровень детализации ниже 5 (INFO), поскольку в случае возникновения ошибок отсутствие информационных сообщений затруднит диагностику. Таким образом, в режиме отладки приложения рекомендуется работать при log_level 6 (VERBOSE), в режиме штатной работы при log_level 5 (INFO).


local log = require('log')log.info('Hello world')log.verbose('Hello from app %s ver %d', app_name, app_ver) -- https://www.lua.org/pil/20.htmllog.verbose(app_metainfo) -- type(app_metainfo) == 'table'

В качестве аргументов функции отправки сообщения в лог (log.error/log.warn/log.info/log.verbose/log.debug) можно передать обычную строку, строку с плейсхолдерами и аргументы для их заполнения (аналогично string.format()) или таблицу (она будет неявно преобразована в строку методом json.encode()). Функции лога также работают с нестроковыми данными (например числами), приводя их к строке c помощью tostring().


Tarantool поддерживает два формата логов: plain и json:


2020-12-15 11:56:14.923 [11479] main/101/interactive C> Tarantool 1.10.8-0-g2f18757b72020-12-15 11:56:14.923 [11479] main/101/interactive C> log level 52020-12-15 11:56:14.924 [11479] main/101/interactive I> mapping 268435456 bytes for memtx tuple arena...

{"time": "2020-12-15T11:56:14.923+0300", "level": "CRIT", "message": "Tarantool 1.10.8-0-g2f18757b7", "pid": 5675 , "cord_name": "main", "fiber_id": 101, "fiber_name": "interactive", "file": "\/tarantool\/src\/main.cc", "line": 514}{"time": "2020-12-15T11:56:14.923+0300", "level": "CRIT", "message": "log level 5", "pid": 5675 , "cord_name": "main", "fiber_id": 101, "fiber_name": "interactive", "file": "\/tarantool\/src\/main.cc", "line": 515}{"time": "2020-12-15T11:56:14.924+0300", "level": "INFO", "message": "mapping 268435456 bytes for memtx tuple arena...", "pid": 5675 , "cord_name": "main", "fiber_id": 101, "fiber_name": "interactive", "file": "\/tarantool\/src\/box\/tuple.c", "line": 261}

Настройка формата происходит через параметр log_format так же, как для параметра log_level. Подробнее о форматах можно прочитать в соответствующем разделе документации.


Tarantool позволяет выводить логи в поток stderr, в файл, в конвейер или в системный журнал syslog. Настройка производится с помощью параметра log. О том, как конфигурировать вывод, можно прочитать в документации.


Обёртка логов


Язык Lua предоставляет широкие возможности для замены на ходу практически любого исполняемого кода. Здесь я хотел бы поделиться способом помещения стандартных методов логирования в функцию-обёртку, который мы использовали при реализации сквозного логирования в своих приложениях. Стоит отметить, что способ этот расположен в "серой" зоне легальности, так что прибегать к нему стоит только при отсутствии возможностей решить проблему более элегантно.


local log = require('log')local context = require('app.context')local function init()    if rawget(_G, "_log_is_patched") then        return    end    rawset(_G, "_log_is_patched", true)    local wrapper = function(level)        local old_func = log[level]        return function(fmt, ...)            local req_id = context.id_from_context()            if select('#', ...) ~= 0 then                local stat                stat, fmt = pcall(string.format, fmt, ...)                if not stat then                    error(fmt, 3)                end            end            local wrapped_message            if type(fmt) == 'string' then                wrapped_message = {                    message = fmt,                    request_id = req_id                }            elseif type(fmt) == 'table' then                wrapped_message = table.copy(fmt)                wrapped_message.request_id = req_id            else                wrapped_message = {                    message = tostring(fmt),                    request_id = req_id                }            end            return old_func(wrapped_message)        end    end    package.loaded['log'].error = wrapper('error')    package.loaded['log'].warn = wrapper('warn')    package.loaded['log'].info = wrapper('info')    package.loaded['log'].verbose = wrapper('verbose')    package.loaded['log'].debug = wrapper('debug')    return trueend

Данный код обогащает информацию, переданную в лог в любом поддерживаемом формате, идентификатором запроса request_id.


Настройка метрик в Tarantool


Подключение метрик


Для работы с метриками в приложениях Tarantool существует пакет metrics. Это модуль для создания коллекторов метрик и взаимодействия с ними в разнообразных сценариях, включая экспорт метрик в различные базы данных (InfluxDB, Prometheus, Graphite). Материал основан на функционале версии 0.6.0.


Чтобы установить metrics в текущую директорию, воспользуйтесь стандартной командой:


tarantoolctl rocks install metrics 0.6.0

Чтобы добавить пакет в список зависимостей вашего приложения, включите его в соответствующий пункт rockspec-файла:


dependencies = {    ...,    'metrics == 0.6.0-1',}

Для приложений, использующих фреймворк Cartridge, пакет metrics предоставляет специальную роль cartridge.roles.metrics. Включение этой роли на всех процессах кластера упрощает работу с метриками и позволяет использовать конфигурацию Cartridge для настройки пакета.


Встроенные метрики


Сбор встроенных метрик уже включён в состав роли cartridge.roles.metrics.


Для включения сбора встроенных метрик в приложениях, не использующих фреймворк Cartridge, необходимо выполнить следующую команду:


local metrics = require('metrics')metrics.enable_default_metrics()

Достаточно выполнить её единожды на старте приложения, например поместив в файл init.lua.


В список метрик по умолчанию входят:


  • информация о потребляемой Lua-кодом RAM;
  • информация о текущем состоянии файберов;
  • информация о количестве сетевых подключений и объёме сетевого трафика, принятого и отправленного процессом;
  • информация об использовании RAM на хранение данных и индексов (в том числе метрики slab-аллокатора);
  • информация об объёме операций на спейсах;
  • характеристики репликации спейсов Tarantool;
  • информация о текущем времени работы процесса и другие метрики.

Подробнее узнать о метриках и их значении можно в соответствующем разделе документации.


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


Плагины для экспорта метрик


Пакет metrics поддерживает три формата экспорта метрик: prometheus, graphite и json. Последний можно использовать, например, в связке Telegraf + InfluxDB.


Чтобы настроить экспорт метрик в формате json или prometheus для процессов с ролью cartridge.roles.metrics, добавьте соответствующую секцию в конфигурацию кластера:


metrics:  export:    - path: '/metrics/json'      format: json    - path: '/metrics/prometheus'      format: prometheus

Экспорт метрик в формате json или prometheus без использования кластерной конфигурации настраивается средствами модуля http так же, как любой другой маршрут.


local json_metrics = require('metrics.plugins.json')local prometheus = require('metrics.plugins.prometheus')local httpd = require('http.server').new(...)httpd:route(    { path = '/metrics/json' },    function(req)        return req:render({            text = json_metrics.export()        })    end)httpd:route( { path = '/metrics/prometheus' }, prometheus.collect_http)

Для настройки graphite необходимо добавить в код приложения следующую секцию:


local graphite = require('metrics.plugins.graphite')graphite.init{    host = '127.0.0.1',    port = 2003,    send_interval = 60,}

Параметры host и port соответствуют конфигурации вашего сервера Graphite, send_interval периодичность отправки данных в секундах.


При необходимости на основе инструментов пакета metrics можно создать собственный плагин для экспорта. Советы по написанию плагина можно найти в соответствующем разделе документации.


Добавление пользовательских метрик


Ядро пакета metrics составляют коллекторы метрик, созданные на основе примитивов Prometheus:


  • counter предназначен для хранения одного неубывающего значения;
  • gauge предназначен для хранения одного произвольного численного значения;
  • summary хранит сумму значений нескольких наблюдений и их количество, а также позволяет вычислять перцентили по последним наблюдениям;
  • histogram агрегирует несколько наблюдений в гистограмму.

Cоздать экземпляр коллектора можно следующей командой:


local gauge = metrics.gauge('balloons')

В дальнейшем получить доступ к объекту в любой части кода можно этой же командой.


Каждый объект коллектора способен хранить сразу несколько значений метрики с разными лейблами. Например, в результате кода


local gauge = metrics.gauge('balloons')gauge:set(1, { color = 'blue' })gauge:set(2, { color = 'red' })

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


gauge:inc(11, { color = 'blue' }) -- increase 1 by 11

Лейблы концепт, который также был вдохновлён Prometheus используются для различения измеряемых характеристик в рамках одной метрики. Кроме этого, они могут быть использованы для агрегирования значений на стороне базы данных. Рассмотрим рекомендации по их использованию на примере.


В программе есть модуль server, который принимает запросы и способен сам их отправлять. Вместо того, чтобы использовать две различных метрики server_requests_sent и server_requests_received для хранения данных о количестве отправленных и полученных запросов, следует использовать общую метрику server_requests с лейблом type, который может принимать значения sent и received.


Подробнее о коллекторах и их методах можно прочитать в документации пакета.


Заполнение значений пользовательских метрик


Пакет metrics содержит полезный инструмент для заполнения коллекторов метрик коллбэки. Рассмотрим принцип его работы на простом примере.


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


local metrics = require('metrics')local buffer = require('app.buffer')metrics.register_callback(function()    local gauge = metrics.gauge('buffer_count')    gauge.set(buffer.count())end)

Вызов всех зарегистрированных коллбэков производится непосредственно перед сбором метрик в плагине экспорта. С таким подходом метрики, отправляемые наружу, всегда будут иметь наиболее актуальное значение.


Мониторинг HTTP-трафика


Пакет metrics содержит набор инструментов для подсчёта количества входящих HTTP-запросов и измерения времени их обработки. Они предназначены для работы со встроенным пакетом http, и подход будет отличаться в зависимости от того, какую версию вы используете.


Чтобы добавить HTTP-метрики для конкретного маршрута при использовании пакета http 1.x.x, вам необходимо обернуть функцию-обработчик запроса в функцию http_middleware.v1:


local metrics = require('metrics')local http_middleware = metrics.http_middlewarehttp_middleware.build_default_collector('summary', 'http_latency')local route = { path = '/path', method = 'POST' }local handler = function() ... endhttpd:route(route, http_middleware.v1(handler))

Для хранения метрик можно использовать коллекторы histogram и summary.


Чтобы добавить HTTP-метрики для маршрутов роутера при использовании пакета http 2.x.x, необходимо воспользоваться следующим подходом:


local metrics = require('metrics')local http_middleware = metrics.http_middlewarehttp_middleware.build_default_collector('histogram', 'http_latency')router:use(http_middleware.v2(), { name = 'latency_instrumentation' })

Рекомендуется использовать один и тот же коллектор для хранения всей информации об обработке HTTP-запросов (например, выставив в начале коллектор по умолчанию функцией build_default_collector или set_default_collector). Прочитать больше о возможностях http_middleware можно в документации.


Глобальные лейблы


В пункте "Добавление пользовательских метрик" вводится понятие лейблов, предназначенных для различения измеряемых характеристик в рамках одной метрики. Их выставление происходит непосредственно при измерении наблюдения в рамках одного коллектора. Однако это не единственный тип лейблов, которые поддерживает пакет metrics.


Для того чтобы прибавить к каждой метрике какой-то общий для процесса или группы процессов Tarantool (например, имя машины или название приложения) лейбл, необходимо воспользоваться механизмом глобальных лейблов:


local metrics = require('metrics')local global_labels = {}-- постоянное значениеglobal_labels.app = 'MyTarantoolApp'-- переменные конфигурации кластера (http://personeltest.ru/aways/www.tarantool.io/ru/doc/latest/book/cartridge/cartridge_api/modules/cartridge.argparse/)local argparse = require('cartridge.argparse')local params, err = argparse.parse()assert(params, err)global_labels.alias = params.alias-- переменные окружения процессаlocal host = os.getenv('HOST')assert(host)global_labels.host = hostmetrics.set_global_labels(global_labels)

Обратите внимание, что метод set_global_labels полностью перезаписывает таблицу глобальных лейблов при каждом вызове.


Роль cartridge.roles.metrics по умолчанию выставляет alias процесса Tarantool в качестве глобального лейбла.


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


Мониторинг внешних параметров


Tarantool не только позволяет организовывать сбор внутренних метрик о работе приложения, но и способен выступать как агент мониторинга внешних параметров. Подмодуль psutils пакета metrics является примером реализации такого поведения.


С помощью psutils можно настроить сбор метрик об использовании CPU процессами Tarantool. Его информация основывается на данных /proc/stat и /proc/self/task. Подключить сбор метрик можно с помощью следующего кода:


local metrics = require('metrics')metrics.register_callback(function()    local cpu_metrics = require('metrics.psutils.cpu')    cpu_metrics.update()end)

Возможность писать код на Lua делает Tarantool гибким инструментом, позволяющим обходить различные препятствия. Например, psutils возник из необходимости следить за использованием CPU вопреки отказу администраторов со стороны заказчика "подружить" в правах файлы /proc/* процессов Tarantool и плагин inputs.procstat Telegraf, который использовался на местных машинах в качестве основного агента.


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


Визуализация метрик


Пример из tarantool/grafana-dashboard


Хранение метрик в Prometheus


Настройка пути для экспорта метрик Tarantool в формате Prometheus описана в пункте "Плагины для экспорта метрик". Ответ запроса по такому маршруту выглядит следующим образом:


...# HELP tnt_stats_op_total Total amount of operations# TYPE tnt_stats_op_total gaugetnt_stats_op_total{alias="tnt_router",operation="replace"} 1tnt_stats_op_total{alias="tnt_router",operation="select"} 57tnt_stats_op_total{alias="tnt_router",operation="update"} 43tnt_stats_op_total{alias="tnt_router",operation="insert"} 40tnt_stats_op_total{alias="tnt_router",operation="call"} 4...

Чтобы настроить сбор метрик в Prometheus, необходимо добавить элемент в массив scrape_configs. Этот элемент должен содержать поле static_configs с перечисленными в targets URI всех интересующих процессов Tarantool и поле metrics_path, в котором указан путь для экспорта метрик Tarantool в формате Prometheus.


scrape_configs:  - job_name: "tarantool_app"    static_configs:      - targets:         - "tarantool_app:8081"        - "tarantool_app:8082"        - "tarantool_app:8083"        - "tarantool_app:8084"        - "tarantool_app:8085"    metrics_path: "/metrics/prometheus"

В дальнейшем найти метрики в Grafana вы сможете, указав в качестве job соответствующий job_name из конфигурации.


Пример готового docker-кластера Tarantool App + Prometheus + Grafana можно найти в репозитории tarantool/grafana-dashboard.


Хранение метрик в InfluxDB


Чтобы организовать хранение метрик Tarantool в InfluxDB, необходимо воспользоваться стеком Telegraf + InfluxDB и настроить на процессах Tarantool экспорт метрик в формате json (см. пункт "Плагины для экспорта метрик"). Ответ формируется следующим образом:


{    ...    {        "label_pairs": {            "operation": "select",            "alias": "tnt_router"        },        "timestamp": 1606202705935266,        "metric_name": "tnt_stats_op_total",        "value": 57    },    {        "label_pairs": {            "operation": "update",            "alias": "tnt_router"        },        "timestamp": 1606202705935266,        "metric_name": "tnt_stats_op_total",        "value": 43    },    ...}

HTTP-плагин Telegraf требует заранее указать список возможных тегов для метрики. Конфигурация, учитывающая все стандартные лейблы, будет выглядеть следующим образом:


[[inputs.http]]    urls = [        "http://tarantool_app:8081/metrics/json",        "http://tarantool_app:8082/metrics/json",        "http://tarantool_app:8083/metrics/json",        "http://tarantool_app:8084/metrics/json",        "http://tarantool_app:8085/metrics/json"    ]    timeout = "30s"    tag_keys = [        "metric_name",        "label_pairs_alias",        "label_pairs_quantile",        "label_pairs_path",        "label_pairs_method",        "label_pairs_status",        "label_pairs_operation"    ]    insecure_skip_verify = true    interval = "10s"    data_format = "json"    name_prefix = "tarantool_app_"    fieldpass = ["value"]

Список urls должен содержать URL всех интересующих процессов Tarantool, настроенные для экспорта метрик в формате json. Обратите внимание, что лейблы метрик попадают в Telegraf и, соответственно, InfluxDB как теги, название которых состоит из префикса label_pairs_ и названия лейбла. Таким образом, если ваша метрика имеет лейбл с ключом mylbl, то для работы с ним в Telegraf и InfluxDB необходимо указать в пункте tag_keys соответствующего раздела [[inputs.http]] конфигурации Telegraf значение ключа label_pairs_mylbl, и при запросах в InfluxDB ставить условия на значения лейбла, обращаясь к тегу с ключом label_pairs_mylbl.


В дальнейшем найти метрики в Grafana вы сможете, указав measurement в формате <name_prefix>http (например, для указанной выше конфигурации значение measurement tarantool_app_http).


Пример готового docker-кластера Tarantool App + Telegraf + InfluxDB + Grafana можно найти в репозитории tarantool/grafana-dashboard.


Стандартный дашборд Grafana


Для визуализации метрик Tarantool с помощью Grafana на Official & community built dashboards опубликованы стандартные дашборды. Шаблон состоит из панелей для мониторинга HTTP, памяти для хранения данных вместе с индексами и операций над спейсами Tarantool. Версию для использования с Prometheus можно найти здесь, а для InfluxDB здесь. Версия для Prometheus также содержит набор панелей для мониторинга состояния кластера, агрегированной нагрузки и потребления памяти.



Чтобы импортировать шаблон дашборды, достаточно вставить необходимый id или ссылку в меню Import на сервере Grafana. Для завершения процесса импорта необходимо задать переменные, определяющие место хранения метрик Tarantool в соответствующей базе данных.


Генерация дашбордов Grafana с grafonnet


Стандартные дашборды Grafana были созданы с помощью инструмента под названием grafonnet. Что это за заморский зверь и как мы к нему пришли?


С самого начала перед нами стояла задача поддерживать не одну, а четыре дашборды: два похожих проекта, экземпляр каждого из которых находился в двух разных зонах. Изменения, такие как переименование метрик и лейблов или добавление/удаление панелей, происходили чуть ли не ежедневно, но после творческой работы по проектированию улучшений и решению возникающих проблем непременно следовало механическое накликивание изменений мышью, умножавшееся в своём объёме на четыре. Стало ясно, что такой подход следует переработать.


Одним из первых способов решить большинство возникающих проблем было использование механизма динамических переменных (Variables) в Grafana. Например, он позволяет объединить дашборды с метриками из разных зон в одну с удобным переключателем. К сожалению, мы слишком быстро столкнулись с проблемой: использование механизма оповещений (Alert) не совместимо с запросами, использующими динамические переменные.


Любой дашборд в Grafana по сути представляет собой некоторый json. Более того, платформа позволяет без каких-либо затруднений экспортировать в таком формате существующие дашборды. Работать с ним в ручном режиме несколько затруднительно: размер даже небольшого дашборда составляет несколько тысяч строк. Первым способом решения проблемы был скрипт на Python, который заменял необходимые поля в json, по сути превращая один готовый дашборд в другой. Когда разработка библиотеки скриптов пришла к задаче добавления и удаления конкретных панелей, мы начали осознавать, что пытаемся создать генератор дашбордов. И что эту задачу уже кто-то до нас решал.


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


grafonnet opensource-проект под эгидой Grafana, предназначенный для программной генерации дашбордов. Он основан на языке программирования jsonnet языке для генерации json. Сам grafonnet представляет собой набор шаблонов для примитивов Grafana (панели и запросы разных типов, различные переменные) с методами для объединения их в цельный дашборд.


Основным преимуществом grafonnet является используемый в нём язык jsonnet. Он прост и минималистичен, и всегда имеет дело с json-объектами (точнее, с их местной расширенной версией, которая может включать в себя функции и скрытые поля). Благодаря этому на любом этапе работы выходной объект можно допилить под себя, добавив или убрав какое-либо вложенное поле, не внося при этом изменений в исходный код.


Начав с форка проекта и сборки нашей дашборды на основе этого форка, впоследствии мы оформили несколько Pull Request-ов в grafonnet на основе наших изменений. Например, один из них добавил поддержку запросов в InfluxDB на основе визуального редактора.


Визуальный редактор запросов InfluxDB


Код наших стандартных дашбордов расположен в репозитории tarantool/grafana-dashboard. Здесь же находится готовый docker-кластер, состоящий из стеков Tarantool App + Telegraf + InfluxDB + Grafana, Tarantool App + Prometheus + Grafana. Его можно использовать для локальной отладки сбора и обработки метрик в вашем собственном приложении.


При желании из стандартных панелей можно собрать дашборд, расширенный панелями для отображения собственных метрик приложения.


На что смотреть?


В первую очередь, стоит следить за состоянием самих процессов Tarantool. Для этого подойдёт, например, стандартный up Prometheus. Можно соорудить простейший healthcheck самостоятельно:


httpd:route(    { path = '/health' },    function(req)        local body = { app = app, alias = alias, status = 'OK' }        local resp = req:render({ json = body })        resp.status = 200        return resp    end)

Рекомендации по мониторингу внешних параметров ничем принципиально не отличаются от ситуации любого другого приложения. Необходимо следить за потреблением памяти на хранение логов и служебных файлов на диске. Заметьте, что файлы с данными .snap и .xlog возникают даже при использовании движка memtx (в зависимости от настроек). При нормальной работе нагрузка на CPU не должна быть чересчур большой. Исключение составляет момент восстановления данных после рестарта процесса: построение индексов может загрузить все доступные потоки процессора на 100 % на несколько минут.


Потребление RAM удобно разделить на два пункта: Lua-память и память, потребляемая на хранение данных и индексов. Память, доступная для выполнения кода на Lua, имеет ограничение в 2 Gb на уровне Luajit. Обычно приближение метрики к этой границе сигнализирует о наличии какого-то серьёзного изъяна в коде. Более того, зачастую такие изъяны приводят к нелинейному росту используемой памяти, поэтому начинать волноваться стоит уже при переходе границы в 512 Mb на процесс. Например, при высокой нагрузке в наших приложениях показатели редко выходили за предел 200-300 Mb Lua-памяти.


При использовании движка memtx потреблением памяти в рамках заданного лимита memtx_memory (он же метрика quota_size) заведует slab-аллокатор. Процесс происходит двухуровнево: аллокатор выделяет в памяти ячейки, которые после занимают сами данные или индексы спейсов. Зарезервированная под занятые или ещё не занятые ячейки память отображена в quota_used, занятая на хранение данных и индексов arena_used (только данных items_used). Приближение к порогу arena_used_ratio или items_used_ratio свидетельствует об окончании свободных зарезервированных ячеек slab, приближение к порогу quota_used_ratio об окончании доступного места для резервирования ячеек. Таким образом, об окончании свободного места для хранения данных свидетельствует приближение к порогу одновременно метрик quota_used_ratio и arena_used_ratio. В качестве порога обычно используют значение 90 %. В редких случаях в логах могут появляться сообщения о невозможности выделить память под ячейки или данные даже тогда, когда quota_used_ratio, arena_used_ratio или items_used_ratio далеки от порогового значения. Это может сигнализировать о дефрагментации данных в RAM, неудачном выборе схем спейсов или неудачной конфигурации slab-аллокатора. В такой ситуации необходима консультация специалиста.


Рекомендации по мониторингу HTTP-нагрузки и операций на спейсах также являются скорее общими, нежели специфичными для Tarantool. Необходимо следить за количеством ошибок и временем обработки запроса в зависимости от кода ответа, а также за резким возрастанием нагрузки. Неравномерность нагрузки по операциям на спейсы может свидетельствовать о неудачном выборе ключа шардирования.


Заключение


Как этот материал, так и пакет metrics назвать "всеохватными" и "универсальными" на данный момент нельзя. Открытыми или находящимися на данный момент в разработке являются вопросы метрик репликации, мониторинга движка vinyl, метрики event loop и полная документация по уже существующим методам metrics.


Не стоит забывать о том, что metrics и grafana-dashboard являются opensource-разработками. Если при работе над своим проектом вы наткнулись на ситуацию, которая не покрывается текущими возможностями пакетов, не стесняйтесь внести предложение в Issues или поделиться вашим решением в Pull Requests.


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


Полезные ссылки:


Подробнее..

Cortex и не только распределённый Prometheus

04.03.2021 10:11:49 | Автор: admin

В последнее время Prometheus стал де-факто стандартом для сбора и хранения метрик. Он удобен для разработчиков ПО - экспорт метрик можно реализовать в несколько строк кода. Для DevOps/SRE, в свою очередь, есть простой язык PromQL для получения метрик из хранилища и их визуализации в той же Grafana.

Но Prometheus имеет ряд недостатков, способы устранения которых я хочу рассмотреть в этой статье. Также разберём деплой Cortex - распределённого хранилища метрик.

Недостатки

  • Отсутствие отказоустойчивости

    Prometheus работает только в единственном экземпляре, никакого HA.

  • Отсутствие распределения нагрузки

    В принципе, он хорошо скейлится вверх с ростом количества ядер. Так что это проблема только для тех у кого действительно много метрик.

  • Нет поддержки multi-tenancy

    Все метрики летят в один большой котёл и разгребать их потом используя PromQL и метки не всегда удобно. Часто хочется разделить различные приложения и\или команды по своим песочницам чтобы они друг другу не мешали.

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

В принципе, все эти проблемы можно решить настроив несколько HA-пар Prometheus и распределив по ним свои приложения. Если перед каждой парой повесить прокси, то можно получить что-то вроде отказоустойчивости.

Но есть и минусы:

  • После того как один хост из пары упадёт/перезагрузится/whatever - у них случится рассинхронизация. В метриках будут пропуски.

  • Все метрики приложения должны умещаться на один хост

  • Управлять таким зоопарком будет сложнее - какие-то из Prometheus могут быть недогружены, какие-то перегружены. В случае запуска в каком-нибудь Kubernetes это не так важно.

Давайте рассмотрим какими ещё способами можно решить это.

PromQL прокси

Например promxy, который размещается перед 2 или более инстансами Prometheus и делает fan-out входящих запросов на все из них. Затем дедуплицирует полученные метрики и, таким образом, закрывает пропуски в метриках (если, конечно, они не попали на один и тот же временной интервал).

Минусы подобного решения на поверхности:

  • Один запрос нагружает сразу все инстансы за прокси

  • Прокси решает только проблему с пропусками в метриках.

Но для тех, у кого нагрузка укладывается в возможности одного Prometheus (либо ее можно грамотно раскидать по нескольким HA-парам) и кому не нужен multi-tenancy - это очень хороший вариант.

Thanos

Thanos - это уже более продвинутое решение.

Он устанавливает рядом с каждым инстансом Prometheus так называемый Sidecar - отдельный демон, который подглядывает за блоками данных, которые генерирует Prometheus. Как только блок закрывается - Sidecar загружает его в объектное хранилище (S3/GCS/Azure). Длина блоков в Prometheus прибита гвоздями и равна 2 часам.

Также он является прокси между GRPC Thanos StoreAPI и Prometheus для получения метрик, которые еще не были загружены в объектное хранилище.

Отдельный компонент Querier реализует PromQL: в зависимости от временного интервала запроса и настроек глубины хранения данных в Prometheus он может направить его в объектное хранилище, в Sidecar или в разбить на два подзапроса - для свежих данных запрос пойдёт через Sidecar в Prometheus, а для более старых - в объектное хранилище.

Отказоустойчивость свежих данных в Thanos реализуется примерно так же как и в promxy - делается fan-out запросов на все причастные сервера, результаты накладываются друг на друга и дедуплицируются. Задача по защите исторических данных лежит на объектном хранилище.

Multi-tenancy есть в некотором зачаточном состоянии, но в эту сторону проект, судя по всему, не развивается особо.

Cortex

Это наиболее сложный и функциональный проект. Его начали разрабатывать в Grafana Labs для своего SaaS решения по хранению метрик и несколько лет назад выложили в open source, с тех пор разработка идёт на гитхабе.

Как можно видеть на диаграмме выше - в нём очень много компонентов. Но бояться не стоит - большую часть из них можно не использовать, либо запускать в рамках одного процесса - single binary mode.

Так как Cortex изначально разрабатывался как SaaS решение - в нём поддерживается end-to-end multi-tenancy.

Хранение метрик

На данный момент в Cortex есть два движка. Оба они хранят данные в объектном хранилище, среди которых поддерживаются:

  • S3

  • GCS

  • Azure

  • OpenStack Swift (экспериментально)

  • Любая примонтированная ФС (например - NFS или GlusterFS). Хранить блоки на локальной ФС смысла нет т.к. они должны быть доступны всему кластеру.

Далее я буду для краткости называть объектное хранилище просто S3.

Chunks Storage

Изначальный движок в Cortex - он хранит каждую timeseries в отдельном чанке в S3 или в NoSQL (Cassandra/Amazon DynamoDB/Google BigTable), а метаданные (индексы) хранятся в NoSQL.

Chunks Storage, думается, со временем совсем выпилят - насколько я слышал, Grafana Labs свои метрики уже мигрировали в Blocks Storage.

Blocks Storage

Новый, более простой и быстрый движок, основанный на Thanos. Который, в свою очередь, использует формат блоков самого Prometheus. С ним нет нужды в NoSQL и модуле Table Manager (но нужен другой - Store Gateway).

Thanos, в данном, случае является внешней vendored зависимостью в коде Cortex. Есть разговоры о том, чтобы объединить два проекта в один, но когда это будет неизвестно (и будет ли вообще).

Архитектура

Далее я буду рассматривать работу с Blocks Storage.

Упрощённо принцип работы следующий:

  • Prometheus собирает метрики с endpoint-ов и периодически отправляет их в Cortex используя Remote Write протокол. По сути это HTTP POST с телом в виде сериализованных в Protocol Buffers метрик сжатый потом Snappy. В самом Prometheus, при этом, можно поставить минимальный retention period - например 1 день или меньше- читаться из него ничего не будет.

  • Модуль Distributor внутри Cortex принимает, валидирует, проверяет per-tenant и глобальные лимиты и опционально шардит пришедшие метрики. Далее он передает их одному или нескольким Ingester (в зависимости от того применяется ли шардинг).

    Также в рамках этого модуля работает HA Tracker (о нём ниже).

  • Ingester ответственен за запись метрик в долговременное хранилище и выдачу их для выполнения запросов. Изначально метрики записываются в локальную ФС в виде блоков длиной 2 часа. Затем, по истечении некоторого времени, они загружаются в S3 и удаляются с локальной ФС.

    Также поддерживается репликация и zone awareness для дублирования блоков по различным availability domain (стойки, ДЦ, AWS AZ и так далее)

  • Store-Gateway служит для отдачи блоков из S3.

    Он периодически сканирует бакет, находит там новые блоки, синхронизирует их заголовки в локальную ФС (чтобы после перезапуска не скачивать опять) и индексирует.

  • Querier реализует PromQL.

    При получении запроса анализирует его и, если необходимо, разбивает на два - одна часть пойдёт в Store Gateway (для более старых данных), а другая - в Ingester для свежих.

    По факту параллельных запросов может быть больше если запрашиваемый период большой и настроено разбиение по интервалам (об этом дальше в конфиге)

  • Compactor периодически просыпается и сканирует объектное хранилище на предмет блоков, которые можно склеить в более крупные. Это приводит к более эффективному хранению и быстрым запросам.

    Старые блоки не удаляются сразу, а маркируются и удаляются на следующих итерациях чтобы дать время Store-Gateway обнаружить новые, которые уже сжаты.

Отказоустойчивость

Помимо репликации данных между Ingester-ами нам необходимо обеспечить отказоустойчивость самих Prometheus. В Cortex это реализовано просто и элегантно:

  • Два (или более) Prometheus настраиваются на сбор метрик с одних и тех же endpoint-ов

  • В каждом из них настраиваются специальные внешние метки, которые показывают к какой HA-группе принадлежит данный Prometheus и какой у него идентификатор внутри группы.

    Например так:

  external_labels:    __ha_group__: group_1    __ha_replica__: replica_2
  • При приёме метрик Cortex из каждой группы выбирает один Prometheus и сохраняет метрики только от него

  • Остальным отвечает HTTP 202 Accepted и отправляет в /dev/null всё что они прислали

  • Если же активный инстанс перестал присылать метрики (сработал таймаут) - Cortex переключается на приём от кого-то из оставшихся в живых.

Таким образом дедупликация на этом уровне становится не нужна. Правда остаётся вопрос момента переключения - не теряется ли там что-либо (когда старый не прислал, а от нового ещё не приняли), я этот вопрос не изучал глубоко - нужно тестировать.

Авторизация

Каждый запрос на запись метрик из Prometheus должен содержать HTTP-заголовок X-Scope-OrgId равный идентификатору клиента (далее я буду называть их просто tenant, хорошего перевода не придумал). Метрики каждого tenant-а полностью изолированны друг от друга - они хранятся в разных директориях в S3 бакете и на локальной ФС

Таким же образом происходит и чтение метрик - в PromQL запросах тоже нужно тоже указывать этот заголовок.

При этом никакой реальной авторизации Cortex не проводит - он слепо доверяет этому заголовку. Auth Gateway есть в роадмапе, но когда он будет готов неизвестно. Даже просто добавить этот заголовок напрямую в Prometheus нельзя, только используя промежуточный HTTP прокси.

Для более гибкой интеграции Prometheus & Cortex я набросал простенький Remote Write прокси - cortex-tenant, который может вытаскивать ID клиента из меток Prometheus. Это позволяет использовать один инстанс (или HA-группу) Prometheus для отправки метрик нескольким разным клиентам. Мы используем этот функционал для разграничения данных разных команд, приложений и сред.

Авторизацию можно отключить, тогда Cortex не будет проверять наличие заголовка в запросах и будет подразумевать что он всегда равен fake - то есть multi-tenancy будет отключен, все метрики будут падать в один котёл.

При необходимости данные одного клиента можно полностью удалить из кластера - пока это API экспериментально, но работает.

Настройка Cortex

В первую очередь хотелось бы сказать, что Cortex имеет смысл для тех, у кого действительно много метрик, либо хочется хранить централизовано.

Для всех остальных гораздо проще установить несколько HA-пар Prometheus (например на каждую команду или каждый проект) и поверх них натянуть promxy

Так как документация имеет некоторое количество белых пятен - я хочу рассмотреть настройку простого кластера Cortex в режиме single binary - все модули у нас будут работать в рамках одного и того же процесса.

Danger Zone! Дальше много конфигов!

Зависимости

Нам понадобится ряд внешних сервисов для работы.

  • etcd для согласования кластера и хранения Hash Ring

    Cortex также поддерживает Consul и Gossip-протокол, которому не нужно внешнее KV-хранилище. Но для HA-трекера Gossip не поддерживается из-за больших задержек при сходимости. Так что будем юзать etcd

  • memcached для кеширования всего и вся.

    Cortex поддерживает его в нескольких своих модулях. Можно поднять несколько инстансов и указать их все в Cortex - он будет шардить по всем равномерно. В принципе, он не обязателен, но крайне рекомендован с точки зрения производительности.

    Также есть DNS-based discovery через SRV-записи, если не хочется указывать вручную.

  • minio для реализации распределённого S3 хранилища.

    Несколько странный проект, который часто в процессе разработки ломает обратную совместимость и требует полного перезапуска кластера для апгрейда. Также SemVer это для слабаков, поэтому у них версионирование по датам. Его разрабатывают, в основном, индусы - возможно в этом причина...

    Но других вариантов особо нет, можно поднять Ceph с S3 шлюзом, но это еще более громоздко.

    minio поддерживает Erasure Coding для отказоустойчивости, что есть хорошо.

  • HAProxy для связывания компонентов воедино

  • cortex-tenant для распределения метрик по tenant-ам

  • Prometheus собственно для сбора метрик

Общие вводные

  • Кластер мы будем строить плоский из 4 хостов - все они будут идентичны, с одинаковым набором сервисов. Это хорошо для небольших инсталляций, упрощает структуру.

    3 страйпа не поддерживает minio c Erasure Coding - он нарезает от 4 до 16 дисков в один EC-набор. В реальном проекте лучше использовать 5 или какое-либо большее нечетное число чтобы не было Split Brain.

    Также, если у вас много хостов, то некоторые компоненты, такие как etcd, лучше вынести отдельно. etcd основан на Raft и реплицирует данные на все ноды кластера, смысла в их большом количестве нет - это только увеличит среднюю нагрузку лишними репликами.

  • Все данные будем хранить в /data

  • Конфиги я буду приводить для одного хоста, для остальных обычно достаточно поменять адреса и\или хостнеймы

  • В качестве ОС используем RHEL7, но различия с другими дистрибутивами минимальны

  • У нас всё это дело, конечно, раскатывается через Ansible, но плейбук довольно сильно завязан на нашу инфраструктуру. Я постараюсь потом его подчистить и выложить

  • Некоторые RPM пакеты я собираю вручную (etcd, HAProxy и т.п.) с помощью FPM т.к. в репозиториях древние версии.

/etc/hosts
10.0.0.1 ctx110.0.0.2 ctx210.0.0.3 ctx310.0.0.4 ctx4

etcd

Как и Zookeeper, с настройками по умолчанию etcd - бомба замедленного действия. Он не удаляет ненужные снапшоты и разрастается до бесконечности. Зачем так сделано - мне не понятно.

Поэтому настроим его соответственно:

/etc/etcd/etcd.conf
ETCD_NAME="ctx1"ETCD_LOGGER="zap"ETCD_LOG_LEVEL="warn"ETCD_DATA_DIR="/data/etcd/ctx1.etcd"ETCD_LISTEN_CLIENT_URLS="http://personeltest.ru/away/10.0.0.1:2379,http://127.0.0.1:2379"ETCD_LISTEN_PEER_URLS="http://personeltest.ru/away/10.0.0.1:2380"ETCD_ADVERTISE_CLIENT_URLS="http://personeltest.ru/away/10.0.0.1:2379"ETCD_INITIAL_CLUSTER_TOKEN="cortex"ETCD_INITIAL_ADVERTISE_PEER_URLS="http://personeltest.ru/away/10.0.0.1:2380"ETCD_AUTO_COMPACTION_RETENTION="30m"ETCD_AUTO_COMPACTION_MODE="periodic"ETCD_SNAPSHOT_COUNT="10000"ETCD_MAX_SNAPSHOTS="5"ETCD_INITIAL_CLUSTER="ctx1=http://ctx1:2380,ctx2=http://ctx2:2380,ctx3=http://ctx3:2380,ctx4=http://ctx4:2380"

memcached

Тут всё просто, главное выделить нужное количество памяти. Она зависит от количества метрик, которые мы будем хранить. Чем больше уникальных комбинаций меток - тем больше нужно кеша.

/etc/sysconfig/memcached
PORT="11211"USER="memcached"MAXCONN="512"CACHESIZE="2048"OPTIONS="--lock-memory --threads=8 --max-item-size=64m"

Minio

Тут минимум настроек.

По сути мы просто перечисляем хосты, которые будут использоваться для хранения данных (+ путь до директории где данные хранить - /data/minio) и указываем ключи S3. В моем случае это были ВМ с одним диском, если у вас их несколько - то формат URL несколько меняется.

По умолчанию используется странное распределение дисков под данные и под коды Рида-Соломона: половина сырого объема уходит под redundancy. Так как у нас всего 4 хоста - это не особо важно. Но на большем по размеру кластере лучше использовать Storage Classes для снижения доли Parity-дисков.

/etc/minio/minio.env
MINIO_ACCESS_KEY="foo"MINIO_SECRET_KEY="bar"MINIO_PROMETHEUS_AUTH_TYPE="public"LISTEN="0.0.0.0:9000"ARGS="http://personeltest.ru/away/ctx{1...4}/data/minio"

Также нужно будет создать бакет с помощью minio-client - в нашем случае пусть называется cortex

HAProxy

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

Таким образом мы имеем что-то вроде Full Mesh топологии и отказ или перезапуск любого из сервисов или хостов целиком не влияет на функциональность кластера.

На больших кластерах (сотни-тысячи хостов) такая схема может быть узким местом, но если вы работаете с такими, то и сами это знаете :)

/etc/haproxy/haproxy.cfg
global    daemon    maxconn 10000    log 127.0.0.1 local2    chroot /var/emptydefaults    mode http    http-reuse safe    hash-type map-based sdbm avalanche    balance roundrobin    retries 3    retry-on all-retryable-errors    timeout connect 2s    timeout client 300s    timeout server 300s    timeout http-request 300s    option splice-auto    option dontlog-normal    option dontlognull    option forwardfor    option http-ignore-probes    option http-keep-alive    option redispatch 1    option srvtcpka    option tcp-smart-accept    option tcp-smart-connect    option allbackupslisten stats    bind 0.0.0.0:6666    http-request use-service prometheus-exporter if { path /metrics }    stats enable    stats refresh 30s    stats show-node    stats uri /frontend fe_cortex    bind 0.0.0.0:8090 tfo    default_backend be_cortexfrontend fe_cortex_tenant    bind 0.0.0.0:8009 tfo    default_backend be_cortex_tenantfrontend fe_minio    bind 0.0.0.0:9001 tfo    default_backend be_miniobackend be_cortex    option httpchk GET /ready    http-check expect rstring ^ready    server ctx1 10.0.0.1:9009 check observe layer7 inter 5s    server ctx2 10.0.0.2:9009 check observe layer7 inter 5s    server ctx3 10.0.0.3:9009 check observe layer7 inter 5s    server ctx4 10.0.0.4:9009 check observe layer7 inter 5sbackend be_cortex_tenant    option httpchk GET /alive    http-check expect status 200    server ctx1 10.0.0.1:8008 check observe layer7 inter 5s    server ctx2 10.0.0.2:8008 check observe layer7 inter 5s backup    server ctx3 10.0.0.3:8008 check observe layer7 inter 5s backup    server ctx4 10.0.0.4:8008 check observe layer7 inter 5s backupbackend be_minio    balance leastconn    option httpchk GET /minio/health/live    http-check expect status 200    server ctx1 10.0.0.1:9000 check observe layer7 inter 5s    server ctx2 10.0.0.2:9000 check observe layer7 inter 5s backup    server ctx3 10.0.0.3:9000 check observe layer7 inter 5s backup    server ctx4 10.0.0.4:9000 check observe layer7 inter 5s backup

cortex-tenant

Это просто прокси между Prometheus и Cortex. Главное - выбрать уникальное имя метки для хранения там tenant ID. В нашем случае это ctx_tenant

/etc/cortex-tenant.yml
listen: 0.0.0.0:8008target: http://127.0.0.1:8090/api/v1/pushlog_level: warntimeout: 10stimeout_shutdown: 10stenant:  label: ctx_tenant  label_remove: true  header: X-Scope-OrgID

Prometheus

В случае 4 хостов Prometheus-ы можно разбить их на две HA-пары, каждую со своим ID группы и раскидать job-ы по ним.

host1 /etc/prometheus/prometheus.yml
global:  scrape_interval: 60s  scrape_timeout: 5s  external_labels:    __ha_group__: group_1    __ha_replica__: replica_1remote_write:  - name: cortex_tenant    url: http://127.0.0.1:8080/pushscrape_configs:  - job_name: job1    scrape_interval: 60s    static_configs:      - targets:          - ctx1:9090        labels:          ctx_tenant: foobar  - job_name: job2    scrape_interval: 60s    static_configs:      - targets:          - ctx2:9090        labels:          ctx_tenant: deadbeef
host2 /etc/prometheus/prometheus.yml
global:  scrape_interval: 60s  scrape_timeout: 5s  external_labels:    __ha_group__: group_1    __ha_replica__: replica_2remote_write:  - name: cortex_tenant    url: http://127.0.0.1:8080/pushscrape_configs:  - job_name: job1    scrape_interval: 60s    static_configs:      - targets:          - ctx1:9090        labels:          ctx_tenant: foobar  - job_name: job2    scrape_interval: 60s    static_configs:      - targets:          - ctx2:9090        labels:          ctx_tenant: deadbeef

По сути конфигурации в пределах одной HA-группы должны отличаться только лейблом реплики.

Cortex

Ну и последнее. Так как мы будем запускать все модули вместе - конфиг получится довольно объемный. Поэтому разделим его на части, чтобы читабельнее было.

Многие модули, такие как Distributor, Ingester, Compactor, Ruler кластеризуются с помощью Hash-Ring в etcd. На весь кластер выделяется некоторое количество токенов, которые распределяются между всеми участниками кольца равномерно.

Упрощенно - при приходе, допустим, новой метрики её метки хешируются, результат делится по модулю на количество токенов и в итоге направляется на хост, который владеет данным диапазоном токенов. Если он не отвечает - то отправляют следующему по кольцу хосту.

Если хост выходит из кластера (помер, перезагружается и т.п.), то его диапазон перераспределяется по остальным.

Все настройки у нас будут лежать в /etc/cortex/cortex.yml

Также т.к. Cortex сделан для работы в контейнерах - всё можно настроить через командную строку чтобы не пропихивать конфиг в контейнер.

Глобальные настройки

# Список модулей для загрузкиtarget: all,compactor,ruler,alertmanager# Требовать ли заголовок X-Scope-OrgIdauth_enabled: true# Портыserver:  http_listen_port: 9009  grpc_listen_port: 9095limits:  # Разрешаем HA-трекинг  accept_ha_samples: true  # Названия меток, которые мы используем в Prometheus для  # маркировки групп и реплик  ha_cluster_label: __ha_group__  ha_replica_label: __ha_replica__  # Максимальный период в прошлое на который мы можем делать  # PromQL запросы (1 год).  # Всё что больше будет обрезано до этого периода.  # Это нужно для реализации retention period.  # Для фактического удаления старых блоков нужно еще настроить lifecycle  # правило в бакете S3 на пару дней глубже  max_query_lookback: 8760h

Так как Cortex создавался для работы в распределённом режиме в облаке, то работа всех модулей в одном бинарнике считается нетипичной, но я никаких проблем не наблюдал.

Другой трудностью изначально было то, что Cortex не поддерживал гибкий список модулей, которые нужно активировать. Была возможность либо указать all, который на самом деле ни разу не all:

# cortex -modulesalertmanagerallcompactorconfigsdistributor *flusheringester *purger *querier *query-frontend *query-schedulerruler *store-gateway *table-manager *Modules marked with * are included in target All.

Либо указать строго один модуль.

Поэтому пришлось сделать пулл-реквест чтобы добавить возможность загружать список любых модулей. В данном случае мы используем all + compactor, ruler и alertmanager

Хранилище

storage:  # Выбираем хранилище Blocks Storage  engine: blocks# Конфигурируем егоblocks_storage:  # Тип бэкенда  backend: s3    # Параметры доступа к S3  s3:    endpoint: 127.0.0.1:9001    bucket_name: cortex    access_key_id: foo    secret_access_key: bar    # TLS у нас нет    insecure: true    tsdb:    # Где хранить локальные блоки до загрузки в S3    dir: /data/cortex/tsdb    # Через какое время их удалять    retention_period: 12h    # Сжимать Write-Ahead Log    wal_compression_enabled: true  bucket_store:    # Где хранить индексы блоков, найденных в S3    # По сути это должно быть в модуле Store-Gateway,    # но по какой-то причине тут    sync_dir: /data/cortex/tsdb-sync    # Как часто сканировать S3 в поиске новых блоков    sync_interval: 1m    # Настраиваем различные кеши на наши memcached    # Каждый кеш имеет свой префикс ключей, так что пересекаться они не будут    index_cache:      backend: memcached      memcached:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211    chunks_cache:      backend: memcached      memcached:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211    metadata_cache:      backend: memcached      memcached:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211

Distributor

distributor:  ha_tracker:    # Включить HA-трекер для Prometheus    enable_ha_tracker: true    # Таймаут после которого срабатывает failover на другую реплику Prometheus.    # Нужно настроить так чтобы метрики приходили не реже этого интервала,    # иначе будут ложные срабатывания.    ha_tracker_failover_timeout: 30s    # Настраиваем etcd для HA-трекера    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379  # Настраиваем etcd для Hash-Ring дистрибьютеров  ring:    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379

Ingester

ingester:  lifecycler:    address: 10.0.0.1    # Название зоны доступности    availability_zone: dc1    # Немного ждём чтобы всё устаканилось перед перераспределением    # токенов на себя    join_after: 10s    # Храним токены чтобы не генерировать их каждый раз при запуске    tokens_file_path: /data/cortex/ingester_tokens    ring:      # На сколько Ingester-ов реплицировать метрики.      # Если указана зона доступности, то реплики будут выбираться из разных зон      replication_factor: 2      # etcd для Hash-Ring Ingester-ов      kvstore:        store: etcd        etcd:          endpoints:           - http://ctx1:2379           - http://ctx2:2379           - http://ctx3:2379           - http://ctx4:2379

Querier

По поводу подбора правильных величин лучше почитать документацию.

Основная идея в том, чтобы никогда не запрашивать те блоки, которые еще не обработал Compactor:

querier:  # Временные файлы  active_query_tracker_dir: /data/cortex/query-tracker  # Запросы с глубиной больше этой будут направляться в S3  query_store_after: 6h  # Запросы с глубиной меньше этой отправляются в Ingester-ы  query_ingesters_within: 6h5mfrontend_worker:  frontend_address: 127.0.0.1:9095query_range:  # Запросы будут разбиваться на куски такой длины и выполняться параллельно  split_queries_by_interval: 24h  # Выравнивать интервал запроса по его шагу  align_queries_with_step: true  # Включить кеширование результатов  cache_results: true    # Кешируем в memcached  results_cache:    # Сжимаем    compression: snappy    cache:      # TTL кеша      default_validity: 60s      memcached:        expiration: 60s      memcached_client:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211

Store-Gateway

Этот модуль подгружает из S3 бакета заголовки блоков (комбинации меток, временные интервалы и т.п.).

Если включить шардинг и репликацию, то участники кластера распределят все блоки между собой равномерно и каждый блок будет на 2 или более хостах.

store_gateway:  # Включаем шардинг  sharding_enabled: true  sharding_ring:    # Включаем zone awareness    zone_awareness_enabled: true    # Идентификатор зоны    instance_availability_zone: dc1    # Сколько реплик держать    replication_factor: 2    # Hash-ring для Store-Gateway    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379

Compactor

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

compactor:  # Временная директория для блоков.  # Должно быть достаточно много места чтобы можно было загрузить блоки,  # скомпактить их и сохранить результат.  data_dir: /data/cortex/compactor  # Как часто запускать компакцию  compaction_interval: 30m  # Hash-Ring для компакторов  sharding_enabled: true  sharding_ring:    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379

Ruler + AlertManager

Эти модули опциональны и нужны только если хочется генерировать алерты на основе правил.

  • Правила в стандартном Prometheus формате мы будем складывать в /data/cortex/rules/<tenant>/rulesN.yml на каждом хосте. Можно использовать для этого S3 или другие хранилища - см. документацию

  • Cortex периодически сканирует хранилище и перезагружает правила

  • Конфиги AlertManager в стандартном формате складываем в /data/cortex/alert-rules/<tenant>.yml

    Аналогично можно складывать в S3 и т.п.

  • Cortex запускает инстанс AlertManager (внутри своего процесса) отдельно для каждого tenant, если находит конфигурацию в хранилище

ruler:  # Временные файлы  rule_path: /data/cortex/rules-tmp  # Включаем шардинг  enable_sharding: true  # Какому AlertManager-у сообщать об алертах  alertmanager_url: http://ctx1:9009/alertmanager  # Откуда загружать правила  storage:    type: local    local:      directory: /data/cortex/rules    # Hash-ring для Ruler-ов  ring:    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379alertmanager:  # Где хранить состояние алертов  data_dir: /data/cortex/alert-data  # Внешний URL нашего инстанса (нужен для генерации ссылок и т.п.)  external_url: http://ctx1:9009/alertmanager  # Кластеринг - какой адрес слушать и какой анонсировать пирам  cluster_bind_address: 0.0.0.0:9094  cluster_advertise_address: 10.0.0.1:9094  # Список пиров  peers:    - ctx2:9094    - ctx3:9094    - ctx4:9094  # Откуда загружать настройки  storage:    type: local    local:      path: /data/cortex/alert-rules

Заключение

Вот и всё, можно запускать все сервисы - сначала зависимости, потом Cortex, затем - Prometheus.

Я не претендую на полноту, но этого должно быть достаточно чтобы начать работать.

Нужно учитывать, что Cortex активно развивается и на момент написания статьи часть параметров в master-ветке и документации (которая генерируется из неё) уже объявлено deprecated. Так что, вполне возможно, в новых версиях нужно будет конфиги немного исправлять.

Если есть вопросы и\или замечания - пишите, постараюсь добавить в статью.

Подробнее..

Перевод Мониторинг Kubernetes с помощью Prometheus и Thanos

05.11.2020 16:06:36 | Автор: admin

В преддверии старта профессионального курса Мониторинг и логирование: Zabbix, Prometheus, ELK, подготовили для вас интересный перевод, а также предлагаем посмотретьдемо-урок по теме: Prometheus как новый виток систем мониторинга.


Введение

Поздравляем! Вам удалось убедить ваше начальство в миграции приложений на микросервисную архитектуру с использованием контейнеров и Kubernetes.

Вы очень довольны и все идет по плану. Вы создаете свой первый кластер Kubernetes (у всех основных облачных провайдеров: Azure, AWS и GCP, есть простые решения для провиженинга управляемого или неуправляемого Kubernetes), разрабатываете первое контейнерное приложение и развертываете его в кластере. Это было легко, не так ли?

Через некоторое время вы понимаете, что все становится немного сложнее: вам нужно развернуть в кластере несколько приложений, поэтому вам нужен Ingress Controller. Далее вы хотите мониторить нагрузку, поэтому вы начинаете искать решения для этого и, к счастью, находите Prometheus. Разворачиваете его, добавляете Grafana и все!

Позже вы начинаете задаваться вопросом: "Почему Prometheus работает только с одной репликой"? Что произойдет в случае рестарта контейнера? Что будет при простом обновлении версии? Как долго Prometheus может хранить метрики? Что если кластер развалится? Нужен ли еще один кластер для HA и DR? Как мне получить единое представление метрик со всех серверов Prometheus?

Что ж, продолжайте читать, умные люди уже разобрались с этими вопросами.

Типичный кластер Kubernetes

На диаграмме ниже показано типичное развертывание с использованием Kubernetes.

Типичный кластер KubernetesТипичный кластер Kubernetes

Развертывание состоит из трех уровней:

  1. Виртуальные машины: master-ноды и worker-ноды.

  2. Инфраструктурные компоненты Kubernetes.

  3. Пользовательские приложения.

Внутри кластера компоненты взаимодействуют друг с другом обычно через HTTP(s) (REST или gRPC), но некоторые из них предоставляют доступ к API снаружи (Ingress). Эти API в основном используются для следующего:

  1. Управление кластером через Kubernetes API Server.

  2. Взаимодействие с пользовательскими приложениями через Ingress Controller.

В некоторых сценариях приложения могут отправлять трафик за пределы кластера для использования сторонних сервисов, таких как Azure SQL, Azure Blob или любых других.

Что мониторить?

Мониторинг Kubernetes должен включать в себя все три уровня, упомянутые выше.

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

  • Количество узлов.

  • Утилизация ресурсов на узле (процессор, память, диск, пропускная способность сети).

  • Состояние узла (работает, не работает и т.п.).

  • Количество подов, работающих на каждом узле.

Инфраструктура Kubernetes. Для того чтобы убедиться в работоспособности инфраструктуры Kubernetes необходимо собирать следующие метрики:

  • Состояние подов ready (сколько реплик доступно), status, restarts (количество рестартов), age (сколько времени приложение запущено).

  • Статус развертываний (Deployments) desired (целевое количество реплик), current (текущее количество реплик), up-to-date (сколько реплик обновлено), available (сколько реплик доступно), age (сколько времени приложение запущено).

  • Статус StatefulSets.

  • Статистика выполнения CronJobs.

  • Использование ресурсов подом (процессор и память).

  • Проверки работоспособности (Health checks).

  • События Kubernetes.

  • Запросы к API-серверу.

  • Статистика Etcd.

  • Статистика смонтированных томов.

Пользовательские приложения. Каждое приложение должно предоставлять собственные метрики, основанные на его функциональности. Однако для большинства приложений есть общие метрики, такие как:

  • HTTP-запросы (общее количество, задержка, код ответа и т. д.).

  • Количество исходящих соединений (например, с базой данных).

  • Количество потоков.

Сбор метрик, упомянутых выше, позволит вам создавать осмысленные алерты и дашборды, о чем мы кратко поговорим далее.

Thanos

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

Для хранения исторических данных Thanos использует формат хранения Prometheus и может хранить метрики в любом объектном хранилище. Кроме того, он обеспечивает global view для всех экземпляров Prometheus.

Основные компоненты Thanos:

  • Sidecar. Подключается к Prometheus и использует его для запросов в реальном времени через Query Gateway и/или загружает его данные в облачное хранилище для длительного хранения.

  • Query Gateway. Реализует Prometheus API для агрегирования данных из нижележащих компонент (таких как Sidecar или Store Gateway).

  • Store Gateway. Предоставляет доступ к содержимому облачного хранилища.

  • Compactor. Делает уплотнение и даунсэмплинг (downsampling) данных в облачном хранилище.

  • Receiver. Получает данные из remote-write WAL Prometheus, предоставляет их и/или загружает в облачное хранилище.

  • Ruler. Вычисляет recording rules и alerting rules для данных в Thanos.

В этой статье мы сосредоточимся на первых трех компонентах.

Деплой Thanos

Мы начнем с развертывания Thanos Sidecar в тех же кластерах Kubernetes, которые мы используем для пользовательских приложений, Prometheus и Grafana.

Есть много способов установки Prometheus, но я предпочитаю использовать Prometheus-Operator, который предоставляет настройки для мониторинга сервисов и развертываний Kubernetes, а также для управления экземплярами Prometheus.

Самый простой способ установки Prometheus-Operator это использовать его Helm чарт, у которого есть встроенная поддержка высокой доступности, Thanos Sidecar и множество преднастроенных алертов для мониторинга кластерных виртуальных машин, инфраструктуры Kubernetes и ваших приложений.

Перед развертыванием Thanos Sidecar нам нужен Kubernetes Secret с информацией о том, как подключиться к облачному хранилищу.

Для демонстрации я буду использовать Microsoft Azure.

Создайте account для blob-хранилища:

az storage account create --name <storage_name> --resource-group <resource_group> --location <location> --sku Standard_LRS --encryption blob

Затем создайте папку (она же container) для метрик:

az storage container create --account-name <storage_name> --name thanos

Получите ключи от хранилища:

az storage account keys list -g <resource_group> -n <storage_name>

Создайте файл с настройками хранилища (thanos-storage-config.yaml):

Создайте Kubernetes Secret:

kubectl -n monitoring create secret generic thanos-objstore-config --from-file=thanos.yaml=thanos-storage-config.yaml

Создайте файл prometheus-operator-values.yaml, в котором переопределите настройки по умолчанию для Prometheus-Operator.

И деплой:

helm install --namespace monitoring --name prometheus-operator stable/prometheus-operator -f prometheus-operator-values.yaml

Теперь у вас должен быть высокодоступный Prometheus вместе с Thanos Sidecar, который загружает ваши метрики в Azure Blob Storage с неограниченным сроком хранения.

Для того чтобы Thanos Store Gateway предоставить доступ к Thanos Sidecar, нужно выставить его наружу через Ingress. Я использую Nginx Ingress Controller, но вы можете использовать любой другой Ingress Controller, который поддерживает gRPC (возможно, Envoy будет лучшим вариантом).

Для защищенного соединения между Thanos Store Gateway и Thanos Sidecar мы будем использовать mutual TLS. То есть клиент будет аутентифицировать сервер и наоборот.

Если у вас есть .pfx-файл, то вы можете извлечь из него закрытый, открытый ключ и сертификат с помощью openssl:

# public keyopenssl pkcs12 -in cert.pfx -nocerts -nodes | sed -ne '/-BEGIN PRIVATE KEY-/,/-END PRIVATE KEY-/p' > cert.key# private keyopenssl pkcs12 -in cert.pfx -clcerts -nokeys | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > cert.cer# certificate authority (CA)openssl pkcs12 -in cert.pfx -cacerts -nokeys -chain | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > cacerts.cer

Создайте из этого два Kubernetes Secrets.

# a secret to be used for TLS terminationkubectl create secret tls -n monitoring thanos-ingress-secret --key ./cert.key --cert ./cert.cer# a secret to be used for client authenticating using the same CAkubectl create secret generic -n monitoring thanos-ca-secret --from-file=ca.crt=./cacerts.cer

Убедитесь, что у вас есть домен, который резолвится в ваш кластер Kubernetes, и создайте два поддомена, которые будут использоваться для маршрутизации к каждому изThaos SideCar:

thanos-0.your.domainthanos-1.your.domain

Теперь мы можем создать правила Ingress (измените имя хоста):

Теперь у нас есть защищенный доступ к Thanos Sidecars снаружи кластера!

Кластер Thanos

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

Для развертывания компонентов Thanos я решил использовать этот Helm чарт (он пока еще не официальный, но следите за обновлениями, когда примут мой PR).

Создайте файл thanos-values.yaml, чтобы переопределить настройки чарта по умолчанию.

Поскольку для Thanos Store Gateway требуется доступ к blob-хранилищу, мы также создадим секрет хранилища в этом кластере.

kubectl -n thanos create secret generic thanos-objstore-config --from-file=thanos.yaml=thanos-storage-config.yaml

Для развертывания этого чарта мы возьмем те же сертификаты, которые использовали ранее.

helm install --name thanos --namespace thanos ./thanos -f thanos-values.yaml --set-file query.tlsClient.cert=cert.cer --set-file query.tlsClient.key=cert.key --set-file query.tlsClient.ca=cacerts.cer --set-file store.tlsServer.cert=cert.cer --set-file store.tlsServer.key=cert.key --set-file store.tlsServer.ca=cacerts.cer

Это команда установит Thanos Query Gateway и Thanos Storage Gateway, настроив их на использование защищенного канала.

Валидация

Чтобы проверить, все ли работает правильно, вы можете перенаправить порт в HTTP-службу Thanos Query Gateway следующим образом:

kubectl -n thanos port-forward svc/thanos-query-http 8080:10902

После этого откройте браузер по адресу http://localhost:8080, и вы должны увидеть Thanos UI!

Grafana

Для дашбордов вы можете установить Grafana, используя Helm чарт.

Создайте grafana-values.yaml со следующим содержимым:

Обратите внимание, что я добавил к нему три дефолтных дашборда. Вы также можете добавить и свои дашборды (самый простой способ это использовать ConfigMap).

Затем деплой:

helm install --name grafana --namespace thanos stable/grafana -f grafana-values.yaml

И опять port-forward:

kubectl -n thanos port-forward svc/grafana 8080:80

И всё! Вы развернули на основе Prometheus высокодоступное решение для мониторинга с долгосрочным хранилищем и централизованным представлением нескольких кластеров!

Другие варианты

Эта статья посвящена Prometheus и Thanos, но если вам не нужен global view для нескольких кластеров, то вы можете использовать только Prometheus с долговременным хранилищем.

Другим вариантом является Cortex еще одна платформа с открытым исходным кодом, которая немного сложнее, чем Thanos, и использует другой подход.


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

Подробнее..

KubeGraf плагин для мониторинга Kubernetes в Grafana. Как создавался и почему стал востребованным

11.02.2021 16:05:53 | Автор: admin


KubeGraf это плагин для Grafana, который собирает данные с кластера Kubernetes и приложений внутри него, а затем показывает их на красивых и понятных графиках. В феврале этого года вышел релиз 1.5, и стало известно, что предыдущие версии скачали более 250 тысяч раз! Мы расспросили Сергея Спорышева, создателя плагина и директора направления DevOps-продуктов в ITSumma, об истории создания плагина, факапах и причинах популярности.


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


Как-то, рассказывая о разработке KubeGraf, ты упомянул, что вы хотели сделать универсальный инструмент для мониторинга, который поставил и ничего больше делать не надо. Что скажешь спустя полтора года: получилось ли?


Я думаю, получилось. Здесь логично рассказать, откуда возникла идея плагина. В апреле 2019 года я помогал Евгению Потапову, гендиректору ITSumma, готовить доклад для конференции HighLoad в Питере. Он планировал рассказать про мониторинг Kubernetes: какие есть инструменты, подходы Всего этого и методологий, и инструментов, было очень много, но какого-то более-менее универсального решения, чтобы поставил и ничего делать не нужно, не было.


Углубляясь, мы нашли у самой Grafana плагин для мониторинга Kubernetes. Он вроде был жив, но почему-то не поддерживался. Мы его поставили и увидели, что половина функций не работает. Так возникла идея написать своё и докинуть туда багаж практик, который мы наработали внутри компании (одно из направлений ITSumma как раз техподдержка 24/7 и, соответственно, круглосуточный мониторинг).


Была ещё одна вещь, которую мы хотели реализовать. В России раскатка на bare metal это нормальное явление, а все прогрессивные страны пользуются managed-решениями: Kubernetes от Google, Amazon (у нас теперь ещё Яндекс и Mail.ru). В работе с облаками есть свои нюансы, и мы подумали почему бы не сделать плагин, который будет эти нюансы учитывать?


Такой был план: написать плагин, основываясь на решении, выпущенном самой Grafana, дополнить его своими наработками и сделать так, чтобы он работал везде одинаково. Я думаю, получилось. Лично я его юзаю, и наши клиенты тоже: причём как те, кто на голом железе сидит, так и те, кто сидит в Google и Amazon. Лишних телодвижений делать не приходится никому.


А какие фичи плагина ты используешь каждый день?


Одна из основных фишек это как раз возможность работы с облачными провайдерами. Обычно во всех этих системах используется авторизация по сертификатам. В облаке сертификаты тебе никто не даст, поэтому мы сделали возможность работы через токены. Ты применяешь кубовые манифесты, регистрируешь пользователя, выдаешь ему права, и с этим пользователем плагин работает.


Мы мониторим как приложения, которые крутятся в Kubernetes, так и ноды, на которых Kubernetes работает. В реальном времени можно смотреть нагрузку по ноде, сколько ресурсов на ней зарезервировано и какие выставлены лимиты. Например, мы индицируем, что у ноды есть всего лишь 2.5 Гб памяти, 2 Гб которых уже зарезервировано, и тогда подкрашиваем ноду жёлтым либо красным, таким образом предупреждая: Ребят, эта нода скоро закончится. То же самое с лимитами.



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



Второй режим просмотра это так называемая Карта всех приложений. Виден список всех приложений, как они сгруппированы по неймспейсам, какие сущности за собой тянут, сколько там реплик. Все красивенько отображается.



Ну, и последний ключевой момент в одном месте сведена информация обо всех предупреждениях, ошибках, критических ситуациях. Ты можешь просто открыть страничку Cluster Status и посмотреть, что у тебя с кластером происходит. Куда надо обратить внимание, куда не надо.




Можешь вспомнить ситуации из своей рабочей практики, когда KubeGraf реально тебе помог?
Это случилось, когда мы научились правильно индицировать лимиты, реквесты и всё остальное. На одном из проектов облачный кластер Kubernetes в момент пиковых нагрузок разрастался до 25-26 нод. Мы стали смотреть на данные из плагина и, пользуясь ими, сократили парк почти в два раза. Сейчас этот же кластер с большим количеством приложений спокойно работает на 12-13 нодах.


При этом ресурсы кластера мы никак не разбирали. Мы просто смотрели, что такие-то приложения при таком-то трафике практически не должны ничего жрать, но потребление вырастает до колоссальных размеров. Анализировали приложение, смотрели, что где можно подтюнить, как именно построен scheduling на основе реквестов. Видели, например, что зарезервировано 1.5 Гб памяти, но за два месяца никто столько никогда не использовал и можно это сократить. Потихоньку-помаленьку сократили кластер в два раза.


В новостях о плагине вы писали, что его скачали 250 000 раз


По не совсем официальной информации да. Это такая информация для разработчиков, её в паблике нет, но вот я нечаянно узнал. Круто!


Это много или мало?


Много! У нас четыре с лишним сотни клиентов, у одного клиента может быть максимум кластеров 10. Если мы хотя бы 400 умножим на 10 это 4 000. То есть если бы каждый наш клиент был с Kubernetes, мы бы каждому накатили и получили только 4 000. А тут четверть миллиона! Это много и это круто.


Как думаешь, в чём причина такой популярности?


Один из ключевых факторов: плагин от Grafana, о котором я уже упоминал, разработчики по неизвестным причинам перестали поддерживать. И теперь в issues или в pull request на GitHub, я не помню точно, их спрашивают: ребята, вы собираетесь этот инструмент дальше развивать или нет? А они говорят: нет, вот вам ссылка на нормальное решение. И дают ссылку на нас.


Плюс сарафанное радио: мы раскидываем плагин клиентам, активно взаимодействуем с сообществом, много где его презентуем. Скорее всего, в этом причина.


А если оценивать аудиторию англоязычную, русскоязычную как тебе кажется, где KubeGraf больше используют?


Мне кажется, это неправильным в современном мире разделять аудиторию на русскоговорящую и не русскоговорящую.


Недавно вышла версия 1.5. Что там нового?


Мы починили большое количество ошибок совместимости. В середине 2020 года релизнулась седьмая версия Grafana, там всё еще работало, а с 7.3 начались большие изменения в структуре, поэтому пришлось дорабатывать плагин, чтобы ничего не отваливалось. Плюс добавили поддержку последних версий Kubernetes, доработали, по каким путям мы забираем данные из API Kubernetes.


В версии 1.5 мы добавили сбор метрик по лимитам для приложений. До этого работали только с реквестами и текущим потреблением: приложение сколько-то под себя резервирует и сколько-то использует в реальном времени. Но со временем поняли, что очень важна еще и работа с лимитами.


Переработали UX, чтобы было удобно для восприятия, добавили реалтайм индикации. Раньше была сводная таблица по всем приложениям, и мы просто видели, сколько они резервируют и потребляют. В 1.5 добавили индикацию: в табличке подсвечено, где что-то не указано, где зеленая зона, где желтая, красная и так далее.


Как обычно, были небольшие фиксы: где-то слетела легенда, где-то кнопка не работала поправили.


Последнее ключевое в сводной таблице по состоянию кластера мы наконец-то догадались ввести сортировку по важности уведомления: жёлтое, красное и т. д.


Алертинга у вас нет?


Тут можно холиварить долго, но устоявшееся мнение такое: алерты и Grafana это разные вещи. Алерты в Grafana делать можно, но это неправильно. Для алертинга нужен другой инструмент: Prometheus+Alert Manager, например. Grafana это про визуализацию. Мы только планируем на графике сделать индикацию алертинга.


Ты говорил, что многие фичи появлялись, исходя из ваших потребностей. А были такие, которые вы делали по запросам?


Конечно! Здесь соотношение где-то 60/40. 60 это наша практика. 40 это общение с комьюнити, с клиентами. Сейчас мы работаем над версией 1.6, и одним из ключевых новшеств в ней будет обновлённый дашборд. К нам пришли клиенты, которые активно юзают плагин, и говорят: ребята, мы пользуемся, всё круто, но хотелось бы увидеть ещё такую вот панель. Мы отвечаем: ну, супер.


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


Заспойлери что-нибудь ещё из версии 1.6.


Будет небольшой редизайн, потому что первый макет создавался кустарно. Сначала мы с Женей Потаповым набросали на бумажке, как может выглядеть интерфейс. Потом один из наших разработчиков разобрал тулкит Grafana с визуализационными элементами, и мы просто впихали всё, как было. Позже подключился дизайнер, и где-то в версии 1.3-1.4 мы переработали одну из страниц. Теперь перерабатываем остальные и в 1.6 затащим красоты.


Плюс к странице Cluster Status появится сводный дашборд по общему состоянию кластера. Не по отдельным нодам, а именно по всему кластеру, как если бы он был одним большим сервером. Дашборд даст понимание, как он себя чувствует.


Ну и мы хотим полностью переписать плагин. С выхода первой версии прошло два года, Grafana выпустила много вспомогательных инструментов для разработки. Поэтому мы полностью переписываем сборку, переходим на использование компонентов от Grafana и другой фреймворк: плагин написан на Angular, а где-то год назад пошёл тренд на React-плагины, поэтому я думаю, что после 1.6 будет сразу версия 2.0 на React.


С какой скоростью выпускаете новые версии?


Критичные минорные изменения вносим так быстро, как это только возможно, но вообще стараемся релизиться раз в два месяца. Только с версией 1.5 что-то пошло не так готовили её почти полгода. Сказался и личный загруз (это все-таки наш open source проект), и эпидемия, и задержки со стороны Grafana. С релиза версии может пройти от недели до двух месяцев, когда её примут и опубликуют на сайте Grafana. Поэтому релизный цикл плавающий.


Как устроена работа над плагином: сколько человек в команде, сколько времени каждый из вас уделяет разработке?


Команда это два разработчика: я и мой коллега Дмитрий Воронов. Когда нужно, подключаем дизайнера. Менеджерю этот процесс я сам. Если по работе у нас ничего не горит, то понедельник мы посвящаем плагину. Понедельник у нас Grafana Day, такая разминка перед тяжёлой рабочей неделей.


То есть это не выходные, не ночь, а полноценный рабочий день?


Да. Единственное это предрелизная штука, когда обычно я на себя всё захватываю. Там может выжирать и три дня в неделю. Но стандартно день.


В таком режиме получается отдыхать от плагина? Когда два года делаешь какую-то штуку, она может слегка поднадоесть, нет?


Бывает такое, но можно взять на неделю перерыв. Это же pet project: поднадоело давайте отложим.


Были за эти два года такие факапы, которые прям совсем факапы?


Конечно, они бывают в любой разработке. Как-то раз выкатили версию, которая не работала. Потестили на одной из версий Grafana, а на остальных нет. Где-то всё поехало, где-то метрики перестали приходить.


Заметили довольно быстро, потому что мы обычно, пока ждём официального релиза от Grafana, ходим и через Git и обновляем это всё у наших клиентов. Видим, где ломается.


Второй факап был прямо жёсткий. В одной из версий Kubernetes и Prometheus полностью сменился лейблинг. До этого в основных метриках было два лейбла: container name и pod name, а тут они поменялись на container и pod (я уже сейчас не вспомню точно). В какой-то момент мы заметили, что нейминг разъехался: здесь мы собираем по pod, здесь по pod name; здесь по container, здесь по container name. И у нас половина плагина работала, половина не работала. Никто сначала ничего не мог понять. Пришлось пойти, разобраться, упороться регулярками, которые все ненавидят. Ну и вроде собрали, заработало.


Но, в целом, за счет того, что time-to-market у плагина очень высокий, у нас всегда есть минимум две недели в ожидании релиза, когда можно сходить всё исправить. То есть факапы они локальные, в паблик не могут уйти.


Если бы ты сейчас подходил к разработке плагина, что-нибудь бы сделал иначе?


Думаю, нет. Я даже не знаю, что можно было изменить, потому что оно как шло по желанию, так и шло. Какие-то фишки придумывались, какие-то дорабатывались. Что-то изменить? Не знаю.


Какую побочную, помимо технической, пользу принёс плагин тебе самому и вашей компании?


Brand awareness узнаваемость бренда. Это самое большое, что мы получили. Когда компания выпускает open source продукты, она закрепляет свою экспертизу, повышает узнаваемость. На приток клиентов не рассчитывали. Хотели только, чтобы нас узнавали, чтобы люди понимали, что мы это умеем и в этом шарим.


Мне кажется, у нас получилось, потому что про плагин писали везде. Очень много материалов в интернете, и везде ссылочки на нас. Да, определённый процент клиентов приходили с плагина: понимали, что в теме Kubernetes и мониторинга нам можно довериться, и работали с нами дальше.


В небольшом видео с анонсом версии 1.5 ты сказал, что вы планируете сделать плагин отдельным продуктом. Что это за идеи?


У нас была мысль сделать плагин прямо такой stand-alone версией, потому что мы наработали большое количество практик в работе с Kubernetes, и хотели бы их реализовать в отдельном продукте.


Мы думаем про сервис, который будет делать не только визуализацию, но поможет проводить аудит кластера и тюнить его. Grafana здесь будет только мешать, поэтому думаем оформить его в виде stand-alone решения, которое просто так же поставил через Helm и видишь красивую картиночку, где у тебя что происходит. Но это всё пока только на уровне проработки концепции.


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


Если это будет отдельный продукт, который будет шире, чем просто мониторинг и визуализация, он будет платным?


Open source продукты принято делать бесплатными. Но, например, Grafana тоже open source, но есть Grafana Enterprise. Мне кажется, этот продукт будет в двух вариациях free и с какой-то подпиской.


Такой серьезный продукт придётся не только по понедельникам делать.


Да.


Выходит, ITSumma сейчас смотрит чуть в сторону open sourse, в направлении разработки своих продуктов?


Конечно! Нужны же какие-то артефакты работы компании. Хоть что-то после себя оставить нужно.


На этом мы закончили обсуждать плагин KubеGraf и немного поговорили о видеокурсе по мониторингу Kubernetes, который Сергей Спорышев вместе с другими спикерами разрабатывал в Слёрме. К беседе присоединился СТО Слёрма Марсель Ибраев.


Что будет в новом курсе?


МАРСЕЛЬ: Мы будем говорить о том, как мониторить и собирать логи с приложений, которые запущены в кластере, и с самого кластера Kubernetes. Потому что просто поднять кластер это полдела, и с этим справляется большинство, а вот правильно настроить сбор логов и метрик уже получается не у всех. Цель курса рассказать и показать лучшие практики.


Стоит ли идти на курс по логированию тем, кто уже проходил курсы по Kubernetes в Слёрме?


МАРСЕЛЬ: Стоит, потому что в наших курсах по Kubernetes не раскрывается тема логирования и мониторинга. Мы пробовали её включить в первые базовые курсы, но получалось не очень: материала много, и дать его в рамках двух занятий внутри обзорного курса по Kubernetes невозможно. Уже тогда была идея, что надо делать что-то отдельное. Наконец-то мы к этому пришли.


Чем хорош курс? Почему вы бы сами на него пошли?


МАРСЕЛЬ: Во-первых, курс подготовили практикующие инженеры ребята, которые каждый день работают с Kubernetes, набивают шишки. Они рассказывают именно то, что нужно знать. Это чистый опыт. Да, его можно наработать и самому, но мы конвертируем время в деньги (и наоборот) и приносим опыт на блюдечке.


Мы не обещаем, что, пройдя курс, вы сразу станете крутым специалистом. Чтобы освоить все инструменты, нужно много практиковаться. Но начать это делать можно уже во время обучения. И это вторая причина, по которой я бы сам на него пошёл: Слёрм даёт практические задания и стенды для их выполнения. У студента есть все возможности, чтобы закрепить знания на практике и получить навыки, которые потом можно оттачивать на реальных проектах.


Сергей, ты один из спикеров курса по мониторингу и логированию в Kubernetes. Как тебе кажется, чем этот курс хорош?


СЕРГЕЙ: Я впервые участвую в создании такой штуки, но, посмотрев на внутреннюю кухню, поучаствовав в проработке одной из тем, пообщавшись с другими спикерами, я понял, что все спикеры максимально детально разжевывают материал, но делают это не в формате старых книг вроде PHP для чайников, а на реальных практических примерах, затрагивая современные аспекты технологии.


Сама площадка тоже топчик. Я планировал писать на локальном кластере, но потом понял, что всё есть, всё удобно, и с минимальными усилиями за минимальное количество времени ты можешь попробовать выполнять практические задания, в реальном времени видеть результат.


Всё сделано по-максимуму для студента, слушателя курса. Без воды, без лишнего материала. Всё учтено. Такая тонкая грань между вводом и хардкором.


Программа курса максимально последовательная. Когда меня только привлекли к подготовке, я прочитал план и понял, что он составлен идеально. Студента проводят по тем же этапам, которые он бы прошёл сам, настраивая мониторинг и логирование.


Кому будет полезен этот курс?


СЕРГЕЙ: Есть замечательная шутейка, что в современных реалиях Kubernetes это операционная система, а Helm просто пакетный менеджер для него. Всем приходится иметь дело с Kubernetes, значит всем нужно знать мониторинг и логирование такой промышленный стандарт. То есть курс будет полезен всем.


Какие знания необходимы для учёбы?


МАРСЕЛЬ: Мы рассчитывали курс на тех, кто уже знаком с Kubernetes. Знание систем контейнеризации, Docker в первую очередь, тоже горячо приветствуется. Без них будет тяжело, потому что в рамках курса мы не рассказываем о базовых абстракциях.


Предполагается, что у человека есть кластер, он с ним уже работал, но просто не знает, как правильно настроить мониторинг.


Нужно ли идти на курс, если что-то уже настраивал сам?


МАРСЕЛЬ: Думаю, да. У всех свой взгляд, как это сделать и настроить. Иногда я узнаю, как некоторые делают очень страшные вещи, строят костыли, где не надо, и прочее. Поэтому хочется, чтобы люди просвещались в этом вопросе. В рамках обмена опытом, скажем так.


Что ещё важно сказать про курс?


МАРСЕЛЬ: Первая версия прошла тестирование. Бета-тестеры дали очень много фидбека, после которого мы изменили программу. Например, в программе был Zabbix, а потом мы поспрашивали ребят, экспертную группу, и поняли, что он там не нужен, поэтому убрали его и добавили другие темы. Кроме того, мы значительно усилили команду спикеров, подключили ребят из ITSumma.


Бонусом студенты получают видеокурс о Prometheus. Он будет полезен даже для тех, кто с Prometheus никогда не работал.


Узнать больше о курсе и записаться

Подробнее..

Spring Boot приложение в Kubernetes с Postgresql и Loki

03.04.2021 22:15:26 | Автор: admin

Тема развертывания Spring Boot приложения в Kubernetes кластере уже не новая и на многих ресурсах,включая данный , написано немало уже статей с примерами. Сегодня,я бы хотел рассказать не только развертывании самого приложения ,но и о сопутствующих сервисах ,а именно : база данных, балансировщик нагрузки ,а также система сбора и аггрегирования логов.

Более подробно о всех комопонентах :

1) Spring Boot приложение, использующее в качестве БД PostgreSQL

2) Docker образ сервера базы данных

3) Docker Grafana( dashboard для отображеия логов)

4) Docker образ Loki(система сбора логов)

5) Promtail ( агент для отсылки логов в Loki).

Kubernetes cluster будет развернут при помощи microk8s. В качестве балансировщика нагрузки и по совместительству web-сервера будет выступать nginx, а точнее nginx-ingress-controller, который есть в microk8s.


Рассмотрим развертывания каждого компонента по отдельности.

Шаг 1: База данных

Для базы данных используем следующий yaml

apiVersion: v1kind: Servicemetadata:  name: dbspec:  ports:    - port: 5432  selector:    app: db  clusterIP: None---apiVersion: apps/v1kind: Deploymentmetadata:  name: dbspec:  selector:    matchLabels:      app: db  strategy:    type: Recreate  template:    metadata:      labels:        app: db    spec:      containers:        - image: postgres:9.6          name: db          env:            - name: POSTGRES_USER              value: admin            - name: POSTGRES_PASSWORD              value: admin            - name: POSTGRES_DB              value: dbname          ports:            - containerPort: 5432              name: db

В файле сразу описан и сервис,и развертывание базы. Как образ ,использован образ Postgres 9.6

Для создания развертывания исполним командуkubectl apply -f db.yaml

Шаг 2: Grafana

Для Grafana используем следующий yaml

apiVersion: v1kind: Servicemetadata:  name: grafanaspec:  ports:    - port: 3000  selector:    app: grafana  clusterIP: None---apiVersion: apps/v1kind: Deploymentmetadata:  name: grafanaspec:  selector:    matchLabels:      app: grafana  strategy:    type: Recreate  template:    metadata:      labels:        app: grafana    spec:      containers:        - image: grafana/grafana:master          name: grafana          ports:            - containerPort: 3000              name: grafana

Развертывание похоже на то,что использовано для базы данных. Разница в образе (grafana/grafana:master) и в выставляемом порте.

Аналогично выполним командуkubectl apply -f grafana.yaml

Шаг 3: Loki

Как и выше yaml

apiVersion: v1kind: Servicemetadata:  name: lokispec:  ports:    - port: 3100  selector:    app: loki  clusterIP: None---apiVersion: apps/v1kind: Deploymentmetadata:  name: lokispec:  selector:    matchLabels:      app: loki  strategy:    type: Recreate  template:    metadata:      labels:        app: loki    spec:      containers:        - image: grafana/loki:latest          name: loki          ports:            - containerPort: 3100              name: loki

И командаkubectl apply -f grafana.yaml

Шаг 4: Promtail

Для promtail понадобится Helm. Можно использовать helm, встроенный в microk8s(поддерживаются версии 2 и 3). Также можно установить Helm отдельно. В таком случае необходимо в файле config , располложенном в директории .kube, указать ip кластера. Можно выполнить microk8s config

Шаг 5: Ingress

Для nginx используем следующий файл.

apiVersion: networking.k8s.io/v1kind: Ingressmetadata:  name: serverspec:  rules:     #for nginxinc controller host should be set    - http:        paths:          - path: /            pathType: Prefix            backend:              service:                name: server                port:                  number: 8024                            - path: /grafana            pathType: Prefix            backend:              service:                name: grafana                port:                  number: 3000       

И команду kubectl apply -f ingress.yaml

Шаг 7: Приложение

Этот шаг не похож ни на один предыдущий. Здесь не будет использовано ни одного yaml и ни одного готовго Docker образа. Нужное нам развертывание будет создано сразу после процесса сборки. Для этого используется Maven + jkube maven plugin

Сначала install соберирает jar с приложением,затем k8s:resource генерирует ресурсы, потом k8s:build создаст Docker oбраз и k8s:deploy сделает развертывание.

Ниже пример конфигурации плагина для данного процесса

<profile>            <id>kube</id>            <properties>                <spring.profiles.active>docker</spring.profiles.active>            </properties>            <build>                <plugins>                    <plugin>                        <groupId>org.eclipse.jkube</groupId>                        <artifactId>kubernetes-maven-plugin</artifactId>                        <version>1.1.1</version>                        <configuration>                            <verbose>true</verbose>                                      <images>                                <image>                                    <name>imagename:latest</name>                                    <alias>some-alias/alias>                                    <build>                                        <maintainer>John Smith</maintainer>                                        <from>fabric8/java-centos-openjdk11-jre</from>                                        <assembly>                                            <inline>                                                <baseDirectory>/deployments</baseDirectory>                                            </inline>                                        </assembly>                                    </build>                                </image>                            </images>                        </configuration>                        <executions>                            <execution>                                <id>run</id>                                <goals>                                    <goal>resource</goal>                                    <goal>build</goal>                                    <goal>deploy</goal>                                </goals>                            </execution>                        </executions>                    </plugin>                </plugins>            </build>        </profile>

Образ описан под тегом image. Также можно использовать один из генераторов.

Также необходимо создать сервис. Для этого выполним

kubectl expose deployment server --type=LoadBalancer --name=server --port=<some-port>

Почему был использован данный способ создания сервиса,ведь сервис можно сконфигрурировать также в плагине ? На данный момент,была обнарудена ошибка в момент старта приложения в поде : вместо ip сервиса,приходит строка tcp://<ip-service> . Это приводит к NumberFormatException.

Шаг 8: Проверка доступа

В браузере или с помощью curl проверить,что localhost возвращает страницу приложения, localhost/grafana покажет странцицу входа в Grafana.

Шаг 9: Отобразить логи

Для этого необходимо войти в Grafana с помощью логина/пароля admin . После необходимо указать ,в качестве источника данных Loki(http://personeltest.ru/away/loki:3000). Затем в explore ввести {app="название-приложения"} .


PS.

Сбор логов был основан на данной статье

Подробнее..
Категории: Kubernetes , Java , Grafana , Loki , Spring-boot

Как настроить мониторинг любых бизнес-процессов, в БД Oracle построение графиков, используя бесплатную версию Grafana

10.01.2021 14:07:00 | Автор: admin

Вводные. Зачем мне это было нужно

Лично мне нужно было организовать мониторинг домашней солнечной электростанции.

Кратко о матчасти (хотя этот пост не про неё):

  • Инвертор МАП Энергия и 3 солнечных контроллера того же производителя.

  • Внутри инвертора установлен микрокомпьютер (производитель его называет "Малина"), который кое-что умеет в плане мониторинга, но не всё что мне нужно, и не очень удобно. Ценность микрокомпьютера в том, что он снимает данные с com-портов инвертора и контроллеров и публикует их насвоём http-сервере в виде Json. Данные веб-сервисов обновляются примерно каждую секунду. Также есть веб-сервисы для управления встроенными в контроллеры и инвертор реле

  • Парочка Ethernet-устройств SR-201 это такие платы с релюхами, используются для управления нагрузкой и кое-чем еще, управляются по протоколу tcp и udp.

  • Домашний сервер под управлением Centos-8, на нём установлен Oracle (разумеется Express Edition со всеми своими ограничениями, но для домашнего сервера достаточно)

  • В оракле крутятся 2 JOBa (на самом деле это persistent процессы, которые крутят бесконечный цикл и перезапускаются примерно раз в полчаса):

    1. Раз в секуну снимает данные с вебсервисов "Малины", текущее состояние реле устройств SR-201 и пишет это всё в БД Oracle. С Малины снимает с помощью несложных функций на основе utl_http, с реюх - через utl_tcp. Собственно это и есть статистика, которую будем мониторить

    2. Постоянно пересчитывает статистику за некоторый промежуток времени, и на основе полученных результатов, управляет нагрузкой и еще кое-чем через SR-201 и встроенные реле инвертора и контроллеров.

Вот это всё хозяйство мне нужно мониторить. Причем мониторить не события (событиями занимаетс Job2), а строить графики на основе накопленной статистической информации, визуализировать их на компе и мобилке. Сама "Малина" кое-что умеет, но во-первых не всё (про мои SR-201 она точно ничего не знает), во-вторых неудобный интерфейс - нельзя всё посмотреть на одном экране в удомном мне виде, а в третьих - в некоторых местах кривовато.

Вопросы: Почему Oracle а не Postgres например? Ну просто лень, хотелось сделать из того что умею... :-)

Выбор пал на Grafana https://grafana.com - довольно мощное средство визуализации статистики и прочей ерунды. Легко настраивается, удобно использовать. Работает с многими БД...

Собственно описание проекта

Данный пост ни разу не претендует на качественный пошаговый tutorial, я лишь хочу указать путь, по которому Вам будет легче идти.

Итак:

Устанавливаем grafana

$ sudo nano /etc/yum.repos.d/grafana.repo[grafana]name=grafanabaseurl=https://packages.grafana.com/oss/rpmrepo_gpgcheck=1enabled=1gpgcheck=1gpgkey=https://packages.grafana.com/gpg.keysslverify=1sslcacert=/etc/pki/tls/certs/ca-bundle.crt
dnf updatednf install grafanasystemctl daemon-reloadsystemctl enable --now grafana-serversystemctl status grafana-server

Selinux у меня отключен, файрвол тоже, так что в эти нюансы вдаваться не буду

Далее одна проблемка: Grafana конечно с Oracle работать умеет, но данная опция (плагин) предоставляется только в Enterprise версии, которая начинается от 24к$ и это в мои планы не входит. Устанавливаем плагин grafana-simple-json-datasource

grafana-cli plugins install grafana-simple-json-datasourcesystemctl restart grafana-server

То есть графана у нас в оракл ходить не будет. Она будет брать данные из вебсервиса, теперь дело за малым - вебсервис написать.

Вебсервис будем делать на apache + php

Для этого потребуется установить и настроить:

httpd, php и php-fpm (у меня php 7.2) установлен и сконфигрирован вместе с freepbx которая живёт на том же сервере :-)

Для php нужно подключить библиотеку oci8 - тут есть сложность в том, что для php 7.2 не получится поставить oci8 командой pecl.

В общем путь такой:

Подключаем репозиторий remi, и оттуда:

dnf install php-pecl-oci8

Подключаем oci8 к php

/etc/hp.d/20-oci8.ini

В принципе достаточно раскомментировать 1 строку

extension=oci8.so

Далее этот oci8 не очень хочет запускаться, тут помогут примерно такие строки в

/etc/php-fpm.d/www.conf

env[ORACLE_HOSTNAME] = myserver.localdomainenv[ORACLE_UNQNAME] = mydbenv[ORACLE_BASE] = /u01/app/oracleenv[ORACLE_HOME] = /u01/app/oracle/product/18.4.0/dbhome_1env[ORA_INVENTORY] = /u01/app/oraInventoryenv[ORACLE_SID] = mydbenv[LD_LIBRARY_PATH] = /u01/app/oracle/product/18.4.0/dbhome_1/lib:/lib:/usr/libenv[NLS_LANG] = AMERICAN_CIS.UTF8

Теперь при исполнении php-скрипта на вебсервере, oci8 прекрасно запускается

Выкладываем скрипт на вебсервер

/var/www/html/gr/gr.php

<?phpheader("Content-Type: application/json;");$conn = oci_pconnect('www', 'www$password', 'mydb', 'AL32UTF8');if (!$conn) {    $e = oci_error();    trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);}// Подготовка выражения$stid = oci_parse($conn, 'begin  LGRAFANA.GetJson(:vPath, :vInp, :vOut); end;');if (!$stid) {    $e = oci_error($conn);    trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);}// Создадим дескрипторы$vInp = oci_new_descriptor($conn, OCI_DTYPE_LOB);$vOut = oci_new_descriptor($conn, OCI_DTYPE_LOB);// Привяжем переменные$vPath = $_SERVER["PATH_INFO"];$postdata = file_get_contents("php://input");$vInp->writeTemporary($postdata, OCI_TEMP_BLOB);oci_bind_by_name($stid, ":vPath", $vPath);oci_bind_by_name($stid, ":vInp", $vInp, -1, OCI_B_BLOB);oci_bind_by_name($stid, ":vOut", $vOut, -1, OCI_B_BLOB);// Выполним логику запроса$r = oci_execute($stid);if (!$r) {    $e = oci_error($stid);    trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);}echo $vOut->load(); $vInp ->close();$vOut ->close();oci_free_statement($stid);oci_commit($conn);oci_close($conn);?>

Вебсервис готов.

В нашей БД есть пакет LGRAFANA, из которого наружу торчит только одна процедура

procedure GetJson(pPathInfo in varchar2, pInpPost in blob, pOutPost out blob);

она и будет обрабатывать Json - запросы от графаны. Эту процедуру целиком приведу чуть ниже, Вы понимаете что это лишь пример для моей электростанции, а в целом формат Json запросов-ответов описан тут

https://grafana.com/grafana/plugins/grafana-simple-json-datasource

Теперь настройка в самой графане:

Configuration - Data Sources - Add DataSource - Simple JSON

Дальше можно идти добавлять DashBoard и накидывать туда панели с нужными графиками

... Если у Вас уже есть реализация пакета LGRAFANA разумеется.

Да кстати про пакет. Он у меня написан не совсем на PL/SQL, но в целом Вы сможете это использовать для того чтобы понять, что надо написать в своём пакете. Это легко переводится на pl/sql.

Вкратце так:

  1. Реализуем метод, который реагирует на pahinfo=/search и отдаёт массив имён метрик которые мы умеем считать

  2. Реализуем метод /query который формирует массив данных по нужным метрикам

Полный текст пакета
pragma include([DEBUG_TRIGGER]::[MACRO_LIB]);CPALL const varchar2(30) := 'Мощность нагр.';CPNET const varchar2(30) := 'Мощность сеть';CPACB const varchar2(30) := 'Мощность АКБ';CPI2C const varchar2(30) := 'Мощность I2C';CPADD const varchar2(30) := 'Доп. Нагрузка';CPMP1 const varchar2(30) := 'Мощность MPPT1';CPMP2 const varchar2(30) := 'Мощность MPPT2';CPMP3 const varchar2(30) := 'Мощность MPPT3';CEDAY const varchar2(30) := 'Выработка за день';CEMP1 const varchar2(30) := 'Выработка MPPT1';CEMP2 const varchar2(30) := 'Выработка MPPT2';CEMP3 const varchar2(30) := 'Выработка MPPT3';CETOB const varchar2(30) := 'На заряд батареи';CEFRB const varchar2(30) := 'Взято от батареи';CEFRN const varchar2(30) := 'Взято от сети';CUNET const varchar2(30) := 'Напряжение сети';CUOUT const varchar2(30) := 'Напряжение выход';CUACB const varchar2(30) := 'Напряжение АКБ';public function TsToUTs(v_Ts in timestamp) return number isv_Dt date;beginv_Dt := v_ts;return trunc((v_Dt - to_date('01.01.1970','DD.MM.YYYY')) -- Кол-во дней с 1 янв 1970 * (24 * 60 * 60)) -- Теперь это кол-во секунд * 1000 -- Теперь миллисекунд + to_number(to_char(v_ts,'FF3')); -- Добавили миллисекундыend;procedure get_query(pInp in out nocopy JSON_OBJECT_T, pOut in out nocopy JSON_ARRAY_T) istype rtflag is record ( fTp varchar2(30),fOb json_object_t,fAr json_array_t);type ttflag is table of rtflag index by string;tflag ttflag;vTmpOb json_object_t;vTmpAr json_array_t;vTmpId varchar2(30);vDBeg timestamp;vDEnd timestamp;vDDBeg date;vDDEnd date;num_tz number;curts number;function GetFlag(pFlagName in varchar2) return boolean isbeginif tflag.exists(pFlagName) thenreturn true;elsereturn false;end if;end;--function GetFlagType(pFlagName in varchar2) return varchar2 is--begin--if tflag.exists(pFlagName) then--return tflag(pFlagName).fTp;--else--pragma error('Нет значения ['||pFlagName||'] в мвссиве tflag');--end if;--end;procedure AddTrgData(pTrgName in varchar2, pStamp in number, pValue in number) isbeginvTmpAr := Json_Array_t;vTmpAr.append(pValue);vTmpAr.append(pStamp);tFlag(pTrgName).fAr.append(vTmpAr);end;begin&debug('pInp='||pInp.to_string())vTmpOb := pInp.get_Object('range');num_tz := to_number(::[GA_MAP_STAT].[LIB].GetSetting('MALINA_TIME_ZONE'));vDBeg := vTmpOb.get_Timestamp('from') + numtodsinterval(num_tz,'hour');vDEnd := vTmpOb.get_Timestamp('to')   + numtodsinterval(num_tz,'hour');vDDBeg := to_date(to_char(vDBeg,'dd.mm.yyyy hh24:mi:ss'),'dd.mm.yyyy hh24:mi:ss');vDDEnd := to_date(to_char(vDEnd,'dd.mm.yyyy hh24:mi:ss'),'dd.mm.yyyy hh24:mi:ss');&debug('vDBeg='||to_char(vDBeg,'dd.mm.yyyy hh24:mi:ss:ff'))&debug('vDEnd='||to_char(vDEnd,'dd.mm.yyyy hh24:mi:ss:ff'))vTmpAr := pInp.get_Array('targets');for i in 0 .. vTmpAr.get_size - 1 loopvTmpOb := JSON_OBJECT_T(vTmpAr.get(i));vTmpId := vTmpOb.get_string('target');tflag(vTmpId).fTp := vTmpOb.get_string('type');tflag(vTmpId).fOb := Json_object_t;tflag(vTmpId).fAr := Json_array_t;tflag(vTmpId).fOb.put('target',vTmpId);end loop;-- Взять значения мощностей из статистики МАПif GetFlag(CPALL) or GetFlag(CPNET) or GetFlag(CPACB) or GetFlag(CPI2C) or GetFlag(CUNET) or GetFlag(CUOUT) or GetFlag(CUACB) thenfor (select x(x.[QTIME]:qtime, x.[F__PNET_CALC]:pnet -- Мощность сеть, - x.[F__PLOAD_CALC] + x.[F__PNET_CALC]:pall -- Мощность нагр., - x.[F__PLOAD_CALC]:pacb -- Мощность АКБ, x.[F__P_MPPT_AVG]:pi2c -- Мощность I2C, x.[F__UNET]:unet, x.[F__UOUTMED]:uout, x.[F__UACC]:uacb) in ::[GA_MAP_STAT] allwhere x.[QTIME] >= vDBeg and x.[QTIME] <= vDEndorder by x.[QTIME]) loopcurts := TsToUTs(x.qtime - numtodsinterval(num_tz,'hour'));vTmpId := tflag.first;while vTmpId is not null loopcase vTmpId of:CPALL: AddTrgData(vTmpId,curts,x.pall);:CPNET: AddTrgData(vTmpId,curts,x.pnet);:CPACB: AddTrgData(vTmpId,curts,x.pacb);:CPI2C: AddTrgData(vTmpId,curts,x.pi2c);:CUNET: AddTrgData(vTmpId,curts,x.unet);:CUOUT: AddTrgData(vTmpId,curts,x.uout);:CUACB: AddTrgData(vTmpId,curts,x.uacb);end;vTmpId := tflag.next(vTmpId);end loop;end loop;end if;-- Взять статистику панелейif GetFlag(CPMP1) or GetFlag(CPMP2) or GetFlag(CPMP3) thenfor (select x(x.[QTIME]:qtime,x.[F_UID]:fuid,x.[F_P_CURR]:fpower -- Мощность заряда) in ::[GA_MPPT_STAT] allwhere x.[QTIME] >= vDBeg and x.[QTIME] <= vDEndorder by x.[QTIME], x.[F_UID]) loopcurts := TsToUTs(x.qtime - numtodsinterval(num_tz,'hour'));case x.fuid of:1: if GetFlag(CPMP1) then AddTrgData(CPMP1,curts,x.fpower); end if;:2: if GetFlag(CPMP2) then AddTrgData(CPMP2,curts,x.fpower); end if;:3: if GetFlag(CPMP3) then AddTrgData(CPMP3,curts,x.fpower); end if;end;end loop;end if;-- Взять значения мощностей из статистики допнагрузкиif GetFlag(CPADD) thendeclaretqend timestamp;paend number;beginfor (select x(  x.[QTIME]:qtime, x.[FPOWER]:padd -- Доп. Нагрузка) in ::[GA_LOAD_H] allwhere x.[QTIME] >= (select x(nvl(max(x.[QTIME]),to_timestamp('01.01.1970','dd.mm.yyyy')))in ::[GA_LOAD_H] allwhere x.[QTIME] < vDBeg)and x.[QTIME] < vDEndorder by x.[QTIME]) loopcurts := TsToUTs(x.qtime - numtodsinterval(num_tz,'hour'));tqend := x.qtime;paend := x.padd;AddTrgData(CPADD,curts,x.padd);end loop;curts := TsToUTs(vDEnd - numtodsinterval(num_tz,'hour'));AddTrgData(CPADD,curts,paend);end;end if;-- Взять значения выработки по датамif GetFlag(CEDAY) or GetFlag(CEMP1) or GetFlag(CEMP2) or GetFlag(CEMP3) or GetFlag(CEFRN) thendeclarevDEBeg date;vDEEnd date;vDECur date;curEn number;prven number;vTSCur timestamp;curEnToBat number;curEnFromBat number;procedure GetCeMp(vCeMp in varchar2, vMpUID in number) isbeginvDECur := vDEBeg;while vDECur <= vDEEnd loopvTSCur := to_timestamp(to_char(vDECur,'dd.mm.yyyy'),'dd.mm.yyyy');select x(nvl(max(x.[F_PWR_KW]),0)*1000) in ::[GA_MPPT_STAT] allwhere x.[qtime] >= vTSCurand x.[qtime] < (vTSCur + numtodsinterval(1,'day'))and x.[F_TIMESTAMP] >= vDECurand x.[F_TIMESTAMP] < (vDECur+1)and x.[F_UID] = vMpUIDinto curEn;curts := TsToUTs(vTsCur - numtodsinterval(num_tz,'hour'));AddTrgData(vCeMp,curts,curen);vDECur := vDECur + 1;end loop;end;beginvDEBeg := trunc(vDDBeg);vDEEnd := trunc(vDDEnd);if GetFlag(CEDAY) or GetFlag(CETOB) or GetFlag(CEFRB) thenvDECur := vDEBeg;while vDECur <= vDEEnd loopvTSCur := to_timestamp(to_char(vDECur,'dd.mm.yyyy'),'dd.mm.yyyy');select x( nvl(max(x.[S1].[F_MPPT_DAY_E]),0),nvl(max(x.[S1].[F_ESUM_TO_BAT]),0),nvl(max(x.[S1].[F_ESUM_FROM_BAT]),0)) in ::[GA_BAT_STAT] allwhere x.[qtime] >= vTSCurand x.[qtime] < (vTSCur + numtodsinterval(1,'day'))and x.[S1].[F_TIMESTAMP] >= vDECurand x.[S1].[F_TIMESTAMP] < (vDECur+1)into curEn,curEnToBat,curEnFromBat;curts := TsToUTs(vTsCur - numtodsinterval(num_tz,'hour'));if GetFlag(CEDAY) then AddTrgData(CEDAY,curts,curen); end if;if GetFlag(CETOB) then AddTrgData(CETOB,curts,curenToBat); end if;if GetFlag(CEFRB) then AddTrgData(CEFRB,curts,curenFromBat); end if;vDECur := vDECur + 1;end loop;end if;if GetFlag(CEMP1) thenGetCeMp(CEMP1,1);end if;if GetFlag(CEMP2) thenGetCeMp(CEMP2,2);end if;if GetFlag(CEMP3) thenGetCeMp(CEMP3,3);end if;-- Посчитать сколько взято от сетиif GetFlag(CEFRN) thenvDECur := vDEBeg-1;prven := null;while vDECur <= vDEEnd loopvTSCur := to_timestamp(to_char(vDECur,'dd.mm.yyyy'),'dd.mm.yyyy');curen := 0;for (select x(x.[F__E_NET_B]*10:enet)in ::[GA_MAP_STAT] allwhere x.[qtime] >= vTSCurand x.[qtime] < (vTSCur + numtodsinterval(1,'day'))and x.[F_TIMESTAMP] >= vDECurand x.[F_TIMESTAMP] < (vDECur+1)order by x.[qtime] desc) loopcuren := x.enet;exit;end loop;if curen = 0 and prven != 0 thencuren := prven;end if;if prven is null thenprven := curen;elseif prven = 0 thenprven := curen;end if;curts := TsToUTs(vTsCur - numtodsinterval(num_tz,'hour'));&debug('1. dcur = '||to_char(vDECur,'dd.mm.yyyy')||' prven = '||prven||' curen ='||curen||' diff='||to_char(curen - prven))AddTrgData(CEFRN,curts,curen - prven);prven := curen;end if;vDECur := vDECur + 1;end loop;end if;end;end if;-- Выгрузить собранные массивы  ответvTmpId := tflag.first;while vTmpId is not null looptflag(vTmpId).fOb.put('datapoints',tflag(vTmpId).fAr);tflag(vTmpId).fAr := null;pOut.append(tflag(vTmpId).fOb);tflag(vTmpId).fOb := null;vTmpId := tflag.next(vTmpId);end loop;end;procedure get_search(pInp in out nocopy JSON_OBJECT_T, pOut in out nocopy JSON_ARRAY_T) isvTarget varchar2(100);begin&debug('pInp='||pInp.to_string())vTarget := trim(pInp.get_String('target'));if vTarget is null thenpOut.Append(CPALL);pOut.Append(CPNET);pOut.Append(CPACB);pOut.Append(CPI2C);pOut.Append(CPADD);pOut.Append(CPMP1);pOut.Append(CPMP2);pOut.Append(CPMP3);pOut.Append(CEDAY);pOut.Append(CEMP1);pOut.Append(CEMP2);pOut.Append(CEMP3);pOut.Append(CETOB);pOut.Append(CEFRB);pOut.Append(CEFRN);pOut.Append(CUNET);pOut.Append(CUOUT);pOut.Append(CUACB);end if;end;public procedure GetJson(pPathInfo in varchar2, pInpPost in blob, pOutPost out blob) isvInp JSON_OBJECT_T;vOut JSON_ARRAY_T;beginvInp := JSON_OBJECT_T(pInpPost);vOut := JSON_ARRAY_T();&debug('pPathInfo='||pPathInfo)-- Маршрутизация запроса в зависимости от pPathInfoif pPathInfo = '/search' thenget_search(vInp, vOut);elsif pPathInfo = '/query' thenget_query(vInp, vOut);end if;pOutPost := vOut.to_Blob;end;

Возможно это кому-то окажется полезным :-)

Вот такие результаты:

Подробнее..

Перевод Мониторинг и профилирование Spring Boot приложения

02.01.2021 00:07:07 | Автор: admin

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

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

Какие показатели нужно отслеживать?

  • Доступность услуги:время, в течение которого услуга доступна для использования.Это может быть измерено с точки зрения времени отклика, например, процентиль X, сокращенно обозначаемый как pX, например, p95, p99, p99.999.Не для всех сервисов требуется p99,999, системы с гарантированной высокой доступностью, такие как электронная коммерция, поиск, оплата и т. д., должны иметь более высокое SLA.

  • Уровень дефектов:несмотря на все усилия по разработке системы, ни одна система не является идеальной на 100%.Мы должны подсчитывать процент ошибок в основных потоках.В эту категорию могут быть включены производственные сбои, такие как ошибка сервера, ошибка запроса базы данных, ошибка соединения, сетевые ошибки и пропущенные сроки.

  • Безопасность:в наши сверх регулируемые времена нарушения безопасности приложений и сети могут быть дорогостоящими.В эту категорию можно включить измерение контролируемых мер безопасности, таких как доступ к системе, несанкционированный доступ к базе данных, массовая загрузка записей базы данных или перемещение больших объемов данных.

  • Бизнес-результаты: ИТ-клиенты все чаще хотят включать метрики бизнес-процессов в свои SLA, чтобы можно было принимать более правильные бизнес-решения.В этой категории могут быть собраны различные данные, такие как количество пользователей, посетивших страницу, активность входа, эффективность купона и т. д.

Вначале выбор набора показателей может быть нетривиальной задачей, также мы можем столкнуться с дилеммой того, сколько показателей нам следует отслеживать.Мы можем начать с самого минимума и добавить столько, сколько понадобится позже.

Настройка мониторинга

Типичная система настройки мониторинга будет состоять из трех компонентов

  1. Хранилища метрик (какправило,база данных временных рядов), такое как InfluxDB, TimescaleDB, Prometheusи т. д.

  2. Панель инструментов (панель будет использоваться для визуализации данных, хранящихся в хранилище метрик).

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

У нас могут быть и другие компоненты, например, оповещение, где каналами оповещения могут быть электронная почта, Slack или любые другие.Компонент оповещения будет отправлять оповещения владельцам приложений или подписчикам событий.Мы собираемся использоватьGrafanaкак панель управления и систему оповещений,Prometheusкак систему хранения метрик.

Нам понадобятся:

  1. Любая IDE

  2. Платформа Java

  3. Gradle

Создайте проект с помощью инициализатора Spring Boot, добавьте столько зависимостей, сколько нам нужно.Мы собираемся использовать библиотеку Micrometer, это инструментальный фасад, который обеспечивает привязки для многих хранилищ метрик, таких как Prometheus, Datadog и New Relic, и это лишь некоторые из них.

Из коробки Micrometer обеспечивает

  1. HTTP-запрос

  2. JVM

  3. База данных

  4. Метрики, относящиеся к системе кэширования и т. д.

Некоторые метрики включены по умолчанию, тогда как другие можно включить, отключить или настроить.Мы будем использовать файл application.properties для включения, отключения и настройки метрик. Нам также нужно использовать Spring boot actuator, так как он откроет доступ к конечной точкеPrometheus.

Добавьте эти зависимости в файл build.gradle:

  1. io.micrometer: micrometer-registry-prometheus

  2. org.springframework.boot: spring-boot-starter-actuator

dependencies {  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'  implementation 'org.springframework.boot:spring-boot-starter-web'  compileOnly 'org.projectlombok:lombok'  annotationProcessor 'org.projectlombok:lombok'  providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'  implementation 'io.micrometer:micrometer-registry-prometheus'  implementation 'org.springframework.boot:spring-boot-starter-actuator'  // https://mvnrepository.com/artifact/com.h2database/h2  compile group: 'com.h2database', name: 'h2', version: '1.4.200'  testImplementation('org.springframework.boot:spring-boot-starter-test') {  exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'  }}

Мы можем включить экспорт Prometheus, добавив следующую строку в файл свойств.

management.metrics.export.prometheus.enabled = true

После добавления этой строки Micrometer начнет накапливать данные о приложении, и эти данные можно будет просмотреть, перейдя на конечную точку actuator/Prometheus.Эта конечная точка будет использоваться в скрипте Prometheus для получения данных с наших серверов приложений.

Несмотря на то, что мы добавили эту строку в свойствах, мы не можем просматривать конечную точку Prometheus, поскольку она отключена по умолчанию, мы можем открыть ее, используя конечную точку управления, включивPrometheusв список.

management.endpoints.web.exposure.include=prometheus

ПРИМЕЧАНИЕ. Не включайте все конечные точки actuator, так как это может открыть лазейку в системе безопасности.Мы должны выбирать их выборочно, особенно в производственной системе, даже если мы хотим, не раскрывать конечную точку для всего мира, так как она может раскрыть большой объем данных о приложении, использовать какой-то прокси или какое-то правило, чтобы скрыть данные от внешнего мира.

Различные частиHTTP-запросовнастраиваются, например SLA, настройка признака должна быть вычислена или нет гистограмма процентилей делается спомощьюсвойствmetrics.distribution.

В примере application.properties могут быть такие строки

# Включить экспорт prometheusmanagement.metrics.export.prometheus.enabled = true# Включить конечную точку Prometheusmanagement.endpoints.web.exposure.include = Прометей# включить гистограмму на основе процентилей для http запросовmanagement.metrics.distribution.percentiles-histogram.http.server.requests = true# сегментов гистограммы http SLAmanagement.metrics.distribution.sla.http.server.requests = 100 мс, 150 мс, 250 мс, 500 мс, 1 с# включить метрики JVMmanagement.metrics.enable.jvm = true

Теперь, если мы запустим приложение и перейдем на страницу http://locahost:8080/actator/prometheus, будет отображаться чертовски много данных.

Приведенные выше данные отображают детали HTTP-запроса, exception=None означает, что исключение не произошло, если оно есть, мы можем использовать это для фильтрации количества запросов, которые не удалось выполнить из-за этого исключения,method=GETимя метода HTTP.status=200HTTP статус равен 200,uri=/actator/prometheusотображает путь URL,le=xyzотображает время обработки,N.0отображает количествовызововэтой конечной точки.

Эти данные представляют собой гистограмму, которую можно построить в Grafana, например, чтобы построить график p95 за 5 минут, мы можем использовать следующий запрос.

histogram_quantile(0.95,sum(rate(http_server_requests_seconds_bucke[5m])) by (le))

В Grafana можно построить и другие графики показателей, например круговую диаграмму и т. д.

Пользовательские показатели

Часто нам нужны специальные метрики, некоторые из сценариев использования это количество вошедших в систему пользователей, сведения о запасах доступных, в настоящее время, количество заказов в очереди и т. д. Некоторые из бизнес сценариев использования могут быть решены с использованием пользовательских метрик, микрометра. поддерживает различные типы показателей, такие как таймер, датчик, счетчик, сводки распределения, таймеры для длительных задач и т. д. В основном мы сосредоточимся на датчике и счетчике. Датчик дает нам мгновенные данные, такие как длина очереди, тогда как счетчик похож на монотонно увеличивающееся число, начинающееся с единицы.

Для демонстрации этого мы собираемся создать пример менеджера запасов, который будет хранить детали в памяти и обеспечит две функции:

  1. Добавить товары

  2. Получить товары

Для этого мы создадим один счетчик и одну метрику вметодеinit, при каждомвызове getItems мы увеличиваем счетчик, а также измеряем размер запаса, тогда как при вызове addItems мы только обновляем метрику.

@Componentpublic class StockManager {  @Autowired private MeterRegistry meterRegistry;  private List<String> orders = new Vector<>();  private Counter counter;  private Gauge gauge;@PostConstruct  public void init() {    counter =        Counter.builder("order_created")            .description("number of orders created")            .register(meterRegistry);    gauge =        Gauge.builder("stock.size", this, StockManager::getNumberOfItems)            .description("Number of items in stocks")            .register(meterRegistry);  }public int getNumberOfItems() {    return orders.size();  }public void addItems(List<String> items) {    orders.addAll(items);    // measure gauge    gauge.measure();  }public List<String> getItem(int count) {    List<String> items = new ArrayList<>(count);    while (count < 0) {      try {        items.add(orders.remove(0));      } catch (ArrayIndexOutOfBoundsException e) {        break;      }      count -= 1;    }    // increase counter    counter.increment();    //  measure gauge    gauge.measure();    return items;  }}

В демонстрационных целях мы добавим две конечные точки для добавления товаров и получения товаров.

@RestController@RequestMapping(path = "stocks")@RequiredArgsConstructor(onConstructor = @__(@Autowired))public class StockController {  @NonNull private StockManager stockManager;  @GetMapping  @ResponseBody  public List<String> getItems(@RequestParam int size) {    return stockManager.getItem(size);  }  @PostMapping  @ResponseBody  public int addItems(@RequestParam List<String> items) {    stockManager.addItems(items);    return stockManager.getNumberOfItems();  }}  @PostMapping  @ResponseBody  public int addItems(@RequestParam List<String> items) {    stockManager.addItems(items);    return stockManager.getNumberOfItems();  }}

Давайте сначала добавим десять товаров, используя два вызова API:

  1. Curl -X POST http://localhost:8080/stocks?Items = 1,2,3,4

  2. Curl -X POST http://localhost:8080/stocks?Items = 5,6,7,8,9,10

Теперь, если мы перейдем к конечным точкам Prometheus, то увидим следующие данные, которые показывают, что в настоящее время у нас есть 10 товаров на складе.

# HELP stock_size Количество товаров на складе# TYPE stock_size gaugestock_size 10.0

Теперь мы собираемся разместить заказ на 3 товара:

http://localhost:8080/stocks?size=3

Теперь, если мы посмотрим конечную точку Prometheus, мы получим следующие данные, которые показывают, что размер запаса был изменен до семи.

# HELP stock_size Количество товаров на складе# TYPE stock_size gaugestock_size 7.0

Кроме того, мы видим, что на счетчике добавлено значение 1, это означает, что размещен один ордер.

# HELP order_created_total количество созданных заказов# TYPE order_created_total counterorder_created_total 1.0ordercreated_total 1.0

Профилирование

В программной инженерии профилирование (профилирование программы, профилирование программного обеспечения) - это форма динамического анализа программы, который измеряет, например, объем (память) или временную сложность программы, использование определенных инструкций или частоту и продолжительность вызовов функций.

Чаще всего профилирующая информация помогает оптимизировать программу. Профилирование достигается путем сбора характеристик работы программы, таких как время выполнения отдельных фрагментов (обычно подпрограмм), число верно предсказанных условных переходов, число кэш-промахов и т. д. Инструмент, используемый для анализа работы, называют профилировщиком или профайлером (profiler). Профилировщики могут использовать ряд различных методов, таких как методы, основанные на событиях, статистические, инструментальные методы и методы моделирования. Википедия

Профилирование очень полезно при диагностике системных проблем, например, сколько времени занимает HTTP-вызов, если он занимает N секунд, а затем, где все это время было потрачено, каково распределение N секунд между различными запросами к базе данных, вызовами последующих служб и т. д. Мы можем использовать гистограмму для построения графика распределения на панели инструментов, также мы можем использовать счетчик для измерения количества запросов к БД и т. д. Для профилирования нам необходимо внедрить код во многие функции, которые будут выполняться как часть выполнения метода.

Важно то, что внедряемая часть кода профилирования одинакова для профилировщиков аналогичных типов, что означает, что нам нужно скопировать и вставить аналогичный код в тысячи мест, если нам нужно что-то изменить, нам нужно обновить то же самое. Код профилировщика в каждом файле и, вероятно, в каждой функции, требующей профилирования, повысит сложность и может стать создать полный беспорядок, впрочем мы можем избежать этого беспорядка, используяаспектно-ориентированное программирование (АОП).

Короче говоря, АОП работает пошаблону проектирования прокси, хотя его можно реализовать и с помощью модификации байтового кода.

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

Система зависит от других систем, поэтому мы можем быть заинтересованы в профилировании различных компонентов по-разному, например, вызовов базы данных, HTTP-запросов, последующих вызовов служб или некоторых конкретных методов, которые являются критическими или хотели бы увидеть, что происходит в некоторых конкретных методах.Мы можем использовать ту же библиотеку Micrometer для профилирования, но это может быть не совсем то, что нам нужно, поэтому мы изменим код.

Micrometer поставляется саннотациейTimed, эту аннотацию можно разместить в любомpublic методе, таккак название предполагает, что она может измерять время выполнения, это будет измерять время выполнения соответствующего метода.Вместо того, чтобы напрямую использовать эту аннотацию, мы расширим эту аннотацию для поддержки других функций, таких как ведение журнала, повторная попытка и т. Д.

Аннотация Timed бесполезна без бина TimedAspect, поскольку мы переопределяем аннотациюTimed, поэтому мы также определим класс TimedAspect в соответствии с потребностями, такими как ведение журнала, массовое профилирование (профилируйте все методы в пакете без добавления каких-либо аннотаций к любому методу или классу), повторить попытку и т. д. В этой истории мы рассмотрим три сценария использования:

  1. Массовое профилирование.

  2. Логирование.

  3. Специфический для профиля метод.

Созадим файл java MonitoringTimed.java, в который мы добавим новое поле с именемloggingEnabled,это поле будет использоваться для проверки, включено ли ведение журнала или нет, если оно включено, а затем регистрировать аргументы метода и возвращаемые значения.

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface MonitoringTimed {  /** All fields are same as in {@link io.micrometer.core.annotation.Timed} */  String value() default "";  String[] extraTags() default {};  boolean longTask() default false;  double[] percentiles() default {};  boolean histogram() default false;  String description() default "";  // NEW fields starts here  boolean loggingEnabled() default false;}

Эта аннотация бесполезна без класса аспекта Timed, поэтомубудет определенновый классMonitoringTimedAspectсо всеми необходимыми деталями, этот класс будет иметь метод для профилирования любого метода на основе объединенного объекта обработки иобъектаMonitoringTimed,а другой - для профилирования метода на основе в аннотации MonitoringTimed.

@Around("execution (@com.gitbub.sonus21.monitoring.aop.MonitoringTimed * *.*(..))")public Object timedMethod(ProceedingJoinPoint pjp) throws Throwable {  Method method = ((MethodSignature) pjp.getSignature()).getMethod();  MonitoringTimed timed = method.getAnnotation(MonitoringTimed.class);  if (timed == null) {    method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());    timed = method.getAnnotation(MonitoringTimed.class);  }  final String metricName = generateMetricName(pjp, timed);  return timeThisMethod(pjp, timed, metricName);}public Object timeThisMethod(ProceedingJoinPoint pjp, MonitoringTimed timed) throws Throwable {  final String metricName = generateMetricName(pjp, timed);  return timeThisMethod(pjp, timed, metricName);}

МетодTimedMethodсаннотациейAroundиспользуется для фильтрации всех вызовов методов, аннотированных с помощьюMonitoringTimed.

Для массового профилирования мы определим класс профилировщика, который будет работать с фильтрацией на уровне пакета. Например, для профилирования HTTP-запроса мы можем добавить ControllerProfiler, который будет обрабатывать профилирование всех общедоступных методов, доступных в пакете контроллера.

@Aspect@Componentpublic class ControllerProfiler {  private static Map<String, Object> timedAnnotationData = new HashMap<>();  static {    // use percentile data of p90, p95, p99.99    double[] percentiles = {0.90, 0.95, 0.9999};    // set histogram to true    timedAnnotationData.put("histogram", true);    // set percentile    timedAnnotationData.put("percentiles", percentiles);  }  @Autowired private MonitoringTimedAspect timedAspect;  private static final MonitoringTimed timed = Javanna.createAnnotation(MonitoringTimed.class, timedAnnotationData);  private static final Logger logger = LoggerFactory.getLogger(ControllerProfiler.class);  @Pointcut("execution(* com.gitbub.sonus21.monitoring.controller..*.*(..))")  public void controller() {}  @Around("controller()")  public Object profile(ProceedingJoinPoint pjp) throws Throwable {    // here add other logic like error happen then log parameters etc    return timedAspect.timeThisMethod(pjp, timed);  }}

Интересной строкой в приведенном выше коде является @Pointcut(execution(com.gitbub.sonus21.monitoring.controller...*(..))), которая определяет pointcut, выражение pointcut может быть определено с использованием логических операторов вродеnot (!), or (||), and (&&). После того, как метод квалифицирован согласно выражению pointcut, он может вызвать соответствующий метод, определенный с помощью аннотации [at]Around.Поскольку мы определилиметодprofile,который будет вызываться, мы также можем определить другие методы, используя аннотации [at]After, [at]Before и т. д.

После добавления нескольких элементов с помощью метода POST мы можем увидеть следующие данные в конечной точке Prometheus.

method_timed_seconds {class = "com.gitbub.sonus21.monitoring.controller.StockController", exception = "none", method = "addItems", quantile = "0.9",} 0.0method_timed_seconds_bucket {class = "com.gitbub.sonus21.monitoring.controller.StockController", exception = "none", method = "addItems", le = "0.001",} 3.0method_timed_seconds_bucket {class = "com.gitbub.sonus21.monitoring.controller.StockController", exception = "none", method = "addItems", le = "0.002446676",} 3.0

Мы можем напрямую использоватьаннотациюMonitoringTimedтакже для любого метода для измерения времени выполнения, например, давайтеизмерим,сколько времениStockManagerметод addItemsтратит на добавление элементов.

@MonitoringTimedpublic void addItems(List<String> items) {  orders.addAll(items);  // measure gauge  gauge.measure();}

Как только мы запустим приложение и добавим несколько элементов, мы увидим следующее в конечной точке Prometheus.

method_timed_seconds_count{class="com.gitbub.sonus21.monitoring.service.StockManager",exception="none",method="addItems",} 4.0method_timed_seconds_sum{class="com.gitbub.sonus21.monitoring.service.StockManager",exception="none",method="addItems",} 0.005457965method_timed_seconds_max{class="com.gitbub.sonus21.monitoring.service.StockManager",exception="none",method="addItems",} 0.00615316

MonitoringTimed можно дополнительно настроить, например, можно добавить количество повторных попыток для поддержки повторных попыток в случае сбоя, записать аргументы функции в случае сбоя, чтобы позже можно было проанализировать причину сбоя.

Полный код доступен наGitHub.

Дополнительное чтение

Magic With the Spring Boot Actuator

Spring Boot Actuator in Spring Boot 2.0

Spring Boot Admin Client Configuration Using Basic HTTP Authentication

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru