k8s: add Starlink dish prometheus exporter + dashboard
joshuasing/starlink_exporter 0.9.0 (Docker Hub; ghcr.io lags behind upstream releases). Targets the dish's gRPC management interface at 192.168.100.1:9200, reachable from cluster pods via 10.0.15.254 while the Starlink router is in bypass mode. Grafana dashboard "Starlink" covers throughput, PoP ping latency/drop, obstruction state + 24h total, GPS lock, and dish position/info — synced to /var/lib/grafana/dashboards/ by the existing grafana role.
This commit is contained in:
parent
8466e4590f
commit
04b3855874
5 changed files with 284 additions and 0 deletions
183
ansible/roles/grafana/files/dashboards/starlink.json
Normal file
183
ansible/roles/grafana/files/dashboards/starlink.json
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
{
|
||||
"uid": "starlink",
|
||||
"title": "Starlink",
|
||||
"tags": ["network", "starlink"],
|
||||
"schemaVersion": 38,
|
||||
"version": 1,
|
||||
"timezone": "",
|
||||
"refresh": "30s",
|
||||
"time": {"from": "now-6h", "to": "now"},
|
||||
"editable": true,
|
||||
"graphTooltip": 1,
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"name": "datasource",
|
||||
"label": "Datasource",
|
||||
"type": "datasource",
|
||||
"query": "prometheus",
|
||||
"current": {"text": "Prometheus", "value": "Prometheus", "selected": false},
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"multi": false,
|
||||
"refresh": 1,
|
||||
"skipUrlSync": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"panels": [
|
||||
{"type": "row", "id": 100, "title": "Overview", "collapsed": false, "gridPos": {"h": 1, "w": 24, "x": 0, "y": 0}},
|
||||
|
||||
{"type": "stat", "id": 1, "title": "Dish up", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 0, "y": 1},
|
||||
"fieldConfig": {"defaults": {
|
||||
"mappings": [{"type": "value", "options": {"0": {"text": "DOWN", "color": "red"}, "1": {"text": "UP", "color": "green"}}}],
|
||||
"color": {"mode": "thresholds"},
|
||||
"thresholds": {"mode": "absolute", "steps": [{"color":"red","value":null},{"color":"green","value":1}]}
|
||||
}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]}, "graphMode":"none","colorMode":"background","textMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_up", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 2, "title": "Uptime", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 4, "y": 1},
|
||||
"fieldConfig": {"defaults": {"unit":"s","color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"green","value":300}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]}, "graphMode":"area","colorMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_uptime_seconds", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 3, "title": "Downlink", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 8, "y": 1},
|
||||
"fieldConfig": {"defaults": {"unit":"bps","color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"green","value":1000000}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]}, "graphMode":"area","colorMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_downlink_throughput_bps", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 4, "title": "Uplink", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 12, "y": 1},
|
||||
"fieldConfig": {"defaults": {"unit":"bps","color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"green","value":100000}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]}, "graphMode":"area","colorMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_uplink_throughput_bps", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 5, "title": "PoP ping", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 16, "y": 1},
|
||||
"fieldConfig": {"defaults": {"unit":"s","color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":0.05},{"color":"orange","value":0.1},{"color":"red","value":0.2}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]}, "graphMode":"area","colorMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_pop_ping_latency_seconds", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 6, "title": "Power", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 20, "y": 1},
|
||||
"fieldConfig": {"defaults": {"unit":"watt","color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":80},{"color":"red","value":120}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]}, "graphMode":"area","colorMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_power_input_watts", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "row", "id": 101, "title": "Throughput", "collapsed": false, "gridPos": {"h": 1, "w": 24, "x": 0, "y": 5}},
|
||||
|
||||
{"type": "timeseries", "id": 10, "title": "Downlink (bps)", "datasource": "$datasource",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 6},
|
||||
"fieldConfig": {"defaults": {"unit":"bps","custom":{"lineWidth":1,"fillOpacity":15,"showPoints":"never"}}},
|
||||
"options": {"legend":{"showLegend":true,"placement":"bottom"},"tooltip":{"mode":"multi"}},
|
||||
"targets": [{"expr": "starlink_dish_downlink_throughput_bps", "legendFormat":"downlink", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "timeseries", "id": 11, "title": "Uplink (bps)", "datasource": "$datasource",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 6},
|
||||
"fieldConfig": {"defaults": {"unit":"bps","custom":{"lineWidth":1,"fillOpacity":15,"showPoints":"never"}}},
|
||||
"options": {"legend":{"showLegend":true,"placement":"bottom"},"tooltip":{"mode":"multi"}},
|
||||
"targets": [{"expr": "starlink_dish_uplink_throughput_bps", "legendFormat":"uplink", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "row", "id": 102, "title": "Latency & reliability", "collapsed": false, "gridPos": {"h": 1, "w": 24, "x": 0, "y": 14}},
|
||||
|
||||
{"type": "timeseries", "id": 20, "title": "PoP ping latency (s)", "datasource": "$datasource",
|
||||
"description": "Round-trip latency to the Starlink point-of-presence. Anything under 50ms is healthy; sustained >100ms suggests congestion or weather.",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 15},
|
||||
"fieldConfig": {"defaults": {"unit":"s","custom":{"lineWidth":1,"fillOpacity":10,"showPoints":"never"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":0.05},{"color":"red","value":0.1}]}}},
|
||||
"options": {"legend":{"showLegend":true,"placement":"bottom"},"tooltip":{"mode":"multi"}},
|
||||
"targets": [{"expr": "starlink_dish_pop_ping_latency_seconds", "legendFormat":"pop", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "timeseries", "id": 21, "title": "PoP ping drop ratio", "datasource": "$datasource",
|
||||
"description": "Fraction of pings dropped to the Starlink PoP — 0 is perfect, anything above 0 means packets are being lost upstream of the dish.",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 15},
|
||||
"fieldConfig": {"defaults": {"unit":"percentunit","min":0,"max":1,"custom":{"lineWidth":1,"fillOpacity":30,"showPoints":"never"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":0.01},{"color":"red","value":0.05}]}}},
|
||||
"options": {"legend":{"showLegend":true,"placement":"bottom"},"tooltip":{"mode":"multi"}},
|
||||
"targets": [{"expr": "starlink_dish_pop_ping_drop_ratio", "legendFormat":"drop", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "row", "id": 103, "title": "Obstruction & signal", "collapsed": false, "gridPos": {"h": 1, "w": 24, "x": 0, "y": 23}},
|
||||
|
||||
{"type": "stat", "id": 30, "title": "Currently obstructed", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 0, "y": 24},
|
||||
"fieldConfig": {"defaults": {
|
||||
"mappings":[{"type":"value","options":{"0":{"text":"CLEAR","color":"green"},"1":{"text":"OBSTRUCTED","color":"red"}}}],
|
||||
"color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"red","value":1}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]},"graphMode":"none","colorMode":"background","textMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_currently_obstructed", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 31, "title": "Obstruction fraction", "datasource": "$datasource",
|
||||
"description": "Fraction of the sky the dish believes is obstructed.",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 4, "y": 24},
|
||||
"fieldConfig": {"defaults": {"unit":"percentunit","min":0,"max":1,"color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":0.05},{"color":"red","value":0.15}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]},"graphMode":"area","colorMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_fraction_obstruction_ratio", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 32, "title": "Obstructed (last 24h)", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 8, "y": 24},
|
||||
"fieldConfig": {"defaults": {"unit":"s","color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":60},{"color":"red","value":600}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]},"graphMode":"area","colorMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_last_24h_obstructed_seconds", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 33, "title": "GPS valid", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 12, "y": 24},
|
||||
"fieldConfig": {"defaults": {
|
||||
"mappings":[{"type":"value","options":{"0":{"text":"NO FIX","color":"red"},"1":{"text":"LOCKED","color":"green"}}}],
|
||||
"color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"green","value":1}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]},"graphMode":"none","colorMode":"background","textMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_gps_valid", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 34, "title": "GPS satellites", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 16, "y": 24},
|
||||
"fieldConfig": {"defaults": {"color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"yellow","value":4},{"color":"green","value":8}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]},"graphMode":"area","colorMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_gps_satellites", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "stat", "id": 35, "title": "SNR above noise floor", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 4, "x": 20, "y": 24},
|
||||
"fieldConfig": {"defaults": {
|
||||
"mappings":[{"type":"value","options":{"0":{"text":"LOW","color":"red"},"1":{"text":"OK","color":"green"}}}],
|
||||
"color":{"mode":"thresholds"},
|
||||
"thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"green","value":1}]}}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]},"graphMode":"none","colorMode":"background","textMode":"value"},
|
||||
"targets": [{"expr": "starlink_dish_snr_above_noise_floor", "refId":"A", "datasource":"$datasource"}]},
|
||||
|
||||
{"type": "row", "id": 104, "title": "Dish position & info", "collapsed": true, "gridPos": {"h": 1, "w": 24, "x": 0, "y": 28},
|
||||
"panels": [
|
||||
{"type": "stat", "id": 40, "title": "Tilt", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 6, "x": 0, "y": 29},
|
||||
"fieldConfig": {"defaults": {"unit":"degree"}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]},"graphMode":"none"},
|
||||
"targets": [{"expr": "starlink_dish_tilt_angle_deg", "refId":"A", "datasource":"$datasource"}]},
|
||||
{"type": "stat", "id": 41, "title": "Boresight azimuth", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 6, "x": 6, "y": 29},
|
||||
"fieldConfig": {"defaults": {"unit":"degree"}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]},"graphMode":"none"},
|
||||
"targets": [{"expr": "starlink_dish_boresight_azimuth_deg", "refId":"A", "datasource":"$datasource"}]},
|
||||
{"type": "stat", "id": 42, "title": "Boresight elevation", "datasource": "$datasource",
|
||||
"gridPos": {"h": 4, "w": 6, "x": 12, "y": 29},
|
||||
"fieldConfig": {"defaults": {"unit":"degree"}},
|
||||
"options": {"reduceOptions":{"calcs":["lastNotNull"]},"graphMode":"none"},
|
||||
"targets": [{"expr": "starlink_dish_boresight_elevation_deg", "refId":"A", "datasource":"$datasource"}]},
|
||||
{"type": "table", "id": 43, "title": "Dish info", "datasource": "$datasource",
|
||||
"gridPos": {"h": 6, "w": 24, "x": 0, "y": 33},
|
||||
"options": {"showHeader": true},
|
||||
"targets": [{"expr": "starlink_dish_info", "refId":"A", "instant": true, "format":"table", "datasource":"$datasource"}],
|
||||
"transformations": [{"id":"organize","options":{"excludeByName":{"Time":true,"Value":true,"__name__":true,"app_kubernetes_io_instance":true,"app_kubernetes_io_name":true,"instance":true,"job":true,"namespace":true,"service":true}}}]}
|
||||
]}
|
||||
]
|
||||
}
|
||||
23
home/cluster/argocd/apps/starlink-exporter.yaml
Normal file
23
home/cluster/argocd/apps/starlink-exporter.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: starlink-exporter
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://codeberg.org/gmcintire/infra.git
|
||||
targetRevision: main
|
||||
path: home/cluster/starlink-exporter
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: monitoring
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
55
home/cluster/starlink-exporter/deployment.yaml
Normal file
55
home/cluster/starlink-exporter/deployment.yaml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: starlink-exporter
|
||||
namespace: monitoring
|
||||
labels:
|
||||
app.kubernetes.io/name: starlink-exporter
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: starlink-exporter
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: starlink-exporter
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
runAsGroup: 65532
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: exporter
|
||||
image: docker.io/joshuasing/starlink_exporter:0.9.0
|
||||
# Defaults already match this network: -dish 192.168.100.1:9200,
|
||||
# -listen :9451. Dish is reachable via 10.0.15.254 (home router)
|
||||
# while the Starlink router is in bypass mode.
|
||||
ports:
|
||||
- name: metrics
|
||||
containerPort: 9451
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 24Mi
|
||||
limits:
|
||||
memory: 96Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: metrics
|
||||
periodSeconds: 15
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: metrics
|
||||
periodSeconds: 30
|
||||
failureThreshold: 5
|
||||
5
home/cluster/starlink-exporter/kustomization.yaml
Normal file
5
home/cluster/starlink-exporter/kustomization.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
18
home/cluster/starlink-exporter/service.yaml
Normal file
18
home/cluster/starlink-exporter/service.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: starlink-exporter
|
||||
namespace: monitoring
|
||||
labels:
|
||||
app.kubernetes.io/name: starlink-exporter
|
||||
annotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "9451"
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: starlink-exporter
|
||||
ports:
|
||||
- name: metrics
|
||||
port: 9451
|
||||
targetPort: metrics
|
||||
protocol: TCP
|
||||
Loading…
Add table
Reference in a new issue