# Grafana for the dashboards. The InfluxDB datasource and the three MG4 dashboards are # provisioned from k8s/grafana/ (ConfigMaps generated by kustomization.yaml); the dashboards # are read-only in the UI — change the JSON in git instead. # # Grafana reads InfluxDB with a token that can only read the mg4 bucket. InfluxDB generates # every token but the admin one (a chosen value is ignored), so the init container makes it at # each start: it alone gets the admin token, creates a fresh read-only token, deletes the one # from the previous start, and leaves only the read-only token in a memory-backed volume for # Grafana. Nothing to do by hand, and the Grafana process never sees the admin token. apiVersion: v1 kind: PersistentVolumeClaim metadata: name: grafana-data namespace: mg4 labels: app.kubernetes.io/name: grafana app.kubernetes.io/part-of: mg4 spec: accessModes: ["ReadWriteOnce"] # No storageClassName: the cluster's default class is used. resources: requests: storage: 1Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: grafana namespace: mg4 labels: app.kubernetes.io/name: grafana app.kubernetes.io/part-of: mg4 spec: replicas: 1 strategy: type: Recreate # the SQLite database on the volume allows one writer selector: matchLabels: app.kubernetes.io/name: grafana template: metadata: labels: app.kubernetes.io/name: grafana app.kubernetes.io/part-of: mg4 spec: securityContext: runAsNonRoot: true runAsUser: 472 runAsGroup: 472 fsGroup: 472 seccompProfile: type: RuntimeDefault initContainers: - name: influx-read-token image: docker.io/library/influxdb:2.7.12-alpine command: - sh - -c - | set -eu desc="grafana read-only (made by the Grafana pod at start)" export INFLUX_CONFIGS_PATH=/tmp/influx-configs bucket=$(influx bucket find --name mg4 --hide-headers | cut -f1) old=$(influx auth list --hide-headers | awk -F'\t' -v d="$desc" '$2 == d { print $1 }') token=$(influx auth create --description "$desc" --read-bucket "$bucket" --hide-headers | cut -f3) [ -n "$token" ] || { echo "no token created" >&2; exit 1; } printf '%s' "$token" > /run/influx/token for id in $old; do influx auth delete --id "$id" > /dev/null; done echo "read-only token for Grafana ready" env: - name: INFLUX_HOST value: http://influxdb.mg4.svc:8086 - name: INFLUX_ORG value: mg4 - name: INFLUX_TOKEN valueFrom: secretKeyRef: name: influxdb-auth key: admin-token volumeMounts: - name: influx-token mountPath: /run/influx resources: requests: cpu: 10m memory: 32Mi limits: memory: 128Mi securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] containers: - name: grafana image: docker.io/grafana/grafana:13.2.3 ports: - name: http containerPort: 3000 env: - name: GF_SERVER_ROOT_URL value: https://car-grafana.k8s.stinnesbeck.com - name: GF_SECURITY_ADMIN_USER value: admin - name: GF_SECURITY_ADMIN_PASSWORD valueFrom: secretKeyRef: name: grafana-admin key: admin-password - name: GF_USERS_ALLOW_SIGN_UP value: "false" - name: GF_AUTH_ANONYMOUS_ENABLED value: "false" - name: GF_ANALYTICS_REPORTING_ENABLED value: "false" - name: GF_ANALYTICS_CHECK_FOR_UPDATES value: "false" # Read by datasources.yaml; the token is the file the init container wrote. - name: INFLUX_URL value: http://influxdb.mg4.svc:8086 volumeMounts: - name: data mountPath: /var/lib/grafana - name: datasources mountPath: /etc/grafana/provisioning/datasources - name: dashboard-provider mountPath: /etc/grafana/provisioning/dashboards - name: dashboards mountPath: /var/lib/grafana/dashboards/mg4 - name: influx-token mountPath: /run/influx readOnly: true # The first start creates and migrates Grafana's database: measured 44 s at 0.5 CPU # and 3 minutes at 0.1 CPU. The startup probe allows up to 10 minutes for that, and # liveness/readiness only begin once it succeeded, so a slow first start is never # killed mid-migration (which would restart it slowly again, in a loop). startupProbe: httpGet: path: /api/health port: http periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 60 readinessProbe: httpGet: path: /api/health port: http periodSeconds: 10 timeoutSeconds: 5 livenessProbe: httpGet: path: /api/health port: http periodSeconds: 30 timeoutSeconds: 5 failureThreshold: 3 # About 406 MiB in steady state in the cluster: request what it uses, and leave room # for long-range dashboard queries before the limit OOM-kills it. resources: requests: cpu: 250m memory: 512Mi limits: memory: 1Gi securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] volumes: - name: data persistentVolumeClaim: claimName: grafana-data - name: datasources configMap: name: grafana-datasources - name: dashboard-provider configMap: name: grafana-dashboard-provider - name: dashboards configMap: name: grafana-dashboards # The read-only InfluxDB token, in memory only; made anew at every start. - name: influx-token emptyDir: medium: Memory sizeLimit: 1Mi --- apiVersion: v1 kind: Service metadata: name: grafana namespace: mg4 labels: app.kubernetes.io/name: grafana app.kubernetes.io/part-of: mg4 spec: selector: app.kubernetes.io/name: grafana ports: - name: http port: 80 targetPort: http