Привет, Хабр!
В последнее время я занимаюсь 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 и старайтесь поддерживать обратную совместимость.