Refine Grafana traffic dashboard and documentation
This commit is contained in:
@@ -272,7 +272,9 @@ scrape_configs:
|
|||||||
- targets: ["<gateway>:3456"]
|
- targets: ["<gateway>:3456"]
|
||||||
```
|
```
|
||||||
|
|
||||||
`harbor_traffic_bytes_total` содержит общий накопленный объём по источникам Gateway/Proxy. `harbor_device_traffic_bytes_total` содержит upload/download по стабильному `device_id`; пользовательское название и текущий IP находятся в `harbor_device_info`. `harbor_device_domain_traffic_bytes_total` добавляет наблюдённые домен, сервис, источник и направление для каждого устройства. Dashboard показывает top services за выбранный период и таблицу доменов с фильтром по устройствам.
|
`harbor_traffic_bytes_total` содержит общий накопленный объём по источникам Gateway/Proxy. `harbor_device_traffic_bytes_total` содержит upload/download по стабильному `device_id`; пользовательское название и текущий IP находятся в `harbor_device_info`. `harbor_device_domain_traffic_bytes_total` добавляет наблюдённые домен, сервис, источник и направление для каждого устройства.
|
||||||
|
|
||||||
|
Dashboard отделяет текущую скорость от значений за выбранный период и накопленных счётчиков. Top-10 устройств за период отсортирован по убыванию и служит переходом к детализации. Один общий фильтр по стабильному `device_id` управляет графиками устройства, сервисами и доменами; в списке он подписан как `name · ip`. Нулевые ranking-строки скрыты, а domain table показывает только сервис, домен и трафик. Автообновление настроено на 30 секунд; freshness предупреждает после 60 секунд и считает данные устаревшими после 120 секунд.
|
||||||
|
|
||||||
Domain counters снимаются с активных соединений sing-box раз в 2 секунды и хранятся в памяти dataplane до его перезапуска; историю и retention хранит Prometheus. YouTube и OpenAI / ChatGPT объединяются по известным связанным доменам в label `service`, остальные значения сохраняют домен как имя сервиса. Новые domain series сверх process limit складываются в `_other`. В метрики не входит физический трафик вне Harbor, устройства с policy Direct, ECH/IP-only соединения, соединения между двумя снимками и байты после последнего снимка перед закрытием или quota провайдера.
|
Domain counters снимаются с активных соединений sing-box раз в 2 секунды и хранятся в памяти dataplane до его перезапуска; историю и retention хранит Prometheus. YouTube и OpenAI / ChatGPT объединяются по известным связанным доменам в label `service`, остальные значения сохраняют домен как имя сервиса. Новые domain series сверх process limit складываются в `_other`. В метрики не входит физический трафик вне Harbor, устройства с policy Direct, ECH/IP-only соединения, соединения между двумя снимками и байты после последнего снимка перед закрытием или quota провайдера.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"annotations": {
|
"annotations": {
|
||||||
"list": []
|
"list": []
|
||||||
},
|
},
|
||||||
"description": "Harbor-accounted Gateway and Proxy traffic. Direct and physical device traffic outside Harbor is not included.",
|
"description": "Трафик, учтённый Harbor Gateway и Proxy. Физический и Direct-трафик вне Harbor не включён.",
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"fiscalYearStartMonth": 0,
|
"fiscalYearStartMonth": 0,
|
||||||
"graphTooltip": 1,
|
"graphTooltip": 1,
|
||||||
@@ -20,66 +20,31 @@
|
|||||||
"links": [],
|
"links": [],
|
||||||
"panels": [
|
"panels": [
|
||||||
{
|
{
|
||||||
"datasource": {
|
"collapsed": false,
|
||||||
"type": "prometheus",
|
|
||||||
"uid": "${DS_PROMETHEUS}"
|
|
||||||
},
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "continuous-GrYlRd"
|
|
||||||
},
|
|
||||||
"decimals": 1,
|
|
||||||
"unit": "bytes"
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 6,
|
"h": 1,
|
||||||
"w": 6,
|
"w": 24,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0
|
"y": 0
|
||||||
},
|
},
|
||||||
"id": 1,
|
"id": 10,
|
||||||
"options": {
|
"panels": [],
|
||||||
"colorMode": "value",
|
"title": "Обзор",
|
||||||
"graphMode": "area",
|
"type": "row"
|
||||||
"justifyMode": "auto",
|
|
||||||
"orientation": "auto",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"textMode": "auto",
|
|
||||||
"wideLayout": true
|
|
||||||
},
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"editorMode": "code",
|
|
||||||
"expr": "sum(harbor_traffic_bytes_total)",
|
|
||||||
"instant": true,
|
|
||||||
"legendFormat": "Учтено Harbor",
|
|
||||||
"range": false,
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Всего учтено Harbor",
|
|
||||||
"type": "stat"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "prometheus",
|
"type": "prometheus",
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
|
"description": "Сколько секунд прошло с последнего успешного обновления. До 60 секунд — штатно, 60–119 — задержка, от 120 — данные устарели.",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "thresholds"
|
"mode": "thresholds"
|
||||||
},
|
},
|
||||||
"decimals": 0,
|
"decimals": 0,
|
||||||
|
"noValue": "Нет данных",
|
||||||
"thresholds": {
|
"thresholds": {
|
||||||
"mode": "absolute",
|
"mode": "absolute",
|
||||||
"steps": [
|
"steps": [
|
||||||
@@ -89,7 +54,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"color": "orange",
|
"color": "orange",
|
||||||
"value": 45
|
"value": 60
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"color": "red",
|
"color": "red",
|
||||||
@@ -104,8 +69,8 @@
|
|||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 6,
|
"h": 6,
|
||||||
"w": 6,
|
"w": 6,
|
||||||
"x": 6,
|
"x": 0,
|
||||||
"y": 0
|
"y": 1
|
||||||
},
|
},
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"options": {
|
"options": {
|
||||||
@@ -141,7 +106,7 @@
|
|||||||
"refId": "B"
|
"refId": "B"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Свежесть счётчиков",
|
"title": "Свежесть данных сейчас",
|
||||||
"type": "stat"
|
"type": "stat"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -149,6 +114,7 @@
|
|||||||
"type": "prometheus",
|
"type": "prometheus",
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
|
"description": "Текущая суммарная скорость всех устройств, сглаженная за последние 5 минут.",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"color": {
|
"color": {
|
||||||
@@ -156,7 +122,7 @@
|
|||||||
},
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"drawStyle": "line",
|
"drawStyle": "line",
|
||||||
"fillOpacity": 12,
|
"fillOpacity": 10,
|
||||||
"lineInterpolation": "smooth",
|
"lineInterpolation": "smooth",
|
||||||
"lineWidth": 2,
|
"lineWidth": 2,
|
||||||
"pointSize": 4,
|
"pointSize": 4,
|
||||||
@@ -168,15 +134,47 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"decimals": 1,
|
"decimals": 1,
|
||||||
|
"noValue": "Нет данных",
|
||||||
"unit": "Bps"
|
"unit": "Bps"
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Скачивание"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "blue",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Отправка"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "orange",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 9,
|
"h": 6,
|
||||||
"w": 12,
|
"w": 18,
|
||||||
"x": 12,
|
"x": 6,
|
||||||
"y": 0
|
"y": 1
|
||||||
},
|
},
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"options": {
|
"options": {
|
||||||
@@ -197,13 +195,20 @@
|
|||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "sum by (source) (rate(harbor_traffic_bytes_total[5m]))",
|
"expr": "sum(rate(harbor_device_traffic_bytes_total{direction=\"download\"}[5m]))",
|
||||||
"legendFormat": "{{source}}",
|
"legendFormat": "Скачивание",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"editorMode": "code",
|
||||||
|
"expr": "sum(rate(harbor_device_traffic_bytes_total{direction=\"upload\"}[5m]))",
|
||||||
|
"legendFormat": "Отправка",
|
||||||
|
"range": true,
|
||||||
|
"refId": "B"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Общая скорость за 5 минут",
|
"title": "Скорость сейчас, средняя за 5 минут",
|
||||||
"type": "timeseries"
|
"type": "timeseries"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -211,59 +216,7 @@
|
|||||||
"type": "prometheus",
|
"type": "prometheus",
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"fieldConfig": {
|
"description": "Устройства с наибольшим учтённым трафиком в текущем диапазоне времени. Нажмите название, чтобы выбрать устройство в детализации ниже.",
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"decimals": 1,
|
|
||||||
"unit": "bytes"
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 7,
|
|
||||||
"w": 12,
|
|
||||||
"x": 0,
|
|
||||||
"y": 9
|
|
||||||
},
|
|
||||||
"id": 4,
|
|
||||||
"options": {
|
|
||||||
"displayMode": "gradient",
|
|
||||||
"maxVizHeight": 300,
|
|
||||||
"minVizHeight": 16,
|
|
||||||
"minVizWidth": 8,
|
|
||||||
"namePlacement": "auto",
|
|
||||||
"orientation": "horizontal",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"showUnfilled": true,
|
|
||||||
"sizing": "auto",
|
|
||||||
"valueMode": "color"
|
|
||||||
},
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"editorMode": "code",
|
|
||||||
"expr": "sum by (source) (harbor_traffic_bytes_total)",
|
|
||||||
"instant": true,
|
|
||||||
"legendFormat": "{{source}}",
|
|
||||||
"range": false,
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Объём по источникам",
|
|
||||||
"type": "bargauge"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": {
|
|
||||||
"type": "prometheus",
|
|
||||||
"uid": "${DS_PROMETHEUS}"
|
|
||||||
},
|
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"color": {
|
"color": {
|
||||||
@@ -277,15 +230,47 @@
|
|||||||
"inspect": false
|
"inspect": false
|
||||||
},
|
},
|
||||||
"decimals": 1,
|
"decimals": 1,
|
||||||
|
"noValue": "Нет данных",
|
||||||
"unit": "bytes"
|
"unit": "bytes"
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "device_id"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "custom.hidden",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "name"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "links",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"targetBlank": false,
|
||||||
|
"title": "Выбрать устройство",
|
||||||
|
"url": "${__url_time_range}&var-device_id=${__data.fields[\"device_id\"]}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 7,
|
"h": 9,
|
||||||
"w": 12,
|
"w": 24,
|
||||||
"x": 12,
|
"x": 0,
|
||||||
"y": 9
|
"y": 7
|
||||||
},
|
},
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"options": {
|
"options": {
|
||||||
@@ -299,12 +284,18 @@
|
|||||||
],
|
],
|
||||||
"show": false
|
"show": false
|
||||||
},
|
},
|
||||||
"showHeader": true
|
"showHeader": true,
|
||||||
|
"sortBy": [
|
||||||
|
{
|
||||||
|
"desc": true,
|
||||||
|
"displayName": "За период"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "sum by (device_id) (harbor_device_traffic_bytes_total) * on (device_id) group_left (name, ip) max by (device_id, name, ip) (harbor_device_info)",
|
"expr": "topk(10, (sum by (device_id) (increase(harbor_device_traffic_bytes_total[$__range])) > 0) * on (device_id) group_left (name, ip) max by (device_id, name, ip) (harbor_device_info))",
|
||||||
"format": "table",
|
"format": "table",
|
||||||
"instant": true,
|
"instant": true,
|
||||||
"legendFormat": "{{name}} · {{ip}}",
|
"legendFormat": "{{name}} · {{ip}}",
|
||||||
@@ -312,22 +303,22 @@
|
|||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Устройства по объёму",
|
"title": "Топ-10 устройств за выбранный период",
|
||||||
"transformations": [
|
"transformations": [
|
||||||
{
|
{
|
||||||
"id": "organize",
|
"id": "organize",
|
||||||
"options": {
|
"options": {
|
||||||
"excludeByName": {
|
"excludeByName": {
|
||||||
"Time": true,
|
"Time": true
|
||||||
"device_id": true
|
|
||||||
},
|
},
|
||||||
"indexByName": {
|
"indexByName": {
|
||||||
"name": 0,
|
"name": 0,
|
||||||
"ip": 1,
|
"ip": 1,
|
||||||
"Value": 2
|
"Value": 2,
|
||||||
|
"device_id": 3
|
||||||
},
|
},
|
||||||
"renameByName": {
|
"renameByName": {
|
||||||
"Value": "Учтено",
|
"Value": "За период",
|
||||||
"ip": "IP",
|
"ip": "IP",
|
||||||
"name": "Устройство"
|
"name": "Устройство"
|
||||||
}
|
}
|
||||||
@@ -336,11 +327,25 @@
|
|||||||
],
|
],
|
||||||
"type": "table"
|
"type": "table"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 16
|
||||||
|
},
|
||||||
|
"id": 11,
|
||||||
|
"panels": [],
|
||||||
|
"title": "Устройство",
|
||||||
|
"type": "row"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "prometheus",
|
"type": "prometheus",
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
|
"description": "Скорость одного выбранного устройства по Gateway/Proxy и upload/download, средняя за последние 5 минут.",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"color": {
|
"color": {
|
||||||
@@ -360,6 +365,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"decimals": 1,
|
"decimals": 1,
|
||||||
|
"noValue": "Нет данных за период",
|
||||||
"unit": "Bps"
|
"unit": "Bps"
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": []
|
||||||
@@ -368,7 +374,7 @@
|
|||||||
"h": 9,
|
"h": 9,
|
||||||
"w": 24,
|
"w": 24,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 16
|
"y": 17
|
||||||
},
|
},
|
||||||
"id": 6,
|
"id": 6,
|
||||||
"options": {
|
"options": {
|
||||||
@@ -390,13 +396,13 @@
|
|||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "sum by (device_id, source, direction) (rate(harbor_device_traffic_bytes_total[5m])) * on (device_id) group_left (name, ip) max by (device_id, name, ip) (harbor_device_info{name=~\"$device\"})",
|
"expr": "sum by (source, direction) (rate(harbor_device_traffic_bytes_total{device_id=\"$device_id\"}[5m]))",
|
||||||
"legendFormat": "{{name}} · {{source}} · {{direction}}",
|
"legendFormat": "{{source}} · {{direction}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Скорость выбранных устройств",
|
"title": "${device_id:text}: скорость сейчас",
|
||||||
"type": "timeseries"
|
"type": "timeseries"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -404,12 +410,14 @@
|
|||||||
"type": "prometheus",
|
"type": "prometheus",
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
|
"description": "Накопленные счётчики выбранного устройства за всё время хранения Harbor. Нулевые серии скрыты.",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "palette-classic"
|
"mode": "palette-classic"
|
||||||
},
|
},
|
||||||
"decimals": 1,
|
"decimals": 1,
|
||||||
|
"noValue": "Трафик не учтён",
|
||||||
"unit": "bytes"
|
"unit": "bytes"
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": []
|
||||||
@@ -418,11 +426,11 @@
|
|||||||
"h": 7,
|
"h": 7,
|
||||||
"w": 24,
|
"w": 24,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 25
|
"y": 26
|
||||||
},
|
},
|
||||||
"id": 7,
|
"id": 7,
|
||||||
"options": {
|
"options": {
|
||||||
"displayMode": "gradient",
|
"displayMode": "basic",
|
||||||
"maxVizHeight": 300,
|
"maxVizHeight": 300,
|
||||||
"minVizHeight": 16,
|
"minVizHeight": 16,
|
||||||
"minVizWidth": 8,
|
"minVizWidth": 8,
|
||||||
@@ -435,47 +443,62 @@
|
|||||||
"fields": "",
|
"fields": "",
|
||||||
"values": false
|
"values": false
|
||||||
},
|
},
|
||||||
"showUnfilled": true,
|
"showUnfilled": false,
|
||||||
"sizing": "auto",
|
"sizing": "auto",
|
||||||
"valueMode": "color"
|
"valueMode": "text"
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "sum by (device_id, source, direction) (harbor_device_traffic_bytes_total) * on (device_id) group_left (name, ip) max by (device_id, name, ip) (harbor_device_info{name=~\"$device\"})",
|
"expr": "sum by (source, direction) (harbor_device_traffic_bytes_total{device_id=\"$device_id\"}) > 0",
|
||||||
"instant": true,
|
"instant": true,
|
||||||
"legendFormat": "{{name}} · {{source}} · {{direction}}",
|
"legendFormat": "{{source}} · {{direction}}",
|
||||||
"range": false,
|
"range": false,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Объём выбранных устройств",
|
"title": "${device_id:text}: накоплено",
|
||||||
"type": "bargauge"
|
"type": "bargauge"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 33
|
||||||
|
},
|
||||||
|
"id": 12,
|
||||||
|
"panels": [],
|
||||||
|
"title": "Сервисы и домены",
|
||||||
|
"type": "row"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "prometheus",
|
"type": "prometheus",
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
|
"description": "Сервисы выбранного устройства по объёму domain traffic в текущем диапазоне времени. Нулевые серии скрыты.",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "palette-classic"
|
"mode": "palette-classic"
|
||||||
},
|
},
|
||||||
"decimals": 1,
|
"decimals": 1,
|
||||||
|
"noValue": "Нет данных за период",
|
||||||
"unit": "bytes"
|
"unit": "bytes"
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": []
|
||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 9,
|
"h": 10,
|
||||||
"w": 10,
|
"w": 10,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 32
|
"y": 34
|
||||||
},
|
},
|
||||||
"id": 8,
|
"id": 8,
|
||||||
"options": {
|
"options": {
|
||||||
"displayMode": "gradient",
|
"displayMode": "basic",
|
||||||
"maxVizHeight": 300,
|
"maxVizHeight": 300,
|
||||||
"minVizHeight": 16,
|
"minVizHeight": 16,
|
||||||
"minVizWidth": 8,
|
"minVizWidth": 8,
|
||||||
@@ -488,21 +511,21 @@
|
|||||||
"fields": "",
|
"fields": "",
|
||||||
"values": false
|
"values": false
|
||||||
},
|
},
|
||||||
"showUnfilled": true,
|
"showUnfilled": false,
|
||||||
"sizing": "auto",
|
"sizing": "auto",
|
||||||
"valueMode": "color"
|
"valueMode": "text"
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "topk(15, sum by (service) (increase(harbor_device_domain_traffic_bytes_total[$__range])))",
|
"expr": "topk(10, sum by (service) (increase(harbor_device_domain_traffic_bytes_total{device_id=\"$device_id\"}[$__range])) > 0)",
|
||||||
"instant": true,
|
"instant": true,
|
||||||
"legendFormat": "{{service}}",
|
"legendFormat": "{{service}}",
|
||||||
"range": false,
|
"range": false,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Сервисы за выбранный период",
|
"title": "${device_id:text}: сервисы за период",
|
||||||
"type": "bargauge"
|
"type": "bargauge"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -510,6 +533,7 @@
|
|||||||
"type": "prometheus",
|
"type": "prometheus",
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
|
"description": "Домены выбранного устройства в текущем диапазоне времени. Устройство и IP не повторяются в строках.",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"color": {
|
"color": {
|
||||||
@@ -523,15 +547,16 @@
|
|||||||
"inspect": false
|
"inspect": false
|
||||||
},
|
},
|
||||||
"decimals": 1,
|
"decimals": 1,
|
||||||
|
"noValue": "Нет данных за период",
|
||||||
"unit": "bytes"
|
"unit": "bytes"
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": []
|
||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 9,
|
"h": 10,
|
||||||
"w": 14,
|
"w": 14,
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 32
|
"y": 34
|
||||||
},
|
},
|
||||||
"id": 9,
|
"id": 9,
|
||||||
"options": {
|
"options": {
|
||||||
@@ -545,46 +570,198 @@
|
|||||||
],
|
],
|
||||||
"show": false
|
"show": false
|
||||||
},
|
},
|
||||||
"showHeader": true
|
"showHeader": true,
|
||||||
|
"sortBy": [
|
||||||
|
{
|
||||||
|
"desc": true,
|
||||||
|
"displayName": "Трафик за период"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "topk(50, sum by (device_id, service, domain) (increase(harbor_device_domain_traffic_bytes_total[$__range])) * on (device_id) group_left (name, ip) max by (device_id, name, ip) (harbor_device_info{name=~\"$device\"}))",
|
"expr": "topk(15, sum by (service, domain) (increase(harbor_device_domain_traffic_bytes_total{device_id=\"$device_id\"}[$__range])) > 0)",
|
||||||
"format": "table",
|
"format": "table",
|
||||||
"instant": true,
|
"instant": true,
|
||||||
"legendFormat": "{{name}} · {{service}} · {{domain}}",
|
"legendFormat": "{{service}} · {{domain}}",
|
||||||
"range": false,
|
"range": false,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Домены выбранных устройств",
|
"title": "${device_id:text}: домены за период",
|
||||||
"transformations": [
|
"transformations": [
|
||||||
{
|
{
|
||||||
"id": "organize",
|
"id": "organize",
|
||||||
"options": {
|
"options": {
|
||||||
"excludeByName": {
|
"excludeByName": {
|
||||||
"Time": true,
|
"Time": true
|
||||||
"device_id": true
|
|
||||||
},
|
},
|
||||||
"indexByName": {
|
"indexByName": {
|
||||||
"name": 0,
|
"service": 0,
|
||||||
"ip": 1,
|
"domain": 1,
|
||||||
"service": 2,
|
"Value": 2
|
||||||
"domain": 3,
|
|
||||||
"Value": 4
|
|
||||||
},
|
},
|
||||||
"renameByName": {
|
"renameByName": {
|
||||||
"Value": "Трафик за период",
|
"Value": "Трафик за период",
|
||||||
"domain": "Домен",
|
"domain": "Домен",
|
||||||
"ip": "IP",
|
|
||||||
"name": "Устройство",
|
|
||||||
"service": "Сервис"
|
"service": "Сервис"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"type": "table"
|
"type": "table"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 44
|
||||||
|
},
|
||||||
|
"id": 13,
|
||||||
|
"panels": [],
|
||||||
|
"title": "Техническая детализация",
|
||||||
|
"type": "row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "${DS_PROMETHEUS}"
|
||||||
|
},
|
||||||
|
"description": "Накопленный объём всех счётчиков Harbor за всё время хранения.",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"decimals": 1,
|
||||||
|
"noValue": "Нет данных",
|
||||||
|
"unit": "bytes"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 8,
|
||||||
|
"x": 0,
|
||||||
|
"y": 45
|
||||||
|
},
|
||||||
|
"id": 1,
|
||||||
|
"options": {
|
||||||
|
"colorMode": "none",
|
||||||
|
"graphMode": "none",
|
||||||
|
"justifyMode": "auto",
|
||||||
|
"orientation": "auto",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"textMode": "auto",
|
||||||
|
"wideLayout": true
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"editorMode": "code",
|
||||||
|
"expr": "sum(harbor_traffic_bytes_total)",
|
||||||
|
"instant": true,
|
||||||
|
"legendFormat": "Учтено Harbor",
|
||||||
|
"range": false,
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Накоплено: весь Harbor",
|
||||||
|
"type": "stat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "${DS_PROMETHEUS}"
|
||||||
|
},
|
||||||
|
"description": "Накопленные счётчики за всё время хранения отдельно для Gateway и Proxy.",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"decimals": 1,
|
||||||
|
"noValue": "Нет данных",
|
||||||
|
"unit": "bytes"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "gateway"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "blue",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "proxy"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "purple",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 16,
|
||||||
|
"x": 8,
|
||||||
|
"y": 45
|
||||||
|
},
|
||||||
|
"id": 4,
|
||||||
|
"options": {
|
||||||
|
"displayMode": "basic",
|
||||||
|
"maxVizHeight": 300,
|
||||||
|
"minVizHeight": 16,
|
||||||
|
"minVizWidth": 8,
|
||||||
|
"namePlacement": "auto",
|
||||||
|
"orientation": "horizontal",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"showUnfilled": false,
|
||||||
|
"sizing": "auto",
|
||||||
|
"valueMode": "text"
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"editorMode": "code",
|
||||||
|
"expr": "sum by (source) (harbor_traffic_bytes_total) > 0",
|
||||||
|
"instant": true,
|
||||||
|
"legendFormat": "{{source}}",
|
||||||
|
"range": false,
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Накоплено: источники",
|
||||||
|
"type": "bargauge"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"refresh": "30s",
|
"refresh": "30s",
|
||||||
@@ -597,29 +774,25 @@
|
|||||||
"templating": {
|
"templating": {
|
||||||
"list": [
|
"list": [
|
||||||
{
|
{
|
||||||
"allValue": ".*",
|
"current": {},
|
||||||
"current": {
|
|
||||||
"selected": true,
|
|
||||||
"text": "Все",
|
|
||||||
"value": "$__all"
|
|
||||||
},
|
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "prometheus",
|
"type": "prometheus",
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"definition": "label_values(harbor_device_info, name)",
|
"definition": "query_result(label_join(max by (device_id, name, ip) (harbor_device_info), \"display\", \" · \", \"name\", \"ip\"))",
|
||||||
|
"description": "Одно устройство для графиков, сервисов и доменов ниже. Значение фильтра — стабильный device_id.",
|
||||||
"hide": 0,
|
"hide": 0,
|
||||||
"includeAll": true,
|
"includeAll": false,
|
||||||
"label": "Устройство",
|
"label": "Устройство",
|
||||||
"multi": true,
|
"multi": false,
|
||||||
"name": "device",
|
"name": "device_id",
|
||||||
"options": [],
|
"options": [],
|
||||||
"query": {
|
"query": {
|
||||||
"query": "label_values(harbor_device_info, name)",
|
"query": "query_result(label_join(max by (device_id, name, ip) (harbor_device_info), \"display\", \" · \", \"name\", \"ip\"))",
|
||||||
"refId": "PrometheusVariableQueryEditor-VariableQuery"
|
"refId": "PrometheusVariableQueryEditor-VariableQuery"
|
||||||
},
|
},
|
||||||
"refresh": 1,
|
"refresh": 1,
|
||||||
"regex": "",
|
"regex": "/display=\"(?<text>[^\"]+)\"|device_id=\"(?<value>[^\"]+)\"/g",
|
||||||
"skipUrlSync": false,
|
"skipUrlSync": false,
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
"type": "query"
|
"type": "query"
|
||||||
@@ -630,10 +803,19 @@
|
|||||||
"from": "now-24h",
|
"from": "now-24h",
|
||||||
"to": "now"
|
"to": "now"
|
||||||
},
|
},
|
||||||
"timepicker": {},
|
"timepicker": {
|
||||||
|
"refresh_intervals": [
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h"
|
||||||
|
]
|
||||||
|
},
|
||||||
"timezone": "browser",
|
"timezone": "browser",
|
||||||
"title": "Harbor Gateway Traffic",
|
"title": "Harbor Gateway: трафик",
|
||||||
"uid": "harbor-gateway-traffic",
|
"uid": "harbor-gateway-traffic",
|
||||||
"version": 2,
|
"version": 3,
|
||||||
"weekStart": "monday"
|
"weekStart": "monday"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export const HARBOR_VERSIONS = Object.freeze({
|
export const HARBOR_VERSIONS = Object.freeze({
|
||||||
macClient: '0.20.0',
|
macClient: '0.20.1',
|
||||||
gatewayClient: '0.21.0',
|
gatewayClient: '0.21.1',
|
||||||
gatewayBackend: '0.21.0',
|
gatewayBackend: '0.21.0',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export function instructionBlocks({ isGateway, host, port, controlHost }) {
|
|||||||
paragraphs: [
|
paragraphs: [
|
||||||
`Prometheus забирает накопленные Harbor counters с http://${controlHost}/metrics. Ручка читает готовый snapshot и не запускает новый сбор трафика.`,
|
`Prometheus забирает накопленные Harbor counters с http://${controlHost}/metrics. Ручка читает готовый snapshot и не запускает новый сбор трафика.`,
|
||||||
'Harbor обновляет traffic snapshot раз в 15 секунд, поэтому начальный scrape interval и refresh dashboard в 30 секунд не создают лишних одинаковых выборок.',
|
'Harbor обновляет traffic snapshot раз в 15 секунд, поэтому начальный scrape interval и refresh dashboard в 30 секунд не создают лишних одинаковых выборок.',
|
||||||
'Dashboard показывает общий объём и скорость, top services за выбранный период и домены выбранных устройств. В статистику входит только трафик, учтённый Harbor.',
|
'Dashboard показывает свежесть и текущую скорость, Top-10 устройств за период и детализацию одного выбранного устройства по стабильному ID. Сервисы и домены используют тот же фильтр; накопленные счётчики вынесены в техническую секцию.',
|
||||||
],
|
],
|
||||||
steps: [
|
steps: [
|
||||||
'Добавьте блок ниже в prometheus.yml и перезагрузите Prometheus.',
|
'Добавьте блок ниже в prometheus.yml и перезагрузите Prometheus.',
|
||||||
|
|||||||
@@ -36,21 +36,42 @@ test('metrics route reads the current snapshot before static fallback', () => {
|
|||||||
assert.doesNotMatch(server.slice(metricsRoute, staticFallback), /deviceInventory\.refresh\(/);
|
assert.doesNotMatch(server.slice(metricsRoute, staticFallback), /deviceInventory\.refresh\(/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Grafana dashboard covers global and named per-device traffic', () => {
|
test('Grafana dashboard is organized around period ranking and one stable device selection', () => {
|
||||||
const expressions = dashboard.panels.flatMap((panel) => panel.targets || []).map(({ expr }) => expr).filter(Boolean);
|
const expressions = dashboard.panels.flatMap((panel) => panel.targets || []).map(({ expr }) => expr).filter(Boolean);
|
||||||
const titles = dashboard.panels.map(({ title }) => title);
|
const titles = dashboard.panels.map(({ title }) => title);
|
||||||
|
const deviceVariable = dashboard.templating.list[0];
|
||||||
|
const freshness = dashboard.panels.find(({ id }) => id === 2);
|
||||||
|
const topDevices = dashboard.panels.find(({ id }) => id === 5);
|
||||||
|
const domains = dashboard.panels.find(({ id }) => id === 9);
|
||||||
|
|
||||||
assert.equal(dashboard.title, 'Harbor Gateway Traffic');
|
assert.equal(dashboard.title, 'Harbor Gateway: трафик');
|
||||||
assert.ok(titles.includes('Всего учтено Harbor'));
|
assert.equal(dashboard.refresh, '30s');
|
||||||
assert.ok(titles.includes('Общая скорость за 5 минут'));
|
assert.deepEqual(titles.filter((title) => ['Обзор', 'Устройство', 'Сервисы и домены', 'Техническая детализация'].includes(title)), [
|
||||||
assert.ok(titles.includes('Устройства по объёму'));
|
'Обзор',
|
||||||
assert.ok(titles.includes('Скорость выбранных устройств'));
|
'Устройство',
|
||||||
assert.ok(titles.includes('Сервисы за выбранный период'));
|
'Сервисы и домены',
|
||||||
assert.ok(titles.includes('Домены выбранных устройств'));
|
'Техническая детализация',
|
||||||
|
]);
|
||||||
|
assert.ok(titles.includes('Скорость сейчас, средняя за 5 минут'));
|
||||||
|
assert.ok(titles.includes('Топ-10 устройств за выбранный период'));
|
||||||
|
assert.ok(titles.includes('${device_id:text}: накоплено'));
|
||||||
|
assert.ok(titles.includes('${device_id:text}: сервисы за период'));
|
||||||
|
assert.ok(titles.includes('${device_id:text}: домены за период'));
|
||||||
assert.ok(expressions.some((expression) => expression.includes('harbor_traffic_bytes_total')));
|
assert.ok(expressions.some((expression) => expression.includes('harbor_traffic_bytes_total')));
|
||||||
assert.ok(expressions.some((expression) => expression.includes('harbor_device_traffic_bytes_total')));
|
assert.ok(expressions.some((expression) => expression.includes('harbor_device_traffic_bytes_total')));
|
||||||
assert.ok(expressions.some((expression) => expression.includes('harbor_device_domain_traffic_bytes_total[$__range]')));
|
assert.ok(expressions.some((expression) => expression.includes('harbor_device_domain_traffic_bytes_total{device_id="$device_id"}[$__range]')));
|
||||||
assert.ok(expressions.some((expression) => expression.includes('harbor_domain_traffic_last_observed_timestamp_seconds')));
|
assert.ok(expressions.some((expression) => expression.includes('harbor_domain_traffic_last_observed_timestamp_seconds')));
|
||||||
assert.ok(expressions.some((expression) => expression.includes('group_left (name, ip)')));
|
assert.match(topDevices.targets[0].expr, /^topk\(10,[\s\S]*increase\(harbor_device_traffic_bytes_total\[\$__range\]\)[\s\S]*> 0[\s\S]*group_left \(name, ip\)/);
|
||||||
assert.equal(dashboard.templating.list[0].query.query, 'label_values(harbor_device_info, name)');
|
assert.equal(topDevices.options.sortBy[0].desc, true);
|
||||||
|
assert.match(topDevices.fieldConfig.overrides[1].properties[0].value[0].url, /var-device_id=\$\{__data\.fields/);
|
||||||
|
assert.deepEqual(freshness.fieldConfig.defaults.thresholds.steps.map(({ value }) => value), [null, 60, 120]);
|
||||||
|
assert.equal(freshness.fieldConfig.defaults.noValue, 'Нет данных');
|
||||||
|
assert.equal(deviceVariable.name, 'device_id');
|
||||||
|
assert.equal(deviceVariable.multi, false);
|
||||||
|
assert.equal(deviceVariable.includeAll, false);
|
||||||
|
assert.match(deviceVariable.query.query, /query_result\(label_join\(/);
|
||||||
|
assert.match(deviceVariable.regex, /<text>/);
|
||||||
|
assert.match(deviceVariable.regex, /<value>/);
|
||||||
|
assert.deepEqual(domains.transformations[0].options.indexByName, { service: 0, domain: 1, Value: 2 });
|
||||||
|
assert.match(domains.targets[0].expr, /^topk\(15,[\s\S]*> 0\)$/);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user