# K8s Cluster Platform — Services Bootstraps platform/application services (vault, kube-prometheus-stack, harbor, jenkins, sonarqube, headlamp) onto the cluster using ArgoCD app-of-apps pattern. Run this after `cluster-bootstrap` finishes (ArgoCD, MetalLB, Envoy Gateway, NFS storage must already be up). ## Architecture ``` Git repo (cluster-platform) └── ArgoCD watches platform/apps/ → syncs all Applications platform-app.yaml ← root Application, applied once by hand (kubectl apply) platform/apps/ ← ArgoCD watches this path; one Application CRD per service manifests/ ← Helm values + raw manifests referenced by those Applications ``` Sync waves (no hard interdependencies between services yet — grouped for readability / staggered rollout): | Wave | Service | Purpose | |------|---------|---------| | 0 | vault | Secrets engine (standalone, manual init/unseal) | | 0 | headlamp | K8s dashboard | | 1 | headlamp-config | ClusterRoleBinding for login token | | 1 | external-secrets | Vault → K8s Secret operator | | 1 | kube-prometheus-stack | Prometheus + Grafana + Alertmanager | | 1 | harbor | Image registry | | 2 | external-secrets-config | ClusterSecretStore wired to Vault (k8s auth) | | 2 | kube-prometheus-stack-config | Grafana HTTPRoute | | 2 | harbor-config | Harbor HTTPRoute | | 2 | jenkins | CI | | 2 | sonarqube | Code quality (embedded H2, no external Postgres) | | 3 | jenkins-config | Jenkins HTTPRoute | | 3 | sonarqube-config | SonarQube HTTPRoute | ⚠️ Chart `targetRevision` pins in `platform/apps/*.yaml` are best-effort and marked `TODO: verify latest` — this session had no live access to the Helm repos to confirm current versions. Run `helm repo add && helm search repo / --versions` before or after first sync and bump if a pin doesn't resolve. ## Bootstrap Sequence **Apply the root platform Application** ```bash kubectl apply -f platform-app.yaml ``` Everything else is reached by ArgoCD syncing `platform/apps/` from here. ### Post-sync manual steps **Vault — initialize + unseal** (standalone mode, not auto-unseal): ```bash kubectl exec -n vault vault-0 -- vault operator init -key-shares=1 -key-threshold=1 # save the unseal key + root token shown, then: kubectl exec -n vault vault-0 -- vault operator unseal ``` **Wire Vault up for external-secrets** (kv-v2 mount + kubernetes auth method — this is Vault-internal config, not a k8s resource, so it can't go through ArgoCD; do it once after unseal): ```bash kubectl exec -it -n vault vault-0 -- sh export VAULT_TOKEN= vault secrets enable -path=kv kv-v2 vault auth enable kubernetes vault write auth/kubernetes/config \ kubernetes_host="https://kubernetes.default.svc" vault policy write external-secrets - <