From 304a3fae12925d196bb44b4f971a555990941329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20D=C3=A9siles?= <1536672+cdesiles@users.noreply.github.com> Date: Sun, 6 Sep 2026 23:43:50 +0200 Subject: [PATCH] feat(monitoring): add nginx, postgres, valkey dashboards --- roles/grafana/files/nginx-dashboard.json | 84 ++++++++++++++++++++ roles/grafana/files/postgres-dashboard.json | 79 ++++++++++++++++++ roles/grafana/files/valkey-dashboard.json | 88 +++++++++++++++++++++ roles/grafana/tasks/main.yml | 19 +++++ 4 files changed, 270 insertions(+) create mode 100644 roles/grafana/files/nginx-dashboard.json create mode 100644 roles/grafana/files/postgres-dashboard.json create mode 100644 roles/grafana/files/valkey-dashboard.json diff --git a/roles/grafana/files/nginx-dashboard.json b/roles/grafana/files/nginx-dashboard.json new file mode 100644 index 0000000..2d7ebdc --- /dev/null +++ b/roles/grafana/files/nginx-dashboard.json @@ -0,0 +1,84 @@ +{ + "title": "Nginx", + "uid": "nginx-overview", + "tags": ["nginx", "reverse-proxy"], + "schemaVersion": 39, + "version": 1, + "refresh": "1m", + "time": { "from": "now-6h", "to": "now" }, + "timezone": "browser", + "editable": true, + "graphTooltip": 1, + "templating": { + "list": [ + { + "name": "instance", + "label": "Host", + "type": "query", + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "query": { "query": "label_values(up{job=\"nginx\"}, instance)", "refId": "A" }, + "refresh": 2, + "multi": false, + "includeAll": false, + "sort": 1, + "current": { "selected": false, "text": "", "value": "" } + } + ] + }, + "panels": [ + { + "id": 1, + "type": "stat", + "title": "Active Connections", + "gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "short", "color": { "mode": "thresholds" } }, "overrides": [] }, + "options": { "reduceOptions": { "values": false, "calcs": ["lastNotNull"], "fields": "" }, "colorMode": "none", "graphMode": "none", "textMode": "auto", "justifyMode": "auto", "orientation": "auto" }, + "targets": [ { "refId": "A", "expr": "nginx_connections_active{instance=\"$instance\"}" } ] + }, + { + "id": 2, + "type": "stat", + "title": "Requests (6h)", + "gridPos": { "h": 4, "w": 6, "x": 6, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "short", "color": { "mode": "thresholds" } }, "overrides": [] }, + "options": { "reduceOptions": { "values": false, "calcs": ["lastNotNull"], "fields": "" }, "colorMode": "none", "graphMode": "none", "textMode": "auto", "justifyMode": "auto", "orientation": "auto" }, + "targets": [ { "refId": "A", "expr": "sum(increase(nginx_http_requests_total{instance=\"$instance\"}[$__range]))" } ] + }, + { + "id": 3, + "type": "timeseries", + "title": "Request Rate", + "gridPos": { "h": 4, "w": 12, "x": 12, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "reqps", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ { "refId": "A", "expr": "sum(rate(nginx_http_requests_total{instance=\"$instance\"}[$__rate_interval]))", "legendFormat": "requests/s" } ] + }, + { + "id": 4, + "type": "timeseries", + "title": "Connection States", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "short", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ + { "refId": "A", "expr": "nginx_connections_reading{instance=\"$instance\"}", "legendFormat": "reading" }, + { "refId": "B", "expr": "nginx_connections_writing{instance=\"$instance\"}", "legendFormat": "writing" }, + { "refId": "C", "expr": "nginx_connections_waiting{instance=\"$instance\"}", "legendFormat": "waiting" } + ] + }, + { + "id": 5, + "type": "timeseries", + "title": "Connections Accepted / Handled", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "cps", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 10, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ + { "refId": "A", "expr": "rate(nginx_connections_accepted{instance=\"$instance\"}[$__rate_interval])", "legendFormat": "accepted" }, + { "refId": "B", "expr": "rate(nginx_connections_handled{instance=\"$instance\"}[$__rate_interval])", "legendFormat": "handled" } + ] + } + ] +} diff --git a/roles/grafana/files/postgres-dashboard.json b/roles/grafana/files/postgres-dashboard.json new file mode 100644 index 0000000..b7d7ab7 --- /dev/null +++ b/roles/grafana/files/postgres-dashboard.json @@ -0,0 +1,79 @@ +{ + "title": "PostgreSQL", + "uid": "postgres-overview", + "tags": ["postgres", "database"], + "schemaVersion": 39, + "version": 1, + "refresh": "1m", + "time": { "from": "now-6h", "to": "now" }, + "timezone": "browser", + "editable": true, + "graphTooltip": 1, + "templating": { + "list": [ + { + "name": "instance", + "label": "Host", + "type": "query", + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "query": { "query": "label_values(up{job=\"postgres\"}, instance)", "refId": "A" }, + "refresh": 2, + "multi": false, + "includeAll": false, + "sort": 1, + "current": { "selected": false, "text": "", "value": "" } + } + ] + }, + "panels": [ + { + "id": 1, + "type": "stat", + "title": "Up", + "gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "none", "color": { "mode": "thresholds" }, "thresholds": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": 1 } ] }, "mappings": [ { "type": "value", "options": { "0": { "text": "Down", "color": "red" }, "1": { "text": "Up", "color": "green" } } } ] }, "overrides": [] }, + "options": { "reduceOptions": { "values": false, "calcs": ["lastNotNull"], "fields": "" }, "colorMode": "background", "graphMode": "none", "textMode": "auto", "justifyMode": "auto", "orientation": "auto" }, + "targets": [ { "refId": "A", "expr": "pg_up{instance=\"$instance\"}" } ] + }, + { + "id": 2, + "type": "timeseries", + "title": "Active Connections", + "gridPos": { "h": 4, "w": 18, "x": 6, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "short", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ { "refId": "A", "expr": "sum by (datname) (pg_stat_database_numbackends{instance=\"$instance\"})", "legendFormat": "{{datname}}" } ] + }, + { + "id": 3, + "type": "timeseries", + "title": "Cache Hit Ratio", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "percentunit", "min": 0, "max": 1, "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ { "refId": "A", "expr": "sum(rate(pg_stat_database_blks_hit{instance=\"$instance\"}[$__rate_interval])) / (sum(rate(pg_stat_database_blks_hit{instance=\"$instance\"}[$__rate_interval])) + sum(rate(pg_stat_database_blks_read{instance=\"$instance\"}[$__rate_interval])))", "legendFormat": "hit ratio" } ] + }, + { + "id": 4, + "type": "timeseries", + "title": "Transactions", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "ops", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 10, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ + { "refId": "A", "expr": "rate(pg_stat_database_xact_commit{instance=\"$instance\"}[$__rate_interval])", "legendFormat": "commit" }, + { "refId": "B", "expr": "rate(pg_stat_database_xact_rollback{instance=\"$instance\"}[$__rate_interval])", "legendFormat": "rollback" } + ] + }, + { + "id": 5, + "type": "timeseries", + "title": "Deadlocks", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 12 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "ops", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ { "refId": "A", "expr": "rate(pg_stat_database_deadlocks{instance=\"$instance\"}[$__rate_interval])", "legendFormat": "{{datname}}" } ] + } + ] +} diff --git a/roles/grafana/files/valkey-dashboard.json b/roles/grafana/files/valkey-dashboard.json new file mode 100644 index 0000000..133ab48 --- /dev/null +++ b/roles/grafana/files/valkey-dashboard.json @@ -0,0 +1,88 @@ +{ + "title": "Valkey", + "uid": "valkey-overview", + "tags": ["valkey", "redis", "cache"], + "schemaVersion": 39, + "version": 1, + "refresh": "1m", + "time": { "from": "now-6h", "to": "now" }, + "timezone": "browser", + "editable": true, + "graphTooltip": 1, + "templating": { + "list": [ + { + "name": "instance", + "label": "Host", + "type": "query", + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "query": { "query": "label_values(up{job=\"valkey\"}, instance)", "refId": "A" }, + "refresh": 2, + "multi": false, + "includeAll": false, + "sort": 1, + "current": { "selected": false, "text": "", "value": "" } + } + ] + }, + "panels": [ + { + "id": 1, + "type": "stat", + "title": "Up", + "gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "none", "color": { "mode": "thresholds" }, "thresholds": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": 1 } ] }, "mappings": [ { "type": "value", "options": { "0": { "text": "Down", "color": "red" }, "1": { "text": "Up", "color": "green" } } } ] }, "overrides": [] }, + "options": { "reduceOptions": { "values": false, "calcs": ["lastNotNull"], "fields": "" }, "colorMode": "background", "graphMode": "none", "textMode": "auto", "justifyMode": "auto", "orientation": "auto" }, + "targets": [ { "refId": "A", "expr": "redis_up{instance=\"$instance\"}" } ] + }, + { + "id": 2, + "type": "timeseries", + "title": "Connected Clients", + "gridPos": { "h": 4, "w": 18, "x": 6, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "short", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ { "refId": "A", "expr": "redis_connected_clients{instance=\"$instance\"}", "legendFormat": "clients" } ] + }, + { + "id": 3, + "type": "timeseries", + "title": "Memory", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "bytes", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ + { "refId": "A", "expr": "redis_memory_used_bytes{instance=\"$instance\"}", "legendFormat": "used" }, + { "refId": "B", "expr": "redis_memory_max_bytes{instance=\"$instance\"}", "legendFormat": "max" } + ] + }, + { + "id": 4, + "type": "timeseries", + "title": "Ops/sec", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "ops", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ { "refId": "A", "expr": "rate(redis_commands_processed_total{instance=\"$instance\"}[$__rate_interval])", "legendFormat": "ops/s" } ] + }, + { + "id": 5, + "type": "timeseries", + "title": "Cache Hit Rate", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 12 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "percentunit", "min": 0, "max": 1, "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ { "refId": "A", "expr": "rate(redis_keyspace_hits_total{instance=\"$instance\"}[$__rate_interval]) / (rate(redis_keyspace_hits_total{instance=\"$instance\"}[$__rate_interval]) + rate(redis_keyspace_misses_total{instance=\"$instance\"}[$__rate_interval]))", "legendFormat": "hit rate" } ] + }, + { + "id": 6, + "type": "timeseries", + "title": "Evicted Keys", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 12 }, + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { "defaults": { "unit": "ops", "color": { "mode": "palette-classic" }, "custom": { "fillOpacity": 20, "lineWidth": 1, "drawStyle": "line", "showPoints": "never" } }, "overrides": [] }, + "targets": [ { "refId": "A", "expr": "rate(redis_evicted_keys_total{instance=\"$instance\"}[$__rate_interval])", "legendFormat": "evicted/s" } ] + } + ] +} diff --git a/roles/grafana/tasks/main.yml b/roles/grafana/tasks/main.yml index 6dc35f0..f9c41bd 100644 --- a/roles/grafana/tasks/main.yml +++ b/roles/grafana/tasks/main.yml @@ -209,6 +209,25 @@ mode: "0644" notify: Restart grafana +- name: Remove legacy combined services dashboard + ansible.builtin.file: + path: "{{ grafana_provisioning_path }}/dashboards/services.json" + state: absent + notify: Restart grafana + +- name: Deploy services dashboards (nginx, postgres, valkey) + ansible.builtin.copy: + src: "{{ item }}" + dest: "{{ grafana_provisioning_path }}/dashboards/{{ item }}" + owner: "{{ grafana_user }}" + group: "{{ grafana_group }}" + mode: "0644" + loop: + - nginx-dashboard.json + - postgres-dashboard.json + - valkey-dashboard.json + notify: Restart grafana + # Service management - name: Enable and start Grafana service ansible.builtin.systemd: