Grafana: создание dashboard

Задача – добавить дашборд для отображения различной статистики с бекенда.




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




Для примеров запросов использовалась в основном борда Node Exporter Full 0.16 для метрик EC2, и борда AWS ELB Application Load Balancer для ALB.




Добавлять будем:




  • вверху – статусы хоста:

    • часы (время с момента получения последней метрики)



    • CPU usage %



    • Load Average %



    • memory usage %



    • root, /data disks % usage




  • статусы сервисов:

    • nginx



    • php-fpm



    • rabbitmq status



    • memcached status



    • redis status



    • RDS статус




  • статистика EC2 (node_exporter):

    • CPU

      • CPU System/User/IOwait/Idle




    • Memory:

      • RAM total



      • RAM used



      • RAM free




    • Disk

      • IO time




    • Processes

      • Blocked I/O



      • Running




  • статистика Load Balancer

    • RequestCount / Latency

      • TargetResponseTime_Average



      • ActiveConnectionCount_Average




    • HTTPCode

      • HTTPCode_Target_2XX_Count_Average



      • HTTPCode_Target_3XX_Count_Average



      • HTTPCode_Target_4XX_Count_Average



      • HTTPCode_Target_5XX_Count_Average




Настройки dashboard




Переходим в Settings, задаём имя дашборды:





Переменные




Документация – тут>>> и тут>>>.




У нас есть два бекеда – Dev и Production.




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




Переходим в Variables:





  • в Name задаём имя, которое будет использоваться в запросах



  • Type – оставляем Query



  • Label – имя, как оно будет отображаться в дашборде для выбора



  • Data source – Prometheus



  • Refresh – при загрузке дашборда



  • Query – собственно, сам запрос, который вернёт нам значения, и из которого будем получать список окружений
    в данном случае Prometheus-сервер, который запущен на EC2, добавляет external_label в виде env=mobilebackend-dev, его и используем
    для получения значений – используем метрику node_boot_time_seconds, фильтруем вывод по метке job="node-exporter"
    запрос получается:
    label_values(node_boot_time_seconds{job="node-exporter"}, env)





Сохраняем – Add внизу.




Статусы хоста




Первым добавим блок, в котором убдут выводить % использования CPU, Load Avareage, память и диски.




CPU Busy




node_cpu_seconds_total




Прежде, чем заниматься настройкой отображения CPU Busy – давайте вспомним /proc/stats.




Сначала – получим время с момента запуска системы:




root@bm-backed-app-dev:/opt/prometheus-client# cat /proc/uptime

2168671.61 2147672.65




Тут:




  • первая колонка – аптйам системы в секундах



  • вторая – время в сукундах, проведённое в IDLE




Проверяем:




root@bm-backed-app-dev:/opt/prometheus-client# uptime

14:21:45 up 25 days,  2:37




Считаем – кол-во секунд из /proc/uptime делим на часы и кол-во часов в сутках:




root@bm-backed-app-dev:/opt/prometheus-client# echo 2168671 / 3600 / 24 | bc

25




Хорошо, тут всё сходится.




Теперь считаем stats:




root@bm-backed-app-dev:/opt/prometheus-client# cat /proc/stat

...

cpu0 1435156 1188 466232 213091141 58015 0 29879 134098 0 0

...

btime 1529657067




Тут колонки:




  • user: normal processes executing in user mode



  • nice: niced processes executing in user mode



  • system: processes executing in kernel mode



  • idle: twiddling thumbs



  • iowait: waiting for I/O to complete



  • irq: servicing interrupts



  • softirq: servicing softirqs




Время в сотых секунды.




btime – время загрузки системы в секундах с момента January 1, 1970 (UNIX epoch).




Теперь попробуем посчитать:




root@bm-backed-app-dev:/opt/prometheus-client# echo "(1435156+1188+466232+213091141+58015+0+29879+134098) / 100 / 3600 / 24" | bc

24




Выполняем сложение всех счётчиков, делим на 100 – получаем общее кол-во секунд.




Потом, аналогично вычислениям с uptime – получаем кол-во дней, вышло 24 – один день (точнее 4 часа) “потерялся”, но не критично – в целом значения сошлись.




Теперь выведем метрики node_exporter, и сравним их с данными из /proc/stat (на самом деле метрики я вывел немного раньше, поэтому будет разница):




root@bm-backed-app-dev:/opt/prometheus-client# curl -s localhost:9100/metrics | grep node_cpu_seconds_total

HELP node_cpu_seconds_total Seconds the cpus spent in each mode.

TYPE node_cpu_seconds_total counter

node_cpu_seconds_total{cpu="0",mode="idle"} 2.13035164e+06

node_cpu_seconds_total{cpu="0",mode="iowait"} 579.98

node_cpu_seconds_total{cpu="0",mode="irq"} 0

node_cpu_seconds_total{cpu="0",mode="nice"} 11.88

node_cpu_seconds_total{cpu="0",mode="softirq"} 298.71

node_cpu_seconds_total{cpu="0",mode="steal"} 1340.68

node_cpu_seconds_total{cpu="0",mode="system"} 4660.78

node_cpu_seconds_total{cpu="0",mode="user"} 14346.37




И сравниваем с stat:




  • user: stat = 14351.56, exporter = 14346.37



  • system: stat = 4662.32, exporter = 4660.78




Окей – тут тоже всё более-менее сходится, и значение данных из node_cpu_seconds_total понятно.




Запрос




Теперь рассмотрим запрос, который будем использовать для получения CPU Busy %:




(((count(count(node_cpu_seconds_total{env="$environment"}) by (cpu))) - avg(sum by (mode)(irate(node_cpu_seconds_total{mode='idle',env="$environment"}[5m])))) * 100) / count(count(node_cpu_seconds_total{env="$environment"}) by (cpu))




Тут:




  • count – считаем кол-во элементов, полученных из запроса



  • avg: общее среднее значение, документация тут>>>



  • irate –  считает значение в секунду, основываясь на двух последних данных



  • node_cpu_seconds_total – секунды в каждом режиме (systemuseridle etc)



  • {env=~"$environment"} – выборка по значению переменной $environment




Подсчёт кол-ва ядер




Кол-во ядер мы получаем запросом ((count(count(node_cpu_seconds_total{env="$environment"}) by (cpu))).




Рассмотрим его детальнее.




Сначала сделаем выборку по node_cpu_seconds_total{env=~"mobilebackend-dev"}:





Так мы получим значения node_cpu_seconds_total по каждому типу – iowaitusersystemnice etc.




В count(node_cpu_seconds_total{env=~"mobilebackend-dev"}) считаем общее кол-во элементов (iowait, user, system, nice etc), хотя оно нам не надо – мы просто используем этот массив для следующего запроса.




А следующий запрос – count(node_cpu_seconds_total{env=~"mobilebackend-dev"}) by (cpu) возвращает нам общее кол-во node_cpu_seconds_total по типам для каждого ядра:





Например для Production это будет выглядеть так:





И в конце-концов добавив ещё один счётчик – count, и превратив запрос в count(count(node_cpu_seconds_total{env=~"mobilebackend-dev"}) by (cpu)) – мы получим кол-во ядер:





Production





Для наглядности – проверяем на серверах:




root@bm-backed-app-dev:/opt/prometheus-client# grep -c ^processor /proc/cpuinfo

1




И прод:




root@mobilebackend-production:/data/projects# grep -c ^processor /proc/cpuinfo

8




Время ядер в idle




По теме – Understanding Machine CPU usage.




Следующая часть запроса – avg(sum by (mode) (irate(node_cpu_seconds_total{mode='idle',env="$environment"}[5m]))).




Сначала выполняем irate(node_cpu_seconds_total{mode='idle',env="mobilebackend-dev"}[5m]):





Так мы получаем среднее значение времени в секундах, которое cpu0 провёл в статусе idle, т.е. бездельничал.




А “завернув” этот запрос в avg(sum by (mode)() – получим среднее значение для всех ядер:





И если для Dev разницы нет, то на Prod с его 8-ю ядрами значение будет более наглядным:





Т.е. вместе все 8 ядер провели 7.95 секунд в режиме idle.




Если принять 8 за 100%, то вычислим %, который ядра провели не в idle за 1 секунду:




>>> (8.0 - 7.95) * 100 / 8.0

0.6249999999999978




0.6% времени проведено в других режимах – systemuser etc.




Если бы, к примеру, все ядра вместе провели 8 секунд в idle (т.е. каждое ядро за 1 целую секунду провело 1 целую секунду в idle):




>>> (8.0 - 8.0) * 100 / 8.0

0.0




То нагрузка на ЦПУ была бы 0%.




И наоборот, если бы половина времени, т.е. 4 секунды в общем, были бы потрачены в idle, то результат:




>>> (8.0 - 4.0) * 100 / 8.0

50.0




50% idle, 50% – остальные режимы.




Вообще есть хорошая страничка тут – Как найти процент от числа для тех, кто как я не силён в математике 




Настройка панели




Теперь переходим к панели.




Жмём на зголовок панели – Edit:





В General задаём имя:





В Metrics добавляем наш запрос:





В Options настраиваем вид:




  • Gauge – включаем отображение шкалы



  • Spark lines – строка “истории”



  • Coloring – включаем красивую подсветку по значениям, и в Thresholds задаём значения, при которых цвет будет меняться – на оранжевый при 75%, и на красный – при 90%



  • Stat – Current



  • Unit – выбираем None > percent 1-100




Получается такое:





Возвращаемся к дашборе, добавляем ещё один елемент – Row:





Задаём имя, меняем размер панельки с CPU Busy:





Load Average




Следующая панелька будет выводить Load Average.




Можно было бы вывести просто значение node_load1{env=~"$environment"} – но на Dev сервере одно ядро, и значение node_load1 == 1 будет являться условными 100% для одного ядра, а на Production с его 8 ядрами node_load1 == 1 будет всего:




>>> 1.0 / 8 * 100

12.5




12.5%




Значит, что бы корректно отрисовывать шкалу – нам потребуется получить значение LA, поделить его на кол-во ядер и умножить на 100 – получим % от “максимального” (в кавычках, потому что LA может быть и выше 1 для 1 ядра или 8 для 8 ядер) значения.




Следовательно – используем такой запрос:




avg(node_load1{env=~"$environment"}) / count(count(node_cpu_seconds_total{env=~"$environment"}) by (cpu)) * 100





Настраиваем шкалу аналогично CPU Busy:





Перетаскиваем её в Row, меняем размер:





Проверим поведение.




Устанавливаем стресс-тест для CPU:




root@bm-backed-app-dev:/opt/prometheus-client# apt install stress




Запускаем его:




root@bm-backed-app-dev:/opt/prometheus-client# stress —cpu 8 —timeout 20





И данные из uptime:




root@bm-backed-app-dev:/opt/prometheus-client# uptime

18:03:18 up 25 days,  6:18,  1 user,  load average: 1.65, 1.13, 0.64




Memory usage




Далее добавим шкалу с отображением занятой памяти.




Проверим free на проде:




root@mobilebackend-production:/data/projects# free -h

total        used        free      shared  buff/cache   available

Mem:            15G        2.8G        7.5G         93M        4.8G         11G




15 ГБ всего, 2.8 занято активными процессами, 4.8 – кеш, свободной памяти 7.5. ОК.




Составим запрос:




((node_memory_MemTotal_bytes{env="$environment"} - node_memory_MemFree_bytes{env="$environment"}) / (node_memory_MemTotal_bytes{env="$environment"} )) * 100




  • в (node_memory_MemTotal_bytes{env="$environment"} - node_memory_MemFree_bytes{env="$environment"})  считаем общее кол-во занятой памяти (active + cache), назовём её busy



  • и считаем busy / total * 100 – получаем % от свободной памяти




В Options включаем шкалу, настраиваем аналогично примерам выше:





Получается теперь:





Disk usage




В Prometheus выполняем запрос для проверки:




node_filesystem_avail_bytes{env="mobilebackend-dev",fstype="ext4"}





Добавляем панель, добавляем в неё запрос на получение метрик о /rootfs и вычисляем % занятого места:




100 - ((node_filesystem_avail_bytes{env="$environment",mountpoint="/rootfs",fstype="ext4"} * 100) / node_filesystem_size_bytes{env="$environment",mountpoint="/rootfs",fstype="ext4"})





Настраиваем шкалу аналогично предыдущим:





Повторяем для второго диска – /rootfs/data, получаем такую картинку в дашборде:





Текущеее время




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




Для вывода времени – опять используем Singlestat панели, и функцию timestamp(), которой передадим метрику up (можно любую – нам только требуется получить из метрики время).




Создаём панель, в Metrics указываем:




timestamp(up{instance="localhost:9090",job="prometheus"}) * 1000




В Options в Units выбираем YYYY-MM-DD HH:mm:ss и получаем текущее время (правда дата не выводится, но она и не нужна):





Сортируем по желанию, и получаем первую часть дашборды:





Статусы сервисов




Теперь – добавим простое отображение стаусов Up/Down для сервисов, которые работают на хосте и необходимы для работы приложения.




У нас это:




  • nginx



  • php-fpm



  • memcahed



  • redis



  • rabbitmq



  • AWS RDS



  • AWS ALB




Memcahed статус




Начнём с вывода статуса memcahed.




Создаём новую Row, в ней – новую Singlestat панель.




На сервере запущен quay.io/prometheus/memcached-exporter, который среди прочих метрик возвращает memcahed_up:




...

  memcached-exporter:

    image: quay.io/prometheus/memcached-exporter

    network_mode: "host"

...




memcahed на сервере один, потому можем создать простой запрос на получение кол-ва сервисов в up:




memcached_up{env="$environment",instance="localhost:9150",job="memcached-exporter"}




В Value mappings добавляем отображение статуса – Up, если memcahed_up == 1, или DOWN – если ноль:





В Options > Threshold задаём значения 1,1 (меньше одного – сразу красным) и Stat Current, получаем симпатичный статус:





RabbitMQ статус




Запущен https://github.com/kbudde/rabbitmq_exporter.




Создаём пользователя для доступа експортёра:




root@bm-backed-app-dev:/opt/prometheus-client# rabbitmqctl add_user prometheus password

Creating user "prometheus" ...




Добавляем ему тег “мониторинг”:




root@bm-backed-app-dev:/opt/prometheus-client# rabbitmqctl set_user_tags prometheus monitoring




Добавим его в переменные контейнера:




...

  rabbitmq-exporter:

    image: kbudde/rabbitmq-exporter

    network_mode: "host"

    environment:

    - PUBLISH_PORT=9419

    - RABBIT_USER=prometheus

    - RABBIT_PASSWORD=password

...




Перезапускаем сервис:




root@bm-backed-app-dev:/opt/prometheus-client# systemctl restart prometheus-client.service




Проверяем метрики:




root@bm-backed-app-dev:/home/admin# curl -s localhost:9419/metrics | grep -v # | grep _up

rabbitmq_up 1




И добавляем ещё одну Singlestat панель:





PHP-FPM stats




Повторяем для NGINX – для него всё, как в примерах выше, и добавляем ещё один статус – для PHP-FPM.




Единственное отличие тут – это количество.




Если все сервисы запущены по одному на дев/прод сервер, то php-fpm запущено 6 пулов:




root@bm-backed-app-dev:/opt/prometheus-client# ps aux | grep fpm: | grep -v grep | grep -v master | awk '{print $11, $12, $13}' | uniq

php-fpm: pool dev.admin.domain1.com

php-fpm: pool dev.admin.domain2.com

php-fpm: pool dev.domain1.com

php-fpm: pool dev.domain2.com

php-fpm: pool dev.domain3.com

php-fpm: pool dev.admin.domain3.com





Соответственно, php_fpm_up вернёт не 1, а 6 результатов – по 1 на каждый пул.




Учитывая это – формируем запрос в Metrics и используем count():




count(php_fpm_up{env="$environment",instance="localhost:9253",job="php-fpm-exporter"})




В Value mappings в Type вместо Value to text выбираем range to text:





MySQL stats




Т.к. mysql_exporter ещё не запущен – то тут кратенько пример его запуска.




В роли серверов баз данных используется AWS RDS, два мастер-инстанса.




Значит – надо запустить два експортёра, по одному на каждый RDS. В Ansible шаблон Compose файл добавляем их:




...

  mysql_exporter_db1:

    image: prom/mysqld-exporter

    networks:

    - prometheus-client

    ports:

    - 9104:9104

    environment:

    - DATA_SOURCE_NAME={{ mysql_monitoring_db1_user }}:{{ mysql_monitoring_db1_pass }}@({{ mysql_monitoring_db1_host }}:3306)/



  mysql_exporter_db2:

    image: prom/mysqld-exporter

    networks:

    - prometheus-client

    ports:

    - 9105:9104

    environment:

    - DATA_SOURCE_NAME={{ mysql_monitoring_db2_user }}:{{ mysql_monitoring_db2_pass }}@({{ mysql_monitoring_db2_host }}:3306)/




Переменные берутся из Ansible vault с зашифрованными паролями.




Добавляем джобы в конфиг Прометеус сервера:




...

  - job_name: 'mysql_exporter_db1'

    static_configs:

      - targets:

        - 'localhost:9104'



  - job_name: 'mysql_exporter_db2'

    static_configs:

      - targets:

        - 'localhost:9105'




Деплоим, проверяем DB1:




root@mobilebackend-dev:/opt/prometheus-client# curl -s localhost:9104/metrics | grep -v # | grep -w mysql_up

mysql_up 1




И DB2 (второй екпортёр слушает на порту 9105):




root@mobilebackend-dev:/opt/prometheus-client# curl -s localhost:9105/metrics | grep -v # | grep -w mysql_up

mysql_up 1




Добавляем в Grafana:




mysql_up{env="$environment",instance="localhost:9104",job="mysql_exporter_db1"}





Load Balancer statistics




Добавим ещё несколько графиков – статистику с AWS Application Load Balancer.




Тут надо добавить ещё одну переменную – Load balancer.




К сожалению – cloudformation_exporter не умеет получать теги, поэтому пока придётся использовать просто имена ALB (надо будет посмотреть – может на стороне Prometheus можно будет сделать им relabel).




Добавляем переменную, в запросе указываем:




aws_applicationelb_request_count_sum{job="aws_applicationelb"}




В регулярном выражении – получаем только имя ALB:




/.*app/(.*)"/





Добавляем Row, и в ней – новую панель, но на этот раз типа Graph, в Metrics добавим четыре запроса – на коды 2хх, 3хх, 4хх и 5хх от targets:




aws_applicationelb_httpcode_target_2_xx_count_sum{load_balancer="app/$load_balancer"}





Аналогично можно добавить статистику по времени ответа бекенда.




Добавляем панель Connections/response time – тут будем выводить кол-во активный сессий и время ответа targets.




Добавляем метрики:




aws_applicationelb_target_response_time_sum{load_balancer="app/$load_balancer"}




И активные сессии:




aws_applicationelb_active_connection_count_sum{load_balancer="app/$load_balancer"}




Series overrides




Сейчас на графике отображаются юниты по оси Y слева и внизу.




Но – на этом графике используются метрики двух типов – в одном выводится count – кол-во сессий, а на втором – время ответа от бекенда до ALB в мс.




Хочется, что слева в шкале выводились числа, снизу время получения метрики, а слева – кол-во милисекнуд ответа бекенда.




Для этого – используем ещё одну интересную возможность Grafana – Series overrides. Интересный пост на эту тему есть тут – Advanced Graphing (Part1): Style Overrides.




Переходим в Axes, и включаем отображение шкалы Y справа, в юнитах используем милисекунды:





Далее переходим в Display > Series overrides > Add override, и в Alias or regex указываем алиас метрики, в данном случае мы хотим выводить время, основываясь на данных из `aws_applicationelb_target_response_time_sum`, который в метриках указывается как response_time ms:





Кликаем на “+” – добавляем желамое действие. Тут указываем отображание времени в Y Right и заодно – можно поиграть со цветом: 





И всё вместе теперь выглядит так: 







EC2 statistics




И последним – добавим графики EC2.




В принципе – тут ничего такого, что уже не рассматривалось выше.




CPU




Сначала – статистика использования CPU – System, User, IOwait, idle.




В самом простом виде запросы выглядел бы так:




sum by (instance)(rate(node_cpu_seconds_total{mode="system",env="$environment"}[5m])) * 100




Получаем % от времени, которое CPU проёвл в режиме system/user/idle и т.д.




Но в случае, когда у нас несколько ядер – добавляем вычисление % от кол-ва ядер, аналоигчно тому, как мы это делали для отрисовки шкалы с % LA и CPU Busy:




(avg(sum by (instance)(rate(node_cpu_seconds_total{mode="idle",env="$environment"}[5m])) * 100)) / count(count(node_cpu_seconds_total{env="$environment",instance="localhost:9100"}) by (cpu))





Memory




Добавляем панель RAM.




Выводим память всего:




https://sidmid.ru/wp-content/uploads/2021/12/41-1024x494.png




Использованной памяти:




node_memory_MemTotal_bytes{env="$environment"} - node_memory_MemFree_bytes{env="$environment"} - (node_memory_Cached_bytes{env="$environment"} + node_memory_Buffers_bytes{env="$environment"})




Свободной памяти:




node_memory_MemFree_bytes{env="$environment"}





Диск




Добавим отображение времени на read-write операции:




irate(node_disk_read_time_seconds_total{env="$environment"}[5m])

irate(node_disk_write_time_seconds_total{env="$environment"}[5m])




В Legend используем {{device}}, куда будет подставлено значение из label “device“:






Получается так: 




Процессы




И последняя уже таблица – процессы в системе.




Выводим кол-во процессов в статусе run:




node_procs_running{env="$environment"}




И blocked:




node_procs_blocked{env="$environment"}





И всё вместе теперь выглядит так:





Источник: https://sidmid.ru/grafana-создание-dashboard/



2023-01-02T04:09:01
DevOps