diff --git a/k3s/grafana/dashboards/homelab-monitoring.json b/k3s/grafana/dashboards/homelab-monitoring.json index 73ac33a..5dacc8a 100644 --- a/k3s/grafana/dashboards/homelab-monitoring.json +++ b/k3s/grafana/dashboards/homelab-monitoring.json @@ -18,7 +18,6 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 4, "links": [], "panels": [ { @@ -138,7 +137,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT\n time AS \"time\",\n (payload->>'usage')::float AS \"CPU Usage\"\nFROM system_metrics \nWHERE\n metric_type = 'cpu'\n AND host = 'server'\nORDER BY time;", + "rawSql": "SELECT\n time AS \"time\",\n host,\n (payload->>'usage')::float AS \"CPU Usage\"\nFROM system_metrics \nWHERE\n metric_type = 'cpu'\nORDER BY time;", "refId": "A", "sql": { "columns": [ @@ -159,6 +158,16 @@ } } ], + "transformations": [ + { + "id": "partitionByValues", + "options": { + "fields": [ + "host" + ] + } + } + ], "title": "CPU Usage", "type": "timeseries" }, @@ -250,7 +259,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT\n time AS \"time\",\n CASE\n WHEN\n LAG(time) OVER (ORDER BY time) IS NOT NULL\n AND EXTRACT(EPOCH FROM (time - LAG(time) OVER (ORDER BY time))) > 0\n THEN\n (\n ( (payload->'enp5s0'->>'rx_bytes')::bigint - LAG((payload->'enp5s0'->>'rx_bytes')::bigint) OVER (ORDER BY time) )\n * 8.0\n ) / (\n EXTRACT(EPOCH FROM (time - LAG(time) OVER (ORDER BY time))) * 1000000\n )\n ELSE NULL\n END AS \"Download (Mbps)\",\n CASE\n WHEN\n LAG(time) OVER (ORDER BY time) IS NOT NULL\n AND EXTRACT(EPOCH FROM (time - LAG(time) OVER (ORDER BY time))) > 0\n THEN\n (\n ( (payload->'enp5s0'->>'tx_bytes')::bigint - LAG((payload->'enp5s0'->>'tx_bytes')::bigint) OVER (ORDER BY time) )\n * 8.0\n ) / (\n EXTRACT(EPOCH FROM (time - LAG(time) OVER (ORDER BY time))) * 1000000\n )\n ELSE NULL\n END AS \"Upload (Mbps)\"\nFROM system_metrics \nWHERE\n metric_type = 'network'\n AND host = 'server'\nORDER BY time;", + "rawSql": "SELECT\n time AS \"time\",\n host,\n CASE\n WHEN\n LAG(time) OVER (PARTITION BY host ORDER BY time) IS NOT NULL\n AND EXTRACT(EPOCH FROM (time - LAG(time) OVER (PARTITION BY host ORDER BY time))) > 0\n THEN\n (\n ( COALESCE((payload->'enp5s0'->>'rx_bytes')::bigint, (payload->'eno1'->>'rx_bytes')::bigint) - LAG(COALESCE((payload->'enp5s0'->>'rx_bytes')::bigint, (payload->'eno1'->>'rx_bytes')::bigint)) OVER (PARTITION BY host ORDER BY time) )\n * 8.0\n ) / (\n EXTRACT(EPOCH FROM (time - LAG(time) OVER (PARTITION BY host ORDER BY time))) * 1000000\n )\n ELSE NULL\n END AS \"Download (Mbps)\",\n CASE\n WHEN\n LAG(time) OVER (PARTITION BY host ORDER BY time) IS NOT NULL\n AND EXTRACT(EPOCH FROM (time - LAG(time) OVER (PARTITION BY host ORDER BY time))) > 0\n THEN\n (\n ( COALESCE((payload->'enp5s0'->>'tx_bytes')::bigint, (payload->'eno1'->>'tx_bytes')::bigint) - LAG(COALESCE((payload->'enp5s0'->>'tx_bytes')::bigint, (payload->'eno1'->>'tx_bytes')::bigint)) OVER (PARTITION BY host ORDER BY time) )\n * 8.0\n ) / (\n EXTRACT(EPOCH FROM (time - LAG(time) OVER (PARTITION BY host ORDER BY time))) * 1000000\n )\n ELSE NULL\n END AS \"Upload (Mbps)\"\nFROM system_metrics \nWHERE\n metric_type = 'network'\nORDER BY time;", "refId": "A", "sql": { "columns": [ @@ -271,6 +280,16 @@ } } ], + "transformations": [ + { + "id": "partitionByValues", + "options": { + "fields": [ + "host" + ] + } + } + ], "title": "Network Usage", "type": "timeseries" }, @@ -362,7 +381,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT\n time AS \"time\",\n (payload->>'used_percent')::float AS \"Memory Used %\"\nFROM system_metrics \nWHERE\n metric_type = 'memory'\n AND host = 'server'\nORDER BY time;", + "rawSql": "SELECT\n time AS \"time\",\n host,\n (payload->>'used_percent')::float AS \"Memory Used %\"\nFROM system_metrics \nWHERE\n metric_type = 'memory'\nORDER BY time;", "refId": "A", "sql": { "columns": [ @@ -383,6 +402,16 @@ } } ], + "transformations": [ + { + "id": "partitionByValues", + "options": { + "fields": [ + "host" + ] + } + } + ], "title": "Memory Usage", "type": "timeseries" }, @@ -478,7 +507,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT\n time AS \"time\",\n (payload->'/'->>'used_percent')::float AS \"Disk Used %\"\nFROM system_metrics \nWHERE\n metric_type = 'disk'\n AND host = 'server'\nORDER BY time;", + "rawSql": "SELECT\n time AS \"time\",\n host,\n (payload->'/'->>'used_percent')::float AS \"Disk Used %\"\nFROM system_metrics \nWHERE\n metric_type = 'disk'\nORDER BY time;", "refId": "A", "sql": { "columns": [ @@ -499,6 +528,16 @@ } } ], + "transformations": [ + { + "id": "partitionByValues", + "options": { + "fields": [ + "host" + ] + } + } + ], "title": "Disk Usage", "type": "timeseries" }, @@ -589,7 +628,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT\n time AS \"time\",\n (payload->'core_temps'->>'core_0')::int AS \"Core 0\",\n (payload->'core_temps'->>'core_1')::int AS \"Core 1\",\n (payload->'core_temps'->>'core_2')::int AS \"Core 2\",\n (payload->'core_temps'->>'core_3')::int AS \"Core 3\"\nFROM system_metrics \nWHERE\n metric_type = 'cpu'\n AND host = 'server'\nORDER BY time;", + "rawSql": "SELECT\n time AS \"time\",\n host,\n (payload->'core_temps'->>'core_0')::int AS \"Core 0\",\n (payload->'core_temps'->>'core_1')::int AS \"Core 1\",\n (payload->'core_temps'->>'core_2')::int AS \"Core 2\",\n (payload->'core_temps'->>'core_3')::int AS \"Core 3\"\nFROM system_metrics \nWHERE\n metric_type = 'cpu'\nORDER BY time;", "refId": "A", "sql": { "columns": [ @@ -610,6 +649,16 @@ } } ], + "transformations": [ + { + "id": "partitionByValues", + "options": { + "fields": [ + "host" + ] + } + } + ], "title": "CPU Temperatures", "type": "timeseries" } @@ -628,6 +677,6 @@ "timezone": "utc", "title": "Homelab Monitoring", "uid": "adf2v2l", - "version": 19, + "version": 0, "weekStart": "" } diff --git a/k3s/grafana/dashboards/reading-analytics.json b/k3s/grafana/dashboards/reading-analytics.json index 5e245bb..e0da8d7 100644 --- a/k3s/grafana/dashboards/reading-analytics.json +++ b/k3s/grafana/dashboards/reading-analytics.json @@ -18,7 +18,6 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 5, "links": [], "panels": [ { @@ -469,6 +468,6 @@ "timezone": "browser", "title": "Reading Analytics", "uid": "adq2m9x", - "version": 11, + "version": 0, "weekStart": "" } diff --git a/k3s/grafana/values.yaml b/k3s/grafana/values.yaml index f6a0154..99cf4f7 100644 --- a/k3s/grafana/values.yaml +++ b/k3s/grafana/values.yaml @@ -109,7 +109,7 @@ dashboardProviders: folder: '' type: file disableDeletion: false - editable: false + editable: true options: path: /var/lib/grafana/dashboards @@ -136,15 +136,10 @@ grafana.ini: check_for_updates: false extraVolumes: - - name: grafana-dashboards - configMap: - name: grafana-dashboards - name: tmp emptyDir: {} extraVolumeMounts: - - name: grafana-dashboards - mountPath: /var/lib/grafana/dashboards - name: tmp mountPath: /tmp diff --git a/tofu/.terraform.lock.hcl b/tofu/.terraform.lock.hcl index cdb2433..8a88157 100644 --- a/tofu/.terraform.lock.hcl +++ b/tofu/.terraform.lock.hcl @@ -1,6 +1,34 @@ # This file is maintained automatically by "tofu init". # Manual edits may be lost in future updates. +provider "registry.opentofu.org/grafana/grafana" { + version = "3.25.9" + constraints = "~> 3.0" + hashes = [ + "h1:m19AUS7s6DUOawfYD4OSEXVqxjp7OG/i8ArjcxCDIW0=", + "zh:052dd83cbf794d6a0c41ef6262286063d64c7b2107f3a6b1b81e679303072759", + "zh:117092dfc73619621eb48046934ef14c09e4419fc6d5bab35bff7549eac905fb", + "zh:153952d2f01812cada014905852c8a03fb68a204b98a533b81830e923bf69848", + "zh:16e9ada001398329ebe0c28be26cb3b71d8a30bd4cc41aa94ec182d671a06b58", + "zh:1eab3a56da8bb9478b0b32e87c3210b82d937d6a9586bd64046840f48486f7a1", + "zh:2e92818816fbac9e52a00d7c2c94b529b2fccdcfcd712c0fef8f4b340658c6ad", + "zh:3518f1973b3942ef380574a6765835204970f39c635b7e1dacde5e2c6a9abf16", + "zh:3ec8c054b91d44cb103ec82484e0974360ad9d4167e035cbfba0c858613f46f1", + "zh:418418c9d9e3e30e6e065d00956c56f78873546ad2c7d52a5fddb3ead207f4f7", + "zh:50009c4adbfe2174984f9c80627eb37c629b2b756acfd79f09b6dde7c6e58c60", + "zh:5482ea23403676f691d765d644519cbb632b1b5c1cfa6f3115705a0512d7d2db", + "zh:55b45e72ff7f186f91e3134eb925718b1e7b3e32aa5c810888f6be1db7242cf4", + "zh:577cc62c2e5e2b6c9eda49c316931318f106b7cbf2f59727d20a16c8a1902637", + "zh:63d43991044566879304f153ef3a1ae50cc4301508888a230462259aac97c7a2", + "zh:76074d9d200c59c707f570603b7b447b48d38ce57b53b54ee921c45b6521d816", + "zh:81141dd99b77fd091f1bd86fff15e6a25abfd92c825e77439457a6eba027f27c", + "zh:a3db02539d1417e326cee4e70ed77bce37bc4586f98b7067a0653e7b608d0cc9", + "zh:a53a7c400f3d8cef527897a95bd4e559e306703706d1dae509963d42423ef006", + "zh:d6b4f270ea3a27542fea8af45323428452563191929e13d3d92a64365dca1694", + "zh:fffaf801f26a13d5a600d6f39f508e7c07ed09e7dd35fc3541d8066236348b6f", + ] +} + provider "registry.opentofu.org/hashicorp/helm" { version = "2.17.0" constraints = "~> 2.13" diff --git a/tofu/grafana.tf b/tofu/grafana.tf index ccef382..0337d34 100644 --- a/tofu/grafana.tf +++ b/tofu/grafana.tf @@ -9,3 +9,19 @@ resource "helm_release" "grafana" { depends_on = [kubernetes_namespace.observability] } + +resource "grafana_folder" "observability" { + title = "Observability" +} + +resource "grafana_dashboard" "homelab_monitoring" { + folder = grafana_folder.observability.id + config_json = file("${path.module}/../k3s/grafana/dashboards/homelab-monitoring.json") + overwrite = true +} + +resource "grafana_dashboard" "reading_analytics" { + folder = grafana_folder.observability.id + config_json = file("${path.module}/../k3s/grafana/dashboards/reading-analytics.json") + overwrite = true +} diff --git a/tofu/providers.tf b/tofu/providers.tf index 3935b7a..d740a24 100644 --- a/tofu/providers.tf +++ b/tofu/providers.tf @@ -10,6 +10,10 @@ terraform { source = "hashicorp/helm" version = "~> 2.13" } + grafana = { + source = "grafana/grafana" + version = "~> 3.25" + } } # Step 9: MinIO state backend - uncomment AFTER MinIO pod is confirmed healthy (Step 4) @@ -37,3 +41,15 @@ provider "helm" { config_context = "default" } } + +data "kubernetes_secret" "grafana_admin" { + metadata { + name = "grafana-admin-secret" + namespace = var.observability_namespace + } +} + +provider "grafana" { + url = "http://localhost:30000" + auth = "${data.kubernetes_secret.grafana_admin.data["admin-user"]}:${data.kubernetes_secret.grafana_admin.data["admin-password"]}" +}