Привет, Хабр!

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

Мы используем Prometheus и его формат метрик, поэтому дальше я буду ориентироваться именно на него.

Что же такое метрика?

По сути, метрика — это совокупность временных рядов, связанных одним именем, но отличающихся набором меток (labels).

Временной ряд (time series) представляет собой набор наблюдений, полученных путем регулярного измерения одной переменной в течение некоторого периода времени. Каждый временной ряд может быть однозначно идентифицирован по имени измеряемой переменной (метрики) и опциональному набору пар "ключ-значение", называемых метками (labels).

Взгляните на json-представление метрики ниже:

{
  "labels": {
    "__name__": "http_server_requests_seconds_bucket",
    "app": "some-useful-app",
    "app_kubernetes_io_managed_by": "Helm",
    "instance": "192.168.22.60:8080",
    "job": "kubernetes-endpoints",
    "kubernetes_name": "some-useful-app-v1",
    "kubernetes_namespace": "some-k8s-namespace",
    "kubernetes_node": "some-k8s-node-1123vlk",
    "kubernetes_pod": " some-useful-app-v1-6bb7445dbb-6rfnx",
    "le": "0.268435456",
    "method": "GET",
    "outcome": "SUCCESS",
    "status": "200",
    "uri": "/actuator/prometheus"
  },
  "name": "http_server_requests_seconds_bucket",
  "timestamp": "2021-12-11T19:02:54Z",
  "value": "82"
}

Отчётливо видны имя метрики, момент времени, когда она была собрана, её значение и набор меток.

Если поднять ещё один экземпляр того же самого приложения в Kubernetes (ещё один pod), то он будет отдавать метрику с точно таким же именем, но другими значениями меток kubernetes_pod/kubernetes_node/instance, что даст нам в итоге другой временной ряд.

Вот ещё один пример метрики и нескольких временных рядов:

jvm_memory_used_bytes{application="some-useful-app",area="nonheap",id="Compressed Class Space",} 1.0232704E7
jvm_memory_used_bytes{application="some-useful-app",area="nonheap",id="CodeHeap 'non-profiled nmethods'",} 2.0701312E7
jvm_memory_used_bytes{application="some-useful-app",area="nonheap",id="Metaspace",} 8.9114568E7
jvm_memory_used_bytes{application="some-useful-app",area="heap",id="G1 Old Gen",} 1.03913472E8
jvm_memory_used_bytes{application="some-useful-app",area="heap",id="G1 Eden Space",} 6.815744E7
jvm_memory_used_bytes{application="some-useful-app",area="nonheap",id="CodeHeap 'non-nmethods'",} 1332608.0
jvm_memory_used_bytes{application="some-useful-app",area="nonheap",id="CodeHeap 'profiled nmethods'",} 2.8387072E7
jvm_memory_used_bytes{application="some-useful-app",area="heap",id="G1 Survivor Space",} 2.2020096E7

Сколько может быть метрик?

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

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

Схема именования метрик

Итак, у каждой метрики есть имя. Выбор имени — очень важный и ответственный этап, так как в дальнейшем оно, по сути, станет частью вашего публичного API: имя метрики "прорастёт" на дашборды и в алерты.

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

В общем случае имя метрики выглядит как:

<library>_<custom_name>_<unit>_<suffix>
или
<service>_<custom_name>_<unit>_<suffix>,

где

  • <library>, <service> — короткое обозначение вашей библиотеки, модуля, сервиса;

  • <custom_name> — произвольное название метрики;

  • <unit> — размерность метрики;

  • <suffix> — суффикс имени метрики в зависимости от её типа (может отсутствовать).

Давайте подробнее разберём эту схему именования. 

Используйте синтаксически допустимые имена

Имя метрики:

  • должно быть записано в нижнем регистре;

  • может содержать ASCII буквы и цифры, а также символы подчёркивания;

  • должно начинаться с буквы.

Использование символов в верхнем регистре допустимо, но крайне не рекомендуется.

fluentd_messages_read_from_kafka_total

Используйте префиксы, связанные с вашим приложением/сервисом/библиотекой/предметной областью 

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

Это простое правило позволяет решить сразу несколько задач:

  • снижает риски коллизий с другими сторонними метриками;

  • позволяет выделить ваши метрики из общей массы и управлять ими.

monitoring_alert_rules_imported_total

pgih_duplicated_indexes

Используйте одну размерность и по возможности указывайте её в имени метрики

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

Отдавайте предпочтение базовым размерностям: секундам, байтам, метрам, граммам и т.п.

Добавляйте размерность как суффикс к имени метрики в форме множественного числа: seconds, bytes, meters, grams и т.п.

http_request_duration_seconds

fluentd_messages_read_from_kafka_bytes_total

elk_requests_processed_bytes_total

Именование меток (labels) 

На имена меток накладываются такие же ограничения, как и на имена метрик (см. выше).

Используйте префиксы для собственных меток и избегайте названий, которые Prometheus (или ваша команда мониторинга) может автоматически добавлять при сборе метрик, таких как: env, cluster, team, zone, region и т.п.

Сами значения меток ничем не ограничены и могут содержать любые UTF-8 символы.

http_server_requests_seconds_sum{application="logs-transfer",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/actuator/health",} 15.3

logstransfer_output_messages_success_processed_total{application="logs-transfer", logstransfer_customer_namespace="dev-k8s-dbo-online-ns", logstransfer_topic_name="java-app-logs",} 1018.0

При инструментировании приложений (добавлении собственных метрик) очень важно указывать в labels только те сведения, которые однозначно относятся к вашему приложению и особенностям его работы. Например, никогда не пытайтесь самостоятельно добавить в метрики информацию о том, в каком окружении работает ваше приложение. Даже если вы точно знаете, что оно всегда будет запускаться в Kubernetes, не нужно самостоятельно пробрасывать в labels через переменные окружения информацию об имени pod’а или namespace.

Именование recording rules

Механизм recording rules в Prometheus позволяет вычислять PromQL выражения на интервальной основе, а результат вычисления сохранять в новый набор временных рядов. Это оптимизация тяжёлых вычислений для дашбордов и алертов.

Для имён метрик, вычисляемых через recording rules, допустимо и общепринято использовать двоеточия:

<service >:<custom_name>:<additional_attribute>

Остальные рекомендации по именованию точно такие же, как и для метрик (см. выше).

logstransfer:top10_consumers:sort_desc

Заключение

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

Относитесь к прикладным метрикам как к части вашего публичного API и старайтесь поддерживать обратную совместимость.

Другие заметки из этого цикла

  1. Заметки о мониторинге #1 — NaN в Prometheus

Комментарии (0)