Архив автора: admin

Примеры запросов к метрикам Prometheus

Синтаксис запросов




Рассмотрим синтаксис некоторых функций, которые мы будем использовать в нашей инструкции. Проверять все наши запросы можно в веб-интерфейсе прометеуса на странице /graph.




irate или rate




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




irate(<запрос на выборку метрики>[интервал времени, на протяжении которого происходят изменения метрики])




avg by




Среднее значение по условию, например:




avg by (instance)




… среднее для каждого инстанса.




avg_over_time




Среднее значение всех значений для метрик в указанном интервале.




avg_over_time(<запрос на выборку метрики>[интервал])




sum




Функция суммирует полученные результаты:




sum(<запрос на получение значений>)




time




Отображает время в формате UNIX TIME.




time()




count




Считает количество значений:




count(<запрос на получение значений>)




Вместе с by значения могут фильтроваться:




count(<запрос на получение значений>) by (<по какому показателю>)




Арифметические операции




1. С полученными числовыми данными можно выполнять различные математические операции. Например, если мы получим значения времени в секундах, то можно его перевести в часы:




<полученное время> / 60




Или наоборот




<полученное время> * 60




2. Полученный остаток от процентного показателя вычисляется по формуле:




100 — <полученный процент>




Системная информация




Для начала рассмотрим примеры получения общих системных показателей.




Время работы системы




Расчет ведется относительно показателя node_boot_time_seconds (время последнего включения системы).




1. Общее время для всех узлов:




sum(time() — node_boot_time_seconds{})




sum(time() — node_boot_time_seconds{}) / 60




* первый запрос в секундах, второй — в минутах.




2. Для некоторых узлов:




sum(time() — node_boot_time_seconds{instance=~»192.168.0.15:9100|192.168.0.20:9100″})




* в данном примере мы получим сумму значений для узлов 192.168.0.15 и 192.168.0.20.




3. Для всех узлов по отдельности:




time() — node_boot_time_seconds{}




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




Температура




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




1. Для всех нод:




node_hwmon_temp_celsius




2. Для конкретных:




node_hwmon_temp_celsius{instance=~’192.168.0.15:9100|192.168.0.20:9100′}




Процессор




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




Количество процессоров




1. Общее количество всех процессоров всех узлов:




sum(count(node_cpu_seconds_total{mode=’system’}) by (cpu))




2. Для некоторых инстансов:




sum(count(node_cpu_seconds_total{instance=~’192.168.0.15:9100|192.168.0.20:9100′,mode=’system’}) by (cpu))




* для 192.168.0.15 и 192.168.0.20.




3. По отдельности:




count(node_cpu_seconds_total{mode=’system’}) by (instance)




Нагрузка




1. На все ядра всех узлов:




(irate(node_cpu_seconds_total{job=»node_exporter_clients»,mode=»idle»}[5m])) * 100




100 — ((irate(node_cpu_seconds_total{job=»node_exporter_clients»,mode=»idle»}[5m])) * 100)




* первый запрос для отображения процента свободного процессорного времени, второй — процент утилизации.




Пример ответа:




{cpu="0", instance="192.168.0.15:9100", job="node_exporter_clients", mode="idle"} 0.6000000238418579

{cpu="0", instance="192.168.0.20:9100", job="node_exporter_clients", mode="idle"} 0.9999999403953552

{cpu="1", instance="192.168.0.15:9100", job="node_exporter_clients", mode="idle"} 0.6000000238418579

{cpu="1", instance="192.168.0.20:9100", job="node_exporter_clients", mode="idle"} 1.5999999642372131

{cpu="2", instance="192.168.0.15:9100", job="node_exporter_clients", mode="idle"} 0.8000000193715096

{cpu="2", instance="192.168.0.20:9100", job="node_exporter_clients", mode="idle"} 0.8000001311302185

{cpu="3", instance="192.168.0.15:9100", job="node_exporter_clients", mode="idle"} 1.0000000149011612

{cpu="3", instance="192.168.0.20:9100", job="node_exporter_clients", mode="idle"} 0.6000000238418579

{cpu="4", instance="192.168.0.15:9100", job="node_exporter_clients", mode="idle"} 0.19999999552965164

{cpu="4", instance="192.168.0.20:9100", job="node_exporter_clients", mode="idle"} 0.2000001072883606

{cpu="5", instance="192.168.0.15:9100", job="node_exporter_clients", mode="idle"} 0.40000002831220627

{cpu="5", instance="192.168.0.20:9100", job="node_exporter_clients", mode="idle"} 0.3999999165534973

{cpu="6", instance="192.168.0.15:9100", job="node_exporter_clients", mode="idle"} 0.6000000238418579

{cpu="6", instance="192.168.0.20:9100", job="node_exporter_clients", mode="idle"} 0.2000001072883606

{cpu="7", instance="192.168.0.15:9100", job="node_exporter_clients", mode="idle"} 0.19999999552965164

{cpu="7", instance="192.168.0.20:9100", job="node_exporter_clients", mode="idle"} 0.3999999165534973




2. Средняя величина по ядрам для всех узлов:




avg by (instance)(irate(node_cpu_seconds_total{job=»node_exporter_clients»,mode=»idle»}[5m])) * 100




100 — (avg by (instance)(irate(node_cpu_seconds_total{job=»node_exporter_clients»,mode=»idle»}[5m])) * 100)




* первый запрос для отображения процента свободного процессорного времени, второй — процент утилизации.




Пример ответа:




{instance="192.168.0.15:9100"} 0.7999999960884452

{instance="192.168.0.20:9100"} 0.9500000253319598




3. Средняя величина по ядрам для конкретного узла:




avg by (instance)(irate(node_cpu_seconds_total{instance=»192.168.0.15:9100″,job=»node_exporter_clients»,mode=»idle»}[5m])) * 100




100 — (avg by (instance)(irate(node_cpu_seconds_total{instance=»192.168.0.15:9100″,job=»node_exporter_clients»,mode=»idle»}[5m])) * 100)




* первый запрос для отображения процента свободного процессорного времени, второй — процент утилизации.




Пример ответа:




{instance=»192.168.0.15:9100″} 1.100000003352747




Время ожидания




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




1. Среднее значение за 30 минут для всех узлов:




avg(irate(node_cpu_seconds_total{mode=»iowait»}[30m])) * 100




2. Для конкретного узла:




avg(irate(node_cpu_seconds_total{instance=~»192.168.0.15:9100|192.168.0.20:9100″,mode=»iowait»}[30m])) * 100




3. Отдельно по каждой ноде:




irate(node_cpu_seconds_total{mode=»iowait»}[30m]) * 100




Оперативная память




Для наших запросов мы будем использовать метрики:







Рассмотрим примеры.




Объем памяти




1. Для всех узлов:




sum(node_memory_MemTotal_bytes{})




2. Для некоторых:




sum(node_memory_MemTotal_bytes{instance=~»192.168.0.15:9100|192.168.0.20:9100″})




3. По отдельности:




node_memory_MemTotal_bytes{}




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




1. Свободно:




100 * (1 — ((avg_over_time(node_memory_MemFree_bytes[5m]) + avg_over_time(node_memory_Cached_bytes[5m]) + avg_over_time(node_memory_Buffers_bytes[5m])) / avg_over_time(node_memory_MemTotal_bytes[5m])))




2. Занято:




100 * ((avg_over_time(node_memory_MemFree_bytes[5m]) + avg_over_time(node_memory_Cached_bytes[5m]) + avg_over_time(node_memory_Buffers_bytes[5m])) / avg_over_time(node_memory_MemTotal_bytes[5m]))




Пример ответа:




{instance="192.168.0.15:9100", job="node_exporter_clients"} 41.96494651116369

{instance="192.168.0.20:9100", job="node_exporter_clients"} 10.573695601658944




Сеть




Мы будем использовать метрики:







Примеры ниже.




Передача данных




1. Входящий трафик за последние 5 минут:




rate(node_network_receive_bytes_total[5m]) * 8 / 1024 / 1024




2. Иcходящий трафик за последние 5 минут:




rate(node_network_transmit_bytes_total[5m]) * 8 / 1024 / 1024




Дисковый накопитель




Используемые метрики:







Переходим к примерам.




Объемы




1. Объем разделов:




node_filesystem_size_bytes{instance=~’192.168.0.15:9100|192.168.0.20:9100′,fstype=~»ext4|xfs»}




node_filesystem_size_bytes{fstype=~»ext4|xfs»}




* для конкретных нод или всех.




2. Объем доступного пространства:




node_filesystem_avail_bytes {fstype=~»ext4|xfs»}




3. Объем в процентах.




Используется:




1 — (node_filesystem_free_bytes{fstype=~»ext4|xfs»} / node_filesystem_size_bytes{fstype=~»ext4|xfs»})




Свободно:




node_filesystem_free_bytes{fstype=~»ext4|xfs»} / node_filesystem_size_bytes{fstype=~»ext4|xfs»}




Нагрузка




1. Чтение в килобайтах:




rate(node_disk_read_bytes_total{instance=»Server10:9100″, device=»sda»}[5m]) / 1024




irate(node_disk_read_bytes_total{}[30m]) / 1024




* для конкретного сервера / диска и для всех серверов и всех дисков.




2. Запись в килобайтах:




rate(node_disk_written_bytes_total{instance=»Server10:9100″, device=»sda»}[5m]) / 1024




irate(node_disk_written_bytes_total{}[30m]) / 1024




* для конкретного сервера / диска и для всех серверов и всех дисков.




Источник: https://sidmid.ru/примеры-запросов-к-метрикам-prometheus/



Prometheus: Recording Rules и теги – разделяем алерты в Slack

Необходимо создать отдельные каналы для backendwebgaming, и их алерты вынести в разные каналы Slack. Тогда бекенд-девелоперы будут знать, что если в их канал Слака прилетел алерт – то на него таки стоит обратить внимание.




Как реализуем: используем Prometheus Recording Rules для правил, что бы создать отдельные алерты. Каждый алерт будет создаваться со своим набором тегов, используя которые мы в Opsgenie будем разруливать алерты через разные Slack-интергации по разным каналам.




Prometheus Recording Rules




Recording Rules позволяют формировать правила с expression, которые потом можно использовать в алертах.




К примеру, у нас есть такой алерт:




- name: Endpoint.rules



  rules:



  - record: blackbox_probe:http_probe

    expr: probe_success{instance!~".*stage.*"}

   

  - alert: EndpointProbeSuccess

    expr: blackbox_probe:http_probe != 1

    for: 2m

    labels:

      severity: warning

    annotations:

      summary: 'Endpoint down'

      description: 'Cant access endpoint `{{ $labels.instance }}`'

      tags: http



  - alert: EndpointProbeSuccess

    expr: blackbox_probe:http_probe != 1

    for: 5m

    labels:

      severity: critical

    annotations:

      summary: 'Endpoint down'

      description: 'Cant access endpoint `{{ $labels.instance }}`!'

      tags: http




Тут используя общее правило blackbox_probe:http_probe мы создаём два алерта – один сработает с уровнем P3/warning через 2 минуты, а второй – с уровнем P1/critical через 5 минут, если первый не закрыли.




Prometheus Tags




Собственно говоря, тут теги – это просто дополнительное поле в аннотациях.




В конфиге Alertmanager для reciever Opsgenie настроена передача этих тегов – tags: "{{ .CommonAnnotations.tags}}":




- name: 'warning'

  opsgenie_configs:

    - message: "{{ .CommonAnnotations.summary }}"

      description: "{{ range .Alerts }}nn{{ .Annotations.description }}{{ end }}n*Source*: <{{ (index .Alerts 0).GeneratorURL }}|Prometheus alerts>"

      source: "{{ (index .Alerts 0).GeneratorURL }}"

      tags: "{{ .CommonAnnotations.tags}}"

      priority: P3




И затем эти теги доступны в Opsgenie:





А значит – мы их сможем использовать в фильтрах Slack-интеграций.




AWS CloudWatch Tags?




А что делать с CloudWatch? В Opsgenie есть отдельные интеграции с AWS CloudWatch, которые триггерят свои алерты.




Увы – к CloudWatch Alarms теги прикрутить нельзя (пока что? не нашёл как?), поэтому делаем через теги в самой интеграции.




Например, для AWS Trusted Advisor заданы такие теги:





Позже можно будет разруливать между каналами типа #devops-alarms-aws и #devops-alarms-security.




Настройка Prometheus alerts




Настроим общий Recording Rule, и используя его – создадим два алерта, которые будут выбирать метрики, используя label namespace:





Тут очень пригодилось то, что мы изначально в имена неймспейсов Kubernetes включаем и кластер (devstageprod), и команду, которая этот Namespace создавала.




Например, в имени namespace=prod-cn-1-18-backend-rabbitmq-ns мы видим, что это Prod-кластер в Китае (cn), версия Kubernetes в нём – 1.18, команда, которая использует этот сервис – backend, ну и имя самого сервиса – rabbitmq.




А затем к каждому алерту мы навесим свои теги – backend и web.




Итак, создаём Recording Rule с именем test:eks:pod_restarts:rate:




groups:



###############

### Testing ###

###############



- name: Test.rules



  rules:



  - record: test:eks:pod_restarts:rate

    expr: rate(kube_pod_container_status_restarts_total{ekscluster=~"prod-.*"}[15m]) * 900




Само выражение нам сейчас не особо важно, тут просто скопировал правило из алерта рестартов подов.




Далее, используя этот rule описываем два алерта:




groups:



###############

### Testing ###

###############



- name: Test.rules



  rules:



  - record: test:eks:pod_restarts:rate

    expr: rate(kube_pod_container_status_restarts_total{ekscluster=~"prod-.*"}[15m]) * 900



  - alert: "TestingAlertBackend"

    expr: test:eks:pod_restarts:rate{namespace=~".*backend.*"} < 2

    for: 1s

    labels:

      severity: warning

    annotations:

      summary: "Testing alert: Backend"

      description: "Ignore it: testing alerting system"

      tags: backend, eks



  - alert: "TestingAlertWeb"

    expr: test:eks:pod_restarts:rate{namespace=~".*web.*"} < 2

    for: 1s

    labels:

      severity: warning

    annotations:

      summary: "Testing alert: Web"

      description: "Ignore it: testing alerting system"

      tags: web, eks




Тут в алерте TestingAlertBackend используем правило test:eks:pod_restarts:rate, и выбираем все неймспейсы, которые содержат имя backend:




test:eks:pod_restarts:rate{namespace=~".*backend.*"}




А затем к этому алерту добавляем два тега – backend и eks.




Аналогично – с алертом для Web-team.




Alertmanager group_by




Кроме того, добавим в параметр group_by Алертменеджера группировку по тегам, что бы алерты Backend отделялись от алертов Web:




route:



  group_by: ['alertname', 'tags']

  repeat_interval: 15m

  group_interval: 5m




Иначе, если группировать только по alertname, Alertmanager сгенерирует один алерт, который упадёт в первый попавшийся канал.




Переходим к Opsgenie.




Настройка Opsgenie и Slack




Создаём два канала в Slack – #devops-alarms-backend и #devops-alarms-web – будем их использовать в двух интеграциях Slack.




Далее, добавляем саму первую интеграцию Slack, для Backend-team, и в его Alert Filter выбираем алерты уровня P3 и с тегом backend:





А в старой интеграции – #devops-alarms-warning – добавляем фильтр с NOT, что бы не дублировать алерты в старый канал:





Проверяем – триггерим алерт, и получаем пачку алертов в оба канала.




Бекенд:





Тут Tags: backend, eks.




И аналогичная пачка прилетела от подов веб-команды:





Готово




Источник: https://sidmid.ru/prometheus-recording-rules-и-теги-разделяем-алерты-в-slack/



2023-01-02T05:29:16
DevOps

2. Практика — запуск сервисов Traefik

Traefik – это обратный прокси с поддержкой Docker, который предоставляет встроенную панель мониторинга.




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




Панель инструментов представляет собой отдельное веб-приложение, которое будет работать в контейнере Traefik по порту 8080.




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




docker -v
Docker version 19.03.1, build 74b1e89
docker-compose -v
docker-compose version 1.22.0, build f46880fe




cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.1.170 test.ru nginx.test.ru nginx2.test.ru




cat docker-compose.yml




version: '2.4'



services:

  traefik:

    image: traefik

    volumes:

       - /var/run/docker.sock:/var/run/docker.sock

    command:  --web

              --defaultentrypoints=http

              --entrypoints="Name:http Address::80"

              --docker

              --docker.endpoint="unix:///var/run/docker.sock"

              --docker.watch=true

              --docker.exposedbydefault=false

    ports:

       - 80:80

       - 8080:8080

  nginx:

    image: nginx:alpine

    container_name: webserver

    restart: unless-stopped

    labels:

    - traefik.frontend.rule=Host:nginx.test.ru

    - traefik.docker.network=traefik

    - traefik.port=80

    - traefik.enable=true

  nginx2:

   image: nginx:alpine

   volumes:

     - /var/www/nginx:/usr/share/nginx/html

   container_name: webserver2

   restart: unless-stopped

   labels:

    - traefik.frontend.rule=Host:nginx2.test.ru

    - traefik.docker.network=traefik

    - traefik.port=80

    - traefik.enable=true<br /><br /></code><span style="font-family: georgia, palatino, serif;">В данном конфиге мы запускаем 3 сервиса, 1 - traefik, 2,3 - контейнеры nginx.</span>




У traefik только 1 точка входа, это 80 порт. На порту 8080 будет доступна панель в которой визуально отображается какие сервисы доступны.
Для поднимаемых нами сервисов указаны следующие доменные имена: nginx.test.ru и nginx2.test.ru,они используют созданную ранее docker сеть traefik
и как мы видим, отвечают по одному 80 порту.  Для того чтобы работал сервис работал ему прописывается пункт label где задаются параметры для traefik.
Ниже показан ответ curl по нашим сервисам:




curl nginx.test.ru



Спойлер