summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--f3s/argocd-apps/prometheus.yaml126
-rw-r--r--f3s/prometheus/manifests/additional-scrape-configs-secret.yaml29
-rw-r--r--f3s/prometheus/manifests/epimetheus-dashboard.yaml481
-rw-r--r--f3s/prometheus/manifests/freebsd-recording-rules.yaml44
-rw-r--r--f3s/prometheus/manifests/grafana-datasources-configmap.yaml76
-rw-r--r--f3s/prometheus/manifests/grafana-restart-hook.yaml76
-rw-r--r--f3s/prometheus/manifests/openbsd-recording-rules.yaml38
-rw-r--r--f3s/prometheus/manifests/persistent-volumes.yaml54
-rw-r--r--f3s/prometheus/manifests/zfs-dashboards.yaml38
-rw-r--r--f3s/prometheus/manifests/zfs-recording-rules.yaml108
10 files changed, 1070 insertions, 0 deletions
diff --git a/f3s/argocd-apps/prometheus.yaml b/f3s/argocd-apps/prometheus.yaml
new file mode 100644
index 0000000..4265168
--- /dev/null
+++ b/f3s/argocd-apps/prometheus.yaml
@@ -0,0 +1,126 @@
+apiVersion: argoproj.io/v1alpha1
+kind: Application
+metadata:
+ name: prometheus
+ namespace: cicd
+ finalizers:
+ - resources-finalizer.argocd.argoproj.io
+spec:
+ project: default
+
+ sources:
+ # Source 1: Upstream Helm chart from prometheus-community
+ - repoURL: https://prometheus-community.github.io/helm-charts
+ chart: kube-prometheus-stack
+ targetRevision: 55.5.0
+ helm:
+ releaseName: prometheus
+ valuesObject:
+ kubeEtcd:
+ enabled: true
+ endpoints:
+ - 192.168.2.120
+ - 192.168.2.121
+ - 192.168.2.122
+ service:
+ enabled: true
+ port: 2381
+ targetPort: 2381
+
+ kubeControllerManager:
+ enabled: true
+ endpoints:
+ - 192.168.2.120
+ - 192.168.2.121
+ - 192.168.2.122
+ service:
+ enabled: true
+ port: 10257
+ targetPort: 10257
+ serviceMonitor:
+ enabled: true
+ https: true
+ insecureSkipVerify: true
+
+ prometheus:
+ prometheusSpec:
+ additionalArgs:
+ - name: web.enable-remote-write-receiver
+ value: ""
+ - name: web.enable-admin-api
+ value: ""
+ enableFeatures:
+ - exemplar-storage
+ - otlp-write-receiver
+ additionalScrapeConfigs: []
+ tsdb:
+ outOfOrderTimeWindow: 744h # 31 days
+ additionalScrapeConfigsSecret:
+ enabled: true
+ name: additional-scrape-configs
+ key: additional-scrape-configs.yaml
+ storageSpec:
+ volumeClaimTemplate:
+ spec:
+ storageClassName: ""
+ accessModes: ["ReadWriteOnce"]
+ resources:
+ requests:
+ storage: 10Gi
+ selector:
+ matchLabels:
+ type: local
+ app: prometheus
+
+ grafana:
+ persistence:
+ enabled: true
+ type: pvc
+ existingClaim: "grafana-data-pvc"
+
+ initChownData:
+ enabled: false
+
+ podSecurityContext:
+ fsGroup: 911
+ runAsUser: 911
+ runAsGroup: 911
+
+ # Disable sidecar-based datasource provisioning
+ sidecar:
+ datasources:
+ enabled: false
+
+ # Mount datasources ConfigMap directly to provisioning directory
+ extraVolumes:
+ - name: datasources-volume
+ configMap:
+ name: grafana-datasources-all
+
+ extraVolumeMounts:
+ - name: datasources-volume
+ mountPath: /etc/grafana/provisioning/datasources
+ readOnly: true
+
+ # Source 2: Additional manifests from Git repository
+ - repoURL: https://codeberg.org/snonux/conf.git
+ targetRevision: master
+ path: f3s/prometheus/manifests
+
+ destination:
+ server: https://kubernetes.default.svc
+ namespace: monitoring
+
+ syncPolicy:
+ automated:
+ prune: false # Manual pruning for safety on complex stack
+ selfHeal: true
+ syncOptions:
+ - CreateNamespace=false
+ - ServerSideApply=true
+ retry:
+ limit: 3
+ backoff:
+ duration: 10s
+ factor: 2
+ maxDuration: 3m
diff --git a/f3s/prometheus/manifests/additional-scrape-configs-secret.yaml b/f3s/prometheus/manifests/additional-scrape-configs-secret.yaml
new file mode 100644
index 0000000..a2d9534
--- /dev/null
+++ b/f3s/prometheus/manifests/additional-scrape-configs-secret.yaml
@@ -0,0 +1,29 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: additional-scrape-configs
+ namespace: monitoring
+ annotations:
+ argocd.argoproj.io/sync-wave: "1"
+type: Opaque
+stringData:
+ additional-scrape-configs.yaml: |
+ - job_name: 'node-exporter'
+ static_configs:
+ - targets:
+ - '192.168.2.130:9100' # f0 via WireGuard
+ - '192.168.2.131:9100' # f1 via WireGuard
+ - '192.168.2.132:9100' # f2 via WireGuard
+ labels:
+ os: freebsd
+ - targets:
+ - '192.168.2.110:9100' # blowfish via WireGuard
+ - '192.168.2.111:9100' # fishfinger via WireGuard
+ labels:
+ os: openbsd
+
+ - job_name: 'pushgateway'
+ honor_labels: true
+ static_configs:
+ - targets:
+ - 'pushgateway.monitoring.svc.cluster.local:9091'
diff --git a/f3s/prometheus/manifests/epimetheus-dashboard.yaml b/f3s/prometheus/manifests/epimetheus-dashboard.yaml
new file mode 100644
index 0000000..2d5d68a
--- /dev/null
+++ b/f3s/prometheus/manifests/epimetheus-dashboard.yaml
@@ -0,0 +1,481 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: epimetheus-dashboard
+ annotations:
+ argocd.argoproj.io/sync-wave: "4"
+ namespace: monitoring
+ labels:
+ grafana_dashboard: '1'
+ app.kubernetes.io/instance: prometheus
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/part-of: kube-prometheus-stack
+ release: prometheus
+data:
+ epimetheus.json: |-
+ {
+ "id": null,
+ "uid": "epimetheus-test",
+ "title": "Epimetheus Test Metrics",
+ "tags": [
+ "epimetheus",
+ "test",
+ "demo"
+ ],
+ "timezone": "browser",
+ "schemaVersion": 38,
+ "version": 1,
+ "refresh": "10s",
+ "panels": [
+ {
+ "id": 0,
+ "gridPos": {
+ "h": 6,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "type": "timeseries",
+ "title": "Data Timeline - All Metrics Over Time",
+ "description": "Shows when metrics data is available - helps identify realtime vs historic data",
+ "targets": [
+ {
+ "expr": "epimetheus_test_requests_total",
+ "legendFormat": "Requests Total",
+ "refId": "A"
+ },
+ {
+ "expr": "epimetheus_test_active_connections",
+ "legendFormat": "Active Connections",
+ "refId": "B"
+ },
+ {
+ "expr": "epimetheus_test_temperature_celsius",
+ "legendFormat": "Temperature",
+ "refId": "C"
+ }
+ ],
+ "fieldConfig": {
+ "defaults": {
+ "custom": {
+ "drawStyle": "line",
+ "lineInterpolation": "linear",
+ "barAlignment": 0,
+ "lineWidth": 1,
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "spanNulls": false,
+ "showPoints": "auto",
+ "pointSize": 5,
+ "stacking": {
+ "mode": "none",
+ "group": "A"
+ },
+ "axisPlacement": "auto",
+ "axisLabel": "",
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "hideFrom": {
+ "tooltip": false,
+ "viz": false,
+ "legend": false
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "color": {
+ "mode": "palette-classic"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "value": null,
+ "color": "green"
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ },
+ "legend": {
+ "showLegend": true,
+ "displayMode": "list",
+ "placement": "bottom",
+ "calcs": []
+ }
+ }
+ },
+ {
+ "id": 1,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 6
+ },
+ "type": "graph",
+ "title": "Request Rate",
+ "targets": [
+ {
+ "expr": "rate(epimetheus_test_requests_total[5m])",
+ "legendFormat": "Requests/sec",
+ "refId": "A"
+ }
+ ],
+ "yaxes": [
+ {
+ "format": "reqps",
+ "label": "Requests per Second"
+ },
+ {
+ "format": "short"
+ }
+ ],
+ "xaxis": {
+ "mode": "time"
+ },
+ "lines": true,
+ "fill": 1,
+ "linewidth": 2,
+ "pointradius": 5,
+ "tooltip": {
+ "shared": true
+ }
+ },
+ {
+ "id": 2,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 6
+ },
+ "type": "stat",
+ "title": "Total Requests",
+ "targets": [
+ {
+ "expr": "epimetheus_test_requests_total",
+ "legendFormat": "Total",
+ "refId": "A"
+ }
+ ],
+ "options": {
+ "graphMode": "area",
+ "colorMode": "value",
+ "justifyMode": "auto",
+ "textMode": "auto",
+ "reduceOptions": {
+ "values": false,
+ "calcs": [
+ "lastNotNull"
+ ]
+ }
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "unit": "short"
+ }
+ }
+ },
+ {
+ "id": 3,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 14
+ },
+ "type": "gauge",
+ "title": "Active Connections",
+ "targets": [
+ {
+ "expr": "epimetheus_test_active_connections",
+ "legendFormat": "Connections",
+ "refId": "A"
+ }
+ ],
+ "options": {
+ "showThresholdLabels": false,
+ "showThresholdMarkers": true
+ },
+ "fieldConfig": {
+ "defaults": {
+ "min": 0,
+ "max": 100,
+ "color": {
+ "mode": "thresholds"
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "value": 0,
+ "color": "green"
+ },
+ {
+ "value": 50,
+ "color": "yellow"
+ },
+ {
+ "value": 80,
+ "color": "red"
+ }
+ ]
+ },
+ "unit": "short"
+ }
+ }
+ },
+ {
+ "id": 4,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 14
+ },
+ "type": "gauge",
+ "title": "Temperature (°C)",
+ "targets": [
+ {
+ "expr": "epimetheus_test_temperature_celsius",
+ "legendFormat": "°C",
+ "refId": "A"
+ }
+ ],
+ "options": {
+ "showThresholdLabels": false,
+ "showThresholdMarkers": true
+ },
+ "fieldConfig": {
+ "defaults": {
+ "min": 0,
+ "max": 50,
+ "color": {
+ "mode": "thresholds"
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "value": 0,
+ "color": "blue"
+ },
+ {
+ "value": 20,
+ "color": "green"
+ },
+ {
+ "value": 30,
+ "color": "yellow"
+ },
+ {
+ "value": 35,
+ "color": "red"
+ }
+ ]
+ },
+ "unit": "celsius"
+ }
+ }
+ },
+ {
+ "id": 5,
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 22
+ },
+ "type": "graph",
+ "title": "Request Duration Histogram",
+ "targets": [
+ {
+ "expr": "histogram_quantile(0.50, rate(epimetheus_test_request_duration_seconds_bucket[5m]))",
+ "legendFormat": "p50",
+ "refId": "A"
+ },
+ {
+ "expr": "histogram_quantile(0.90, rate(epimetheus_test_request_duration_seconds_bucket[5m]))",
+ "legendFormat": "p90",
+ "refId": "B"
+ },
+ {
+ "expr": "histogram_quantile(0.99, rate(epimetheus_test_request_duration_seconds_bucket[5m]))",
+ "legendFormat": "p99",
+ "refId": "C"
+ }
+ ],
+ "yaxes": [
+ {
+ "format": "s",
+ "label": "Duration"
+ },
+ {
+ "format": "short"
+ }
+ ],
+ "xaxis": {
+ "mode": "time"
+ },
+ "lines": true,
+ "fill": 1,
+ "linewidth": 2,
+ "tooltip": {
+ "shared": true
+ }
+ },
+ {
+ "id": 6,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 30
+ },
+ "type": "stat",
+ "title": "Average Request Duration",
+ "targets": [
+ {
+ "expr": "rate(epimetheus_test_request_duration_seconds_sum[5m]) / rate(epimetheus_test_request_duration_seconds_count[5m])",
+ "legendFormat": "Avg Duration",
+ "refId": "A"
+ }
+ ],
+ "options": {
+ "graphMode": "area",
+ "colorMode": "value",
+ "textMode": "auto",
+ "reduceOptions": {
+ "values": false,
+ "calcs": [
+ "lastNotNull"
+ ]
+ }
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "unit": "s",
+ "decimals": 3
+ }
+ }
+ },
+ {
+ "id": 7,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 30
+ },
+ "type": "bargauge",
+ "title": "Jobs Processed by Type",
+ "targets": [
+ {
+ "expr": "sum by (job_type) (epimetheus_test_jobs_processed_total)",
+ "legendFormat": "{{job_type}}",
+ "refId": "A"
+ }
+ ],
+ "options": {
+ "orientation": "horizontal",
+ "displayMode": "gradient",
+ "showUnfilled": true
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "unit": "short"
+ }
+ }
+ },
+ {
+ "id": 8,
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 38
+ },
+ "type": "table",
+ "title": "Jobs Status Breakdown",
+ "targets": [
+ {
+ "expr": "epimetheus_test_jobs_processed_total",
+ "legendFormat": "",
+ "refId": "A",
+ "format": "table",
+ "instant": true
+ }
+ ],
+ "options": {
+ "showHeader": true
+ },
+ "fieldConfig": {
+ "defaults": {},
+ "overrides": []
+ },
+ "transformations": [
+ {
+ "id": "organize",
+ "options": {
+ "excludeByName": {
+ "Time": true,
+ "__name__": true,
+ "instance": true,
+ "job": true
+ },
+ "indexByName": {},
+ "renameByName": {
+ "job_type": "Job Type",
+ "status": "Status",
+ "Value": "Count"
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m"
+ ]
+ },
+ "templating": {
+ "list": []
+ },
+ "annotations": {
+ "list": []
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 1,
+ "links": [],
+ "liveNow": false,
+ "style": "dark"
+ }
diff --git a/f3s/prometheus/manifests/freebsd-recording-rules.yaml b/f3s/prometheus/manifests/freebsd-recording-rules.yaml
new file mode 100644
index 0000000..501a54c
--- /dev/null
+++ b/f3s/prometheus/manifests/freebsd-recording-rules.yaml
@@ -0,0 +1,44 @@
+apiVersion: monitoring.coreos.com/v1
+kind: PrometheusRule
+metadata:
+ name: freebsd-memory-rules
+ namespace: monitoring
+ annotations:
+ argocd.argoproj.io/sync-wave: "3"
+ labels:
+ release: prometheus
+spec:
+ groups:
+ # FreeBSD memory metrics mapped to Linux-compatible names
+ - name: freebsd-memory
+ rules:
+ # Synthetic MemTotal for FreeBSD (maps to size_bytes)
+ - record: node_memory_MemTotal_bytes
+ expr: node_memory_size_bytes{os="freebsd"}
+ labels:
+ os: freebsd
+
+ # Synthetic MemAvailable for FreeBSD
+ # Available = free + inactive + cache (approximation)
+ - record: node_memory_MemAvailable_bytes
+ expr: node_memory_free_bytes{os="freebsd"} + node_memory_inactive_bytes{os="freebsd"} + node_memory_cache_bytes{os="freebsd"}
+ labels:
+ os: freebsd
+
+ # Synthetic MemFree for FreeBSD
+ - record: node_memory_MemFree_bytes
+ expr: node_memory_free_bytes{os="freebsd"}
+ labels:
+ os: freebsd
+
+ # Synthetic Buffers for FreeBSD
+ - record: node_memory_Buffers_bytes
+ expr: node_memory_buffer_bytes{os="freebsd"}
+ labels:
+ os: freebsd
+
+ # Synthetic Cached for FreeBSD
+ - record: node_memory_Cached_bytes
+ expr: node_memory_cache_bytes{os="freebsd"}
+ labels:
+ os: freebsd
diff --git a/f3s/prometheus/manifests/grafana-datasources-configmap.yaml b/f3s/prometheus/manifests/grafana-datasources-configmap.yaml
new file mode 100644
index 0000000..5b03597
--- /dev/null
+++ b/f3s/prometheus/manifests/grafana-datasources-configmap.yaml
@@ -0,0 +1,76 @@
+# Unified Grafana Datasources ConfigMap
+# This ConfigMap contains all datasources (Prometheus, Alertmanager, Loki, Tempo)
+# and is directly mounted to /etc/grafana/provisioning/datasources/
+#
+# Benefits:
+# - Simple and direct (no sidecar complexity)
+# - Grafana provisioning runs on startup
+# - Field order preserved exactly as written
+# - All datasources in one place
+# - Fully IaC compliant
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: grafana-datasources-all
+ namespace: monitoring
+ labels:
+ app.kubernetes.io/name: grafana
+ app.kubernetes.io/component: datasources
+ annotations:
+ argocd.argoproj.io/sync-wave: "1"
+data:
+ datasources.yaml: |
+ apiVersion: 1
+ datasources:
+ - name: Prometheus
+ type: prometheus
+ uid: prometheus
+ url: http://prometheus-kube-prometheus-prometheus.monitoring:9090/
+ access: proxy
+ isDefault: true
+ editable: false
+ jsonData:
+ httpMethod: POST
+ timeInterval: 30s
+ - name: Alertmanager
+ type: alertmanager
+ uid: alertmanager
+ url: http://prometheus-kube-prometheus-alertmanager.monitoring:9093/
+ access: proxy
+ editable: false
+ jsonData:
+ handleGrafanaManagedAlerts: false
+ implementation: prometheus
+ - name: Loki
+ type: loki
+ uid: loki
+ url: http://loki.monitoring.svc.cluster.local:3100
+ access: proxy
+ isDefault: false
+ editable: false
+ jsonData:
+ maxLines: 1000
+ - name: Tempo
+ type: tempo
+ uid: tempo
+ url: http://tempo.monitoring.svc.cluster.local:3200
+ access: proxy
+ isDefault: false
+ editable: false
+ jsonData:
+ httpMethod: GET
+ tracesToLogsV2:
+ datasourceUid: loki
+ spanStartTimeShift: -1h
+ spanEndTimeShift: 1h
+ tracesToMetrics:
+ datasourceUid: prometheus
+ serviceMap:
+ datasourceUid: prometheus
+ nodeGraph:
+ enabled: true
+ search:
+ hide: false
+ lokiSearch:
+ datasourceUid: loki
diff --git a/f3s/prometheus/manifests/grafana-restart-hook.yaml b/f3s/prometheus/manifests/grafana-restart-hook.yaml
new file mode 100644
index 0000000..34d5867
--- /dev/null
+++ b/f3s/prometheus/manifests/grafana-restart-hook.yaml
@@ -0,0 +1,76 @@
+# ServiceAccount, Role, and RoleBinding for Grafana restart hook
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: grafana-restart-sa
+ namespace: monitoring
+ annotations:
+ argocd.argoproj.io/sync-wave: "0"
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: grafana-restart-role
+ namespace: monitoring
+ annotations:
+ argocd.argoproj.io/sync-wave: "0"
+rules:
+ - apiGroups: [""]
+ resources: ["pods"]
+ verbs: ["list", "delete"]
+ - apiGroups: ["apps"]
+ resources: ["deployments"]
+ verbs: ["get", "list"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: grafana-restart-rb
+ namespace: monitoring
+ annotations:
+ argocd.argoproj.io/sync-wave: "0"
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: grafana-restart-role
+subjects:
+ - kind: ServiceAccount
+ name: grafana-restart-sa
+ namespace: monitoring
+---
+# PostSync hook job that restarts Grafana pods to reload datasources
+# This replicates the behavior of the manual "kubectl delete pod" step in the original Justfile
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: grafana-restart-hook
+ namespace: monitoring
+ annotations:
+ argocd.argoproj.io/hook: PostSync
+ argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
+ argocd.argoproj.io/sync-wave: "10"
+spec:
+ template:
+ spec:
+ serviceAccountName: grafana-restart-sa
+ restartPolicy: OnFailure
+ containers:
+ - name: kubectl
+ image: bitnami/kubectl:latest
+ command:
+ - /bin/sh
+ - -c
+ - |
+ echo "Waiting for Grafana deployment to be ready..."
+ kubectl wait --for=condition=available --timeout=300s deployment/prometheus-grafana -n monitoring || true
+
+ echo "Restarting Grafana pods to reload datasources..."
+ kubectl delete pod -n monitoring -l app.kubernetes.io/name=grafana --ignore-not-found=true
+
+ echo "Grafana restart triggered successfully"
+ echo "Waiting for Grafana to become ready again..."
+ sleep 10
+ kubectl wait --for=condition=ready --timeout=180s pod -n monitoring -l app.kubernetes.io/name=grafana || true
+
+ echo "Grafana restart complete"
+ backoffLimit: 2
diff --git a/f3s/prometheus/manifests/openbsd-recording-rules.yaml b/f3s/prometheus/manifests/openbsd-recording-rules.yaml
new file mode 100644
index 0000000..7d88d4a
--- /dev/null
+++ b/f3s/prometheus/manifests/openbsd-recording-rules.yaml
@@ -0,0 +1,38 @@
+apiVersion: monitoring.coreos.com/v1
+kind: PrometheusRule
+metadata:
+ name: openbsd-memory-rules
+ namespace: monitoring
+ annotations:
+ argocd.argoproj.io/sync-wave: "3"
+ labels:
+ release: prometheus
+spec:
+ groups:
+ # OpenBSD memory metrics mapped to Linux-compatible names
+ - name: openbsd-memory
+ rules:
+ # Synthetic MemTotal for OpenBSD (maps to size_bytes)
+ - record: node_memory_MemTotal_bytes
+ expr: node_memory_size_bytes{os="openbsd"}
+ labels:
+ os: openbsd
+
+ # Synthetic MemAvailable for OpenBSD
+ # Available = free + inactive + cache (approximation)
+ - record: node_memory_MemAvailable_bytes
+ expr: node_memory_free_bytes{os="openbsd"} + node_memory_inactive_bytes{os="openbsd"} + node_memory_cache_bytes{os="openbsd"}
+ labels:
+ os: openbsd
+
+ # Synthetic MemFree for OpenBSD
+ - record: node_memory_MemFree_bytes
+ expr: node_memory_free_bytes{os="openbsd"}
+ labels:
+ os: openbsd
+
+ # Synthetic Cached for OpenBSD
+ - record: node_memory_Cached_bytes
+ expr: node_memory_cache_bytes{os="openbsd"}
+ labels:
+ os: openbsd
diff --git a/f3s/prometheus/manifests/persistent-volumes.yaml b/f3s/prometheus/manifests/persistent-volumes.yaml
new file mode 100644
index 0000000..539ca3d
--- /dev/null
+++ b/f3s/prometheus/manifests/persistent-volumes.yaml
@@ -0,0 +1,54 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: prometheus-data-pv
+ labels:
+ type: local
+ app: prometheus
+ annotations:
+ argocd.argoproj.io/sync-wave: "0"
+spec:
+ capacity:
+ storage: 10Gi
+ volumeMode: Filesystem
+ accessModes:
+ - ReadWriteOnce
+ persistentVolumeReclaimPolicy: Retain
+ hostPath:
+ path: /data/nfs/k3svolumes/prometheus/data
+ type: Directory
+---
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: grafana-data-pv
+ annotations:
+ argocd.argoproj.io/sync-wave: "0"
+spec:
+ capacity:
+ storage: 2Gi
+ volumeMode: Filesystem
+ accessModes:
+ - ReadWriteOnce
+ persistentVolumeReclaimPolicy: Retain
+ hostPath:
+ path: /data/nfs/k3svolumes/grafana/data
+ type: Directory
+ claimRef:
+ namespace: monitoring
+ name: grafana-data-pvc
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: grafana-data-pvc
+ namespace: monitoring
+ annotations:
+ argocd.argoproj.io/sync-wave: "0"
+spec:
+ storageClassName: ""
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 2Gi
diff --git a/f3s/prometheus/manifests/zfs-dashboards.yaml b/f3s/prometheus/manifests/zfs-dashboards.yaml
new file mode 100644
index 0000000..7b41f09
--- /dev/null
+++ b/f3s/prometheus/manifests/zfs-dashboards.yaml
@@ -0,0 +1,38 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: freebsd-zfs-dashboards
+ annotations:
+ argocd.argoproj.io/sync-wave: "4"
+ namespace: monitoring
+ labels:
+ grafana_dashboard: '1'
+ app.kubernetes.io/instance: prometheus
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/part-of: kube-prometheus-stack
+ release: prometheus
+data:
+ freebsd-zfs.json: "{\n \"title\": \"FreeBSD ZFS\",\n \"uid\": \"freebsd-zfs\",\n \"timezone\": \"utc\",\n \"schemaVersion\": 38,\n \"refresh\": \"30s\",\n \"tags\": [\n \"zfs\",\n \"freebsd\",\n \"storage\"\n ],\n \"editable\": true,\n \"templating\": {\n \"list\": [\n {\n \"name\": \"datasource\",\n \"type\": \"datasource\",\n \"query\": \"prometheus\",\n \"hide\": 0\n },\n {\n \"name\": \"instance\",\n \"type\": \"query\",\n \"datasource\": {\n \"type\": \"prometheus\",\n \"uid\": \"${datasource}\"\n },\n \"query\": \"label_values(node_zfs_arcstats_size_bytes{os=\\\"freebsd\\\"}, instance)\",\n \"refresh\": 2,\n \"hide\": 0,\n \"label\": \"FreeBSD Server\"\n },\n {\n \"name\": \"pool\",\n \"type\": \"query\",\n \"datasource\": {\n \"type\": \"prometheus\",\n \"uid\": \"${datasource}\"\n },\n \
+ \ \"query\": \"label_values(zfs_pool_size_bytes{instance=\\\"$instance\\\"}, pool)\",\n \"refresh\": 2,\n \"hide\": 0,\n \"label\": \"ZFS Pool\",\n \"includeAll\": true,\n \"multi\": false\n }\n ]\n },\n \"panels\": [\n {\n \"type\": \"row\",\n \"collapsed\": false,\n \"title\": \"Pool Overview\",\n \"gridPos\": {\n \"h\": 1,\n \"w\": 24,\n \"x\": 0,\n \"y\": 0\n }\n },\n {\n \"type\": \"gauge\",\n \"title\": \"Pool Capacity\",\n \"gridPos\": {\n \"h\": 8,\n \"w\": 6,\n \"x\": 0,\n \"y\": 1\n },\n \"datasource\": {\n \"type\": \"prometheus\",\n \"uid\": \"${datasource}\"\n },\n \"targets\": [\n {\n \"expr\": \"zfs_pool_capacity_percent{instance=\\\"$instance\\\",pool=~\\\"$pool\\\"}\",\n \"legendFormat\": \"{{pool}}\"\n }\n ],\n \"fieldConfig\": {\n \
+ \ \"defaults\": {\n \"unit\": \"percent\",\n \"min\": 0,\n \"max\": 100,\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": 0\n },\n {\n \"color\": \"yellow\",\n \"value\": 70\n },\n {\n \"color\": \"red\",\n \"value\": 85\n }\n ]\n }\n }\n }\n },\n {\n \"type\": \"stat\",\n \"title\": \"Pool Health\",\n \"gridPos\": {\n \"h\": 4,\n \"w\": 6,\n \"x\": 6,\n \"y\": 1\n },\n \"datasource\": {\n \"type\": \"prometheus\",\n \"uid\": \"${datasource}\"\n },\n \"targets\": [\n {\n \"expr\": \"zfs_pool_health{instance=\\\"$instance\\\",pool=~\\\"$pool\\\"}\",\n \"legendFormat\": \"\
+ {{pool}}\"\n }\n ],\n \"options\": {\n \"reduceOptions\": {\n \"values\": false,\n \"calcs\": [\n \"lastNotNull\"\n ]\n },\n \"orientation\": \"auto\",\n \"textMode\": \"value_and_name\",\n \"colorMode\": \"background\",\n \"graphMode\": \"none\",\n \"justifyMode\": \"auto\"\n },\n \"fieldConfig\": {\n \"defaults\": {\n \"unit\": \"short\",\n \"mappings\": [\n {\n \"options\": {\n \"0\": {\n \"text\": \"ONLINE\",\n \"color\": \"green\"\n }\n },\n \"type\": \"value\"\n },\n {\n \"options\": {\n \"1\": {\n \"text\": \"DEGRADED\",\n \"color\": \"yellow\"\n }\n },\n \"type\": \"value\"\n },\n {\n\
+ \ \"options\": {\n \"2\": {\n \"text\": \"FAULTED\",\n \"color\": \"red\"\n }\n },\n \"type\": \"value\"\n }\n ],\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n },\n {\n \"color\": \"yellow\",\n \"value\": 1\n },\n {\n \"color\": \"red\",\n \"value\": 2\n }\n ]\n }\n },\n \"overrides\": []\n }\n },\n {\n \"type\": \"stat\",\n \"title\": \"Total Pool Size\",\n \"gridPos\": {\n \"h\": 4,\n \"w\": 6,\n \"x\": 12,\n \"y\": 1\n },\n \"datasource\": {\n \"type\": \"prometheus\",\n \"uid\": \"${datasource}\"\
+ \n },\n \"targets\": [\n {\n \"expr\": \"zfs_pool_size_bytes{instance=\\\"$instance\\\",pool=~\\\"$pool\\\"}\",\n \"legendFormat\": \"{{pool}}\"\n }\n ],\n \"options\": {\n \"reduceOptions\": {\n \"values\": false,\n \"calcs\": [\n \"lastNotNull\"\n ]\n },\n \"orientation\": \"auto\",\n \"textMode\": \"value_and_name\",\n \"colorMode\": \"value\",\n \"graphMode\": \"none\",\n \"justifyMode\": \"auto\"\n },\n \"fieldConfig\": {\n \"defaults\": {\n \"unit\": \"bytes\"\n },\n \"overrides\": []\n }\n },\n {\n \"type\": \"stat\",\n \"title\": \"Free Space\",\n \"gridPos\": {\n \"h\": 4,\n \"w\": 6,\n \"x\": 18,\n \"y\": 1\n },\n \"datasource\": {\n \"type\": \"prometheus\",\n \"uid\": \"${datasource}\"\n },\n \"targets\": [\n\
+ \ {\n \"expr\": \"zfs_pool_free_bytes{instance=\\\"$instance\\\",pool=~\\\"$pool\\\"}\",\n \"legendFormat\": \"{{pool}}\"\n }\n ],\n \"options\": {\n \"reduceOptions\": {\n \"values\": false,\n \"calcs\":