Grafana: создание dashboard

Thank you for reading this post, don't forget to subscribe!

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

Ниже опи­сы­ва­ет­ся про­цесс созда­ния даш­бор­ды, рас­смат­ри­ва­ют­ся при­ме­ры запро­сов из 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.

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


Тут:
  • пер­вая колон­ка – аптй­ам систе­мы в секундах
  • вто­рая – вре­мя в сукун­дах, про­ве­дён­ное в IDLE

Про­ве­ря­ем:

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

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

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

Тут колон­ки:

  • 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).

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

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

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

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

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

  • user: stat = 14351.56, exporter = 14346.37
  • system: stat = 4662.32, exporter = 4660.78

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

Запрос

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

Тут:

  • 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

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


И прод:
Время ядер в 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 секунду:

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

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

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

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

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 будет всего:


12.5%

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

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

Настра­и­ва­ем шка­лу ана­ло­гич­но 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:

Memory usage

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

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

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

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

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

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

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

Disk usage

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

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

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

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

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

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

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

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

В 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:

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

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

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

RabbitMQ статус

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

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

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

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

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

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

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

PHP-FPM stats

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

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

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

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

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

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

MySQL stats

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

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

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

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

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

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

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

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

Load Balancer statistics

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

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

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

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

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

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

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

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

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

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

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.

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

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

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

Memory

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

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

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

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

Диск

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

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

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

Процессы

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

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

И blocked:

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