Health-дашборд в grafana для nginx ingress controller

Зачем это всё

В данной заметке будет рассмотрено создание дашборда в графане в виде сводной таблицы по основным 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"
}

Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: