Зачем это всё
В данной заметке будет рассмотрено создание дашборда в графане в виде сводной таблицы по основным prometheus-метрикам, которые отдает nginx controller.
Дашборд позволит получать сводную информацию по работе http-приложений по конкретным путям, покажет RPS, кол-во запросов по кодам ответа и percentile (50, 90, 95).
Итоговый пример на скриншоте ниже:
Исходный JSON в конце статьи.
Подготовка запросов
Для получения картинки выше необходимо подготовить запросы.
- Первый столбец содержит имя приложения и является “собирающим” для всех остальных и формируется в transform-data панели дашбордов, см. далее. Этот столбец можно сформировать из любого запроса которые далее будут использованы, т.к. в них есть все необходимые метки.
- Второй столбец path можно также получить из запроса выше, т.к. в нем есть все необходимые метрики – by (exported_service, path)
- Третий столбец – RPS, кол-во запросов в секунду. Получается через функцию rate() и далее суммируется по всем инстансам, чтобы не было одинаковых данных
sum by (path, exported_service) (rate(nginx_ingress_controller_requests{exported_namespace=~"$namespace", exported_service!=""}[1h]))
- Четвертый столбец – сумма запросов за определенное время.
sum (increase(nginx_ingress_controller_requests{exported_namespace=~"$namespace",exported_service!=""}[1h])) by (exported_service, path)
- Пятый столбец – среднее время ответа за определенный период, запрос вычисляет суммарную скорость изменения времени обработки запросов за последние 5 минут, агрегируя данные по
path
иexported_service
, и делит на суммарную скорость изменения количества запросов за последние 5 минут
sum by (path, exported_service) (rate(nginx_ingress_controller_request_duration_seconds_sum{exported_namespace=~"$namespace", exported_service!=""}[5m])) /
sum by (path, exported_service) (rate(nginx_ingress_controller_request_duration_seconds_count{exported_namespace=~"$namespace", exported_service!=""}[5m]))
- 6, 7 и 8 столбцы показывают в процентах 200, 4хх и 5хх коды ответа запросов на основе всех входящих запросов. Кол-во запросов с нужным кодом делится на кол-во всех запросов.
(sum (increase(nginx_ingress_controller_requests{status=~"2..",exported_namespace=~"$namespace",exported_service!=""}[1h])) by (exported_service))/(sum (increase(nginx_ingress_controller_requests{exported_namespace=~"$namespace",exported_service!=""}[1h])) by (exported_service))
- 9, 10 и 11 столбцы показывают 50, 90 и 95 процентиль соответственно. Для этого используются специальные метрики *_bucket.
histogram_quantile(0.90, sum by (path, exported_service, le) (
rate(nginx_ingress_controller_request_duration_seconds_bucket{exported_namespace=~"$namespace", exported_service!=""}[5m])
))
Создание дашборда в Grafana
Transform data
При создании каждого запроса важно указать в трех местах тип этого запроса:
- table в правом верхнем углу для самой панельки, на которой будет дашборд
- format: table и type: instant внизу под самим запросом
Далее необходимо трансформировать данные из запросов, добавив “Join by field” и указав поле exported_service. Это поле подтягивается из всех имеющихся запросов и подставляется на первое место в таблице в первый столбец.
После этого необходимо отфильтровать данные: убрать лишнее, оставить нужное:
И осталось задать читаемые имена столбцов и задать им порядок:
Как видно из примера, каждый запрос имеет свою нумерацию по алфавиту, и над каждым из запросов можно выполнять какие-либо операции с его данными и их отображением в таблице.
Panel options
Для корректного отображения значений всех запросов и цветовой окраски используется панелька справа, позволяющая переопределить каждое поле или задать поведение по умолчанию.
В данном примере для поля Avg добавлено override, где указаны единицы измерения – секунды, а для поля 50 процентиля – short, т.е. без указания секунд
short необходимо использовать, чтобы было больше места в таблице, и для корректного цветого окраса через treshold. На скриншоте ниже видно, что если есть значения больше 1 секунды, то ячейка будет красной и привлечёт внимание. Всё остальное по умолчанию зеленое.
Исходный JSON
{
"datasource": {
"uid": "P1809F7CD0C75ACF3",
"type": "prometheus"
},
"description": "Данные с nginx ingress controlle, реальное время",
"fieldConfig": {
"defaults": {
"custom": {
"align": "center",
"cellOptions": {
"type": "color-background"
},
"inspect": false,
"filterable": false,
"width": 100
},
"mappings": [
{
"options": {
"NaN": {
"color": "#404040",
"index": 0,
"text": "-"
}
},
"type": "value"
},
{
"options": {
"match": "null",
"result": {
"color": "#404040",
"index": 1,
"text": "-"
}
},
"type": "special"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "transparent",
"value": null
}
]
},
"color": {
"mode": "thresholds"
},
"decimals": 2,
"links": [],
"unit": "percentunit"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "2xx"
},
"properties": [
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "dark-red",
"value": null
},
{
"color": "dark-yellow",
"value": 0.85
},
{
"color": "dark-green",
"value": 0.95
}
]
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "exported_service"
},
"properties": [
{
"id": "custom.width",
"value": 223
}
]
},
{
"matcher": {
"id": "byName",
"options": "path"
},
"properties": [
{
"id": "custom.width",
"value": 455
}
]
},
{
"matcher": {
"id": "byName",
"options": "RPS (1h)"
},
"properties": [
{
"id": "custom.width",
"value": 114
},
{
"id": "unit",
"value": "none"
}
]
},
{
"matcher": {
"id": "byName",
"options": "Requests (1h)"
},
"properties": [
{
"id": "unit",
"value": "none"
},
{
"id": "custom.width",
"value": 124
}
]
},
{
"matcher": {
"id": "byName",
"options": "Avg (5m)"
},
"properties": [
{
"id": "unit",
"value": "s"
},
{
"id": "custom.width",
"value": 118
}
]
},
{
"matcher": {
"id": "byName",
"options": "p50"
},
"properties": [
{
"id": "unit",
"value": "short"
},
{
"id": "custom.width",
"value": 105
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "dark-green",
"value": null
},
{
"color": "dark-red",
"value": 1
}
]
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "p90"
},
"properties": [
{
"id": "unit",
"value": "short"
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "dark-green",
"value": null
},
{
"color": "dark-red",
"value": 1
}
]
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "p95"
},
"properties": [
{
"id": "unit",
"value": "short"
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "dark-green",
"value": null
},
{
"color": "dark-red",
"value": 1
}
]
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "4xx"
},
"properties": [
{
"id": "color",
"value": {
"mode": "thresholds"
}
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "dark-green",
"value": null
},
{
"color": "#EAB839",
"value": 0.01
}
]
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "5xx"
},
"properties": [
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "dark-green",
"value": null
},
{
"color": "semi-dark-red",
"value": 0.01
}
]
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "2xx"
},
"properties": [
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "dark-red",
"value": null
},
{
"color": "dark-yellow",
"value": 0.8
},
{
"color": "dark-green",
"value": 0.95
}
]
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Service"
},
"properties": [
{
"id": "custom.width",
"value": 244
}
]
}
]
},
"gridPos": {
"h": 12,
"w": 24,
"x": 0,
"y": 8
},
"id": 187,
"options": {
"showHeader": true,
"cellHeight": "sm",
"footer": {
"show": false,
"reducer": [
"sum"
],
"countRows": false,
"fields": ""
},
"frameIndex": 1,
"sortBy": [
{
"desc": false,
"displayName": "Service"
}
]
},
"pluginVersion": "10.4.13",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "(sum (increase(nginx_ingress_controller_requests{status=~\"2..\",exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h])) by (exported_service))/(sum (increase(nginx_ingress_controller_requests{exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h])) by (exported_service))",
"format": "table",
"hide": false,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "(sum (increase(nginx_ingress_controller_requests{status=~\"4..\",exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h])) by (exported_service))/(sum (increase(nginx_ingress_controller_requests{exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h])) by (exported_service))",
"format": "table",
"hide": false,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "C"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "(sum (increase(nginx_ingress_controller_requests{status=~\"5..\",exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h])) by (exported_service))/(sum (increase(nginx_ingress_controller_requests{exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h])) by (exported_service))",
"format": "table",
"hide": false,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "D"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "(sum (increase(nginx_ingress_controller_requests{status=~\"2..\",exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h] offset 1h)) by (exported_service))/(sum (increase(nginx_ingress_controller_requests{exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h] offset 1h)) by (exported_service))",
"format": "table",
"hide": true,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "E"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "sum (increase(nginx_ingress_controller_requests{exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h])) by (exported_service, path)",
"format": "table",
"hide": false,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "increase(nginx_ingress_controller_requests{exported_namespace=~\"$namespace\",exported_service!=\"\"}[1h])",
"format": "table",
"hide": true,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "F"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "sum by (path, exported_service) (rate(nginx_ingress_controller_requests{exported_namespace=~\"$namespace\", exported_service!=\"\"}[1h]))",
"format": "table",
"hide": false,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "G"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "sum by (path, exported_service) (rate(nginx_ingress_controller_request_duration_seconds_sum{exported_namespace=~\"$namespace\", exported_service!=\"\"}[5m])) /\nsum by (path, exported_service) (rate(nginx_ingress_controller_request_duration_seconds_count{exported_namespace=~\"$namespace\", exported_service!=\"\"}[5m]))",
"format": "table",
"hide": false,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "H"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "histogram_quantile(0.50, sum by (path, exported_service, le) (\n rate(nginx_ingress_controller_request_duration_seconds_bucket{exported_namespace=~\"$namespace\", exported_service!=\"\"}[5m])\n))",
"format": "table",
"hide": false,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "I"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "histogram_quantile(0.90, sum by (path, exported_service, le) (\n rate(nginx_ingress_controller_request_duration_seconds_bucket{exported_namespace=~\"$namespace\", exported_service!=\"\"}[5m])\n))",
"format": "table",
"hide": false,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "J"
},
{
"datasource": {
"type": "prometheus",
"uid": "P1809F7CD0C75ACF3"
},
"editorMode": "code",
"exemplar": false,
"expr": "histogram_quantile(0.95, sum by (path, exported_service, le) (\n rate(nginx_ingress_controller_request_duration_seconds_bucket{exported_namespace=~\"$namespace\", exported_service!=\"\"}[5m])\n))",
"format": "table",
"hide": false,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "K"
}
],
"title": "Статус по сервисам",
"transformations": [
{
"id": "joinByField",
"options": {
"byField": "exported_service",
"mode": "outer"
}
},
{
"id": "filterFieldsByName",
"options": {
"byVariable": false,
"include": {
"names": [
"exported_service",
"Value #B",
"Value #C",
"Value #D",
"Value #A",
"Value #G",
"Value #H",
"Value #I",
"Value #J",
"Value #K",
"path 1"
]
}
}
},
{
"id": "organize",
"options": {
"excludeByName": {
"Time 3": false
},
"indexByName": {
"exported_service": 0,
"path 1": 1,
"Value #G": 2,
"Value #A": 3,
"Value #H": 4,
"Value #B": 5,
"Value #C": 6,
"Value #D": 7,
"Value #I": 8,
"Value #J": 9,
"Value #K": 10
},
"renameByName": {
"Time": "",
"Time 1": " ",
"Time 2": " ",
"Time 3": " ",
"Value #A": "Requests (1h)",
"Value #B": "2xx",
"Value #C": "4xx",
"Value #D": "5xx",
"Value #E": "2xx,% (-1h)",
"Value #F": "Requests",
"Value #G": "RPS (1h)",
"Value #H": "Avg (5m)",
"Value #I": "p50",
"Value #J": "p90",
"Value #K": "p95",
"Value #L": "4xx,% (-3h)",
"Value #M": "5xx,% (-3h)",
"exported_service": "Service",
"path": "Path",
"path 1": ""
},
"includeByName": {}
}
},
{
"disabled": true,
"id": "convertFieldType",
"options": {
"conversions": [
{
"dateFormat": " ",
"destinationType": "string",
"targetField": " "
}
],
"fields": {}
}
}
],
"type": "table"
}