TazLab K8s: Monitoring Detail

Level 3 (Detail) — Prometheus, Grafana, dashboards, metrics-server.

Concept

The monitoring stack uses kube-prometheus-stack (Prometheus + Grafana + Alertmanager) managed as HelmRelease. Grafana uses the shared PostgreSQL instance (tazlab-db) as its backend, making dashboards persistent across pod restarts. Dashboards are managed as ConfigMaps loaded by a sidecar.

HelmRelease

File: infrastructure/operators/monitoring/helmrelease.yaml

FieldValue
Chartkube-prometheus-stack
Chart version61.3.1
Repositoryprometheus-community
Namespacemonitoring

Manifests

File: infrastructure/operators/monitoring/

FilePurpose
helmrepository.yamlHelm repository reference
helmrelease.yamlkube-prometheus-stack installation
namespace.yamlmonitoring namespace
metrics-server.yamlResource metrics (CPU/memory)
grafana-db-creds.yamlVaultDynamicSecret — Grafana DB credentials from Vault database/creds/grafana
grafana-sa.yamlGrafana ServiceAccount (runAsUser in HelmRelease)
tailnet-ingress.yamlGrafana Tailscale ingress (host grafana, tailscale IngressClass)
dashboards/cluster-health.yamlCluster health dashboard ConfigMap
dashboards/nodes-pro.yamlDetailed node metrics dashboard

Grafana PostgreSQL Backend

Grafana is configured to use tazlab-db as its database backend (database grafana, user grafana). This ensures:

  • Dashboard configurations survive pod restarts
  • User sessions and preferences are persistent
  • No PVC dependency for Grafana itself

Grafana admin credentials: adminPassword in the HelmRelease is the post-purge placeholder change-me (changed via Grafana UI). Database credentials are delivered dynamically: VaultDynamicSecret grafana-db-creds (infrastructure/operators/monitoring/grafana-db-creds.yaml) reads Vault database/creds/grafana into Secret grafana-dynamic-creds with lease renewal at renewalPercent: 67 and revoke: true on teardown.

Grafana PostgreSQL mTLS

File: infrastructure/instances/tazlab-db/cluster.yaml (pg_hba), infrastructure/operators/monitoring/helmrelease.yaml (Grafana config), infrastructure/operators/monitoring/vaultpkisecret-client-grafana.yaml (client cert management)

Server-Side: CN-mapped certificate authentication

The tazlab-db cluster is configured with customTLSSecret: tazlab-db-server-tls (from VaultPKISecret server-tls in infrastructure/instances/tazlab-db/vaultpkisecret-server-tls.yaml) and customReplicationTLSSecret pointing to the same secret. pg_hba includes hostssl cert rules for the grafana user, requiring a valid client certificate from the db-client-grafana Vault role:

HostDatabaseUserAddressAuth method
hostsslgrafanagrafana0.0.0.0/0cert
hostsslgrafanagrafana::/0cert
hostsslgrafanagrafana0.0.0.0/0md5 (fallback)
hostsslgrafanagrafana::/0md5 (fallback)

Client-Side: VaultPKISecret db-client-grafana-tls

File: infrastructure/operators/monitoring/vaultpkisecret-client-grafana.yaml

FieldValue
KindVaultPKISecret
Namedb-client-grafana-tls
Namespacemonitoring
Vault mountpki_int
Roledb-client-grafana
CommonNamegrafana
TTL24h
Expiry offset4h
Destination secretdb-client-grafana-tls (type kubernetes.io/tls, overwrite)
CA templaterange ca_chain

The ca.crt template uses a range over ca_chain to write each CA certificate (the modern VaultPKISecret idiom — avoids the old single-CA assumption).

Known issue (2026-07-03): The ca_chain range template replaces an earlier {{ .Secrets.ca.crt | base64Decode }} reference that broke after a Vault PKI intermediate rotation because the template had been hardcoded to the old issuer. The range fix makes it tolerate chain changes automatically.

extraInitContainers: cert copy

The Grafana HelmRelease uses an extraInitContainers entry (copy-db-certs) to copy certificates from the mounted Secret to the expected paths:

  1. extraSecretMounts mounts db-client-grafana-tls at /tmp/db-certs-in (readOnly, mode 0600).
  2. extraEmptyDirMounts creates an emptyDir grafana-certs-empty at /etc/grafana/certs/.
  3. The init container (image: alpine:3.21) copies certs with cp -rL, then chmod 600 /etc/grafana/certs/tls.key.
  4. Grafana’s grafana.ini.database section sets:
    • ssl_mode: require (enforced via GODEBUG=tlskyber=0 to work around Kyber fragmentation on Proxmox VXLAN; upgrade to verify-full planned after Proxmox qemu-server >= 11.0+pve1)
    • ca_cert_path: /etc/grafana/certs/ca.crt
    • client_cert_path: /etc/grafana/certs/tls.crt
    • client_key_path: /etc/grafana/certs/tls.key

Note: The grafana-dynamic-creds valuesFrom reference (leftover from Vault Agent Injector era) was removed from the HelmRelease on 2026-07-11 as part of cluster cycle 2 cleanup.

GODEBUG=tlskyber=0

Grafana is started with GODEBUG=tlskyber=0 to work around Kyber TLS key exchange fragmentation issues on Proxmox VXLAN. The Go Kyber implementation generates large handshake messages that fragment across VXLAN’s reduced MTU, causing connection drops to tazlab-db. Setting tlskyber=0 disables the Kyber KEX, falling back to X25519.

Dashboards as Code

Dashboards are stored as ConfigMaps with label grafana_dashboard: "1". Grafana’s sidecar watches for this label and loads dashboards automatically.

Current dashboards:

  • cluster-health.yaml — High-level node and pod metrics
  • nodes-pro.yaml — Detailed hardware and kernel metrics

metrics-server

File: infrastructure/operators/monitoring/metrics-server.yaml

Provides resource metrics used by kubectl top and HorizontalPodAutoscaler. Installed alongside kube-prometheus-stack.

Alertmanager (TD-057 — Open)

Alertmanager ships with the stack but currently has no receivers — no alert routes reach Discord, email, or any target. The HelmRelease (chart 61.3.1) defines no alertmanagerSpec; a Discord receiver fix was reverted in commit b0b46bc pending a Discord webhook URL (planned delivery: VaultStaticSecret reading Vault tazlab-k8s/static/infra/monitoring/ALERTMANAGER_DISCORD_WEBHOOK). Correct Helm key for the future fix: alertmanager.alertmanagerSpec.configSecret.

DAG Position

operators-namespaces (Level 0, creates monitoring namespace)
→ monitoring (Level 1, installs kube-prometheus-stack and metrics-server)
→ configs (Level 2, Let's Encrypt issuers — secrets arrive via VSO, not configs)
→ instances (Level 3, PostgresCluster `grafana` DB; Grafana ingress is tailnet-side)

Prometheus Status Note

As of the 2026-04-29 power loss recovery, Prometheus uses a manually salvaged Longhorn volume. The volume is healthy and the pod is 2/2 Running. Future consideration: if monitoring data becomes important, the Prometheus PVC should use 2 replicas.

See Also