Commit Graph

3 Commits

Author SHA1 Message Date
root
a307607a99 Migrate Grafana/Prometheus/SonarQube from docker-compose to k8s (step #10)
Real data migrated, not a fresh start: 1.2GB Prometheus TSDB, Grafana's
existing dashboards/DB (its admin password was already changed from
default — confirmed real, in-use data), SonarQube's data/logs/extensions
and its own Postgres DB (confirmed: the actual `management-platform`
SonarQube project survived, matching the Jenkinsfile's own
-Dsonar.projectKey). Old docker-compose services stopped, not removed —
left as a rollback path.

Real problems hit and fixed along the way (see k8s/monitoring/README.md
for the full detail, worth reading before touching any of this again):

- k3s's bundled Traefik defaults to a LoadBalancer Service, which
  immediately hijacked ports 80/443 via iptables DNAT (klipper-lb) out
  from under this VM's existing unrelated nginx-served app ("nqks") the
  moment k3s was installed. Patched Traefik to ClusterIP via a
  HelmChartConfig; nginx now reverse-proxies the 3 new domains to
  Traefik's ClusterIP instead of Traefik touching host ports at all.

- Caught my own mistake before it caused damage: "corrected" what I
  assumed was a typo (grafanna.nav.ovh -> grafana.nav.ovh) — the two-N
  version is what the user actually specified, and is what points at this
  VM; plain grafana.nav.ovh is a different, unrelated, pre-existing live
  server at 109.199.127.74. Renamed everything back before requesting any
  cert or touching that domain.

- Adding hostPort to the SonarQube pod (for Jenkins' hardcoded
  178.18.243.51:9000) broke pod-to-pod routing to that pod entirely —
  Traefik couldn't reach it (hung/504) while direct host-to-pod and
  host-to-ClusterIP both worked fine the whole time, which is what gave
  it away. Replaced with a host-level socat proxy (systemd unit) to the
  Service's ClusterIP instead, which touches nothing pod-level. Verified
  Jenkins' exact address still works AND Traefik routing to the same pod
  works simultaneously.

- SonarQube needed a DB migration after the version jump
  (DB_MIGRATION_NEEDED), then OOM'd during post-migration rule
  re-registration on the compose file's original 256MB web heap — bumped
  SONAR_WEB_JAVAADDITIONALOPTS.

TLS: real Let's Encrypt certs via certbot (HTTP-01, nginx already owns
port 80 so no DNS-01/OVH-webhook replication needed on this second
cluster), auto-renewing. Access: HTTP Basic Auth in front of all three
domains for browser access; SonarQube also stays reachable unauthenticated
at 178.18.243.51:9000 specifically for Jenkins (which authenticates via
its own token, not BasicAuth — that path would break the scanner),
restricted to the main server's IP via ufw, not open to the internet.

Verified end-to-end: all three https://*.nav.ovh domains return 401
without credentials and load correctly with them; Jenkins' exact
SonarQube address (no auth) still returns 200; real data confirmed
present in Grafana, Prometheus, and SonarQube post-migration.
2026-08-21 16:01:39 +02:00
root
f3f08c3ef6 Give management-platform a scoped ServiceAccount for k8s backup/restore
The pod running app.py had no kubectl binary and only a read-only SA
(management-platform-viewer-sa) — backup-k8s-apps.sh/restore-k8s-apps.sh
could never actually run from inside it. Adds:

- management-platform-backup-role (ClusterRole, bound via RoleBinding only
  in n8n/odoo/mautic/nextcloud/erpnext — not cluster-wide): get/list/watch
  on pods/deployments/configmaps/ingresses/PVCs, pods/exec create, pods
  create+delete (needed for restore's populate-before-scale-up loader pod),
  secrets get/list/update/patch, deployments/scale update/patch only (no
  write on full Deployment/Service/ConfigMap/Ingress specs).
- management-platform-sa, replacing management-platform-viewer-sa as the
  pod's identity — inherits the old viewer-role's read-only bindings too
  (retargeted in management-platform-viewer-rbac.yaml) so the existing
  cluster-view page keeps working under one SA.
- k3s binary hostPath-mounted read-only into the pod (same pattern as the
  Jenkins agent's docker-cli container), so `k3s kubectl` is available
  where no separate kubectl binary exists.

Both scripts updated to fall back to k3s kubectl when no kubectl is on
PATH, and to use /proc/meminfo instead of `free` for the resource-safety
check (not present in the pod's minimal image). Also fixes a real issue
found while live-testing this from inside the pod: the pod's pre-existing
/root hostPath mount also exposes the host's own admin ~/.kube/config,
which k3s kubectl was silently preferring over the scoped SA token —
forcing --kubeconfig=/dev/null in both scripts closes that.

Verified end-to-end from inside the actual management-platform pod:
manifests/secret/DB-dump/PVC-data backup for n8n succeeds under the new
SA's scoped permissions, and a kube-system access attempt is correctly
rejected (Forbidden) once the kubeconfig leak is closed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 11:30:03 +02:00
root
3782241a88 Add management-platform Deployment manifest with viewer SA wired in
Source-of-truth manifest for the management-platform Deployment, with
serviceAccountName: management-platform-viewer-sa added so the pod can
authenticate for the read-only k8s cluster-view feature. Not yet applied
to the cluster.
2026-08-14 12:20:58 +02:00