Major accomplishments from comprehensive cluster review: ## Storage HA (answering "are volumes replicated?") - Verified 3-node Longhorn HA: ALL 17 volumes have 3 replicas - Fixed CLAUDE.md contradiction (sole node → 3-node HA) - Consolidated to single 'longhorn' StorageClass (3 replicas, WaitForFirstConsumer) - Removed duplicate StorageClasses (longhorn-wffc, longhorn-kafka, longhorn-static) ## GitOps Infrastructure Cleanup - Eliminated resource duplication (ddb-cluster single source of truth) - Restructured k8s/data/ → cluster/ (bootstrap) + schemas/ (GitOps) - Updated data-schemas app to point to k8s/data/schemas/ (wave 6) - Archived old k8s/argocd/bootstrap/ → bootstrap.archived/ ## Bootstrap Dependencies Fixed - Added 05-wait-for-databases.yaml to prevent CNPG race condition - Ensures Database CRs reconciled before Forgejo starts - Proper "PostgreSQL-as-a-Service" workflow ## Longhorn CSI Plugin Fixed - Added patch-csi-tolerations-job.yaml (GitOps PostSync hook) - CSI plugin now runs on all 3 nodes (cp-1, cp-2, cp-3) - Fixes volume attachment on tainted control-plane nodes ## Live Migration (Zero Downtime) - Migrated 37 applications to ArgoCD app-of-apps management - Fixed Forgejo startup issues: * Service selector mismatch (app: forgejo → app: gitea) * Missing homelab-ca ConfigMap * Missing forgejo-oidc secret (temporary) * CNPG database creation timing ## Documentation (10 comprehensive files) - WHATS-NEXT.md - Daily GitOps workflow - MIGRATION-STATUS.md - Cluster health report - REVIEW-SUMMARY.md - Session overview - GITOPS-REBUILD-PLAN.md - Architecture reference - DDB-REVIEW.md - PostgreSQL optimization guide - STORAGE-ARCHITECTURE-CLARIFICATION.md - Storage HA investigation - BOOTSTRAP-DEPENDENCY-FIX.md - CNPG race condition fix - STORAGECLASS-CONSOLIDATION.md - Single StorageClass rationale - IMPLEMENTATION-CHECKLIST.md - Migration checklist - bootstrap.sh - Automated bootstrap script ## Cluster Status - ArgoCD: 4/4 pods running - DDB cluster: 3/3 instances healthy - Longhorn: 3/3 nodes, all CSI plugins running - Forgejo: Running, accessible at http://192.168.1.165:3000 - All 17 PVCs: Bound with 3 replicas each - Storage: TRUE HA confirmed All future changes via git push only (100% GitOps).
128 lines
5.5 KiB
Markdown
128 lines
5.5 KiB
Markdown
# Cluster bootstrap & CD — two scopes
|
||
|
||
The GitOps repo is hosted on the cluster's own Forgejo, so ArgoCD cannot pull it
|
||
until Forgejo exists. There are two distinct workflows — pick the one that
|
||
matches your situation.
|
||
|
||
```
|
||
Scope 1 — from scratch cold cluster, nothing running yet → full bootstrap
|
||
Scope 2 — iterating CD cluster + Forgejo + ArgoCD are up → just git push
|
||
```
|
||
|
||
---
|
||
|
||
## Scope 1 — Cluster from scratch (cold bootstrap)
|
||
|
||
Strict ordering, because each layer depends on the previous one existing.
|
||
|
||
```
|
||
Talos + Cilium CNI Terraform (nodes) — cluster reachable
|
||
Substrate cert-manager, ingress-nginx, reloader — Terraform helm_releases
|
||
Longhorn, ArgoCD — imperative install
|
||
Phase 0 (git-independent) CNPG + ddb-cluster + redis + Forgejo — manual, below
|
||
Seed git push repo into Forgejo
|
||
Phase 1 app-of-apps root — ArgoCD deploys the rest
|
||
```
|
||
|
||
Run every command from a local checkout with `KUBECONFIG` pointed at the cluster.
|
||
|
||
### Phase 0 — bootstrap Forgejo and its data plane (git-independent)
|
||
|
||
```bash
|
||
# 1. CNPG operator (public Helm) + its CRDs.
|
||
kubectl apply -f k8s/argocd/bootstrap/cnpg-operator.yaml
|
||
argocd app sync cnpg-operator
|
||
# CNPG ships CRDs in the chart's crds/ folder, which ArgoCD's helm-template does
|
||
# NOT install. If the ddb-cluster apply below fails with "no matches for kind
|
||
# Cluster", install them once:
|
||
# kubectl apply --server-side -f \
|
||
# https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.24/releases/cnpg-1.24.1.yaml
|
||
|
||
# 2. Namespaces first. cicd MUST be labelled privileged (DinD/runner needs it);
|
||
# the Forgejo app deliberately does NOT create it.
|
||
kubectl create namespace ddb --dry-run=client -o yaml | kubectl apply -f -
|
||
kubectl create namespace cicd --dry-run=client -o yaml | kubectl apply -f -
|
||
kubectl label namespace cicd pod-security.kubernetes.io/enforce=privileged --overwrite
|
||
|
||
# 3. Postgres cluster + Forgejo database + Redis (raw manifests, local checkout).
|
||
kubectl apply -f k8s/data/ddb-cluster.yaml
|
||
kubectl apply -f k8s/data/forgejo-database.yaml
|
||
kubectl apply -f k8s/security/ci-cd/forgejo-redis.yaml
|
||
|
||
# 4. Copy the CNPG-generated app password into cicd (Secrets are ns-scoped).
|
||
kubectl get secret ddb-cluster-app -n ddb -o yaml \
|
||
| sed 's/namespace: ddb/namespace: cicd/' \
|
||
| kubectl apply -n cicd -f -
|
||
|
||
# 5. Forgejo (public Helm, values inlined — git-independent).
|
||
kubectl apply -f k8s/argocd/bootstrap/forgejo.yaml
|
||
argocd app sync forgejo
|
||
```
|
||
|
||
### Seed + Phase 1
|
||
|
||
```bash
|
||
# 6. Push this repo to the freshly-created Forgejo so Phase 1 has a source.
|
||
git remote add forgejo https://forgejo.riotpiao.com/riotpiao.com/homelab.git
|
||
git push forgejo main
|
||
|
||
# 7. (Optional) Cloudflared tunnel secret — if the SOPS CMP plugin is NOT yet
|
||
# wired up: create the cloudflared-token Secret manually before wave 8 syncs.
|
||
# Skip this step if the CMP plugin is already live and handling SOPS decryption.
|
||
kubectl create namespace cloudflared --dry-run=client -o yaml | kubectl apply -f -
|
||
sops -d k8s/applications/cloudflared/cloudflared-secrets.enc.yaml \
|
||
| yq '.cloudflared.tunnelToken' \
|
||
| kubectl create secret generic cloudflared-token -n cloudflared \
|
||
--from-literal=token=- --dry-run=client -o yaml | kubectl apply -f -
|
||
# Once the SOPS CMP plugin is deployed (Phase 1 wave 0), migrate this Secret
|
||
# into the plugin's script output and remove this manual step.
|
||
|
||
# 8. Deploy everything else from git — one sync brings up the whole cluster.
|
||
kubectl apply -f k8s/argocd/projects/homelab-project.yaml # AppProject
|
||
kubectl apply -k k8s/argocd/root # app-of-apps root
|
||
argocd app sync homelab-root
|
||
```
|
||
|
||
`homelab-root` renders every Application under `k8s/argocd/apps/` and syncs them
|
||
in sync-wave order (networking → storage/observability → logging → security →
|
||
data → messaging → applications).
|
||
|
||
---
|
||
|
||
## Scope 2 — Iterating CD (day-2, cluster already up)
|
||
|
||
The app-of-apps and its children already exist and reconcile from Forgejo. To
|
||
change anything, you do NOT re-run the bootstrap — you just push:
|
||
|
||
```bash
|
||
# edit manifests under k8s/**
|
||
git add -A && git commit -m "..."
|
||
git push # to Forgejo main (origin)
|
||
# ArgoCD auto-syncs (child apps have syncPolicy.automated); to force it:
|
||
argocd app sync homelab-root
|
||
```
|
||
|
||
- Adding a new service = add an Application manifest under `k8s/argocd/apps/`
|
||
and its manifests under the path it references, then push. The app-of-apps
|
||
picks it up on next sync.
|
||
- Changing a Helm value = edit the values file the Application points at
|
||
(`$values/...`), push.
|
||
- Forgejo itself is Phase-0 / bootstrap (manual sync only) — a bad CI commit
|
||
must not be able to break the system CI depends on.
|
||
|
||
---
|
||
|
||
## Notes / prerequisites
|
||
|
||
- **Secrets:** several values files (loki S3, grafana admin, authentik, vault)
|
||
expect secrets that helmfile used to inject via `--set`. Under ArgoCD these
|
||
come from the `*.enc.yaml` SOPS files via the ArgoCD SOPS plugin — confirm the
|
||
plugin is configured before syncing waves 2–3, or those charts render with
|
||
empty secrets.
|
||
- **Substrate** (cert-manager, ingress-nginx, reloader) is installed by Terraform
|
||
as bootstrap Helm releases, not ArgoCD. Cilium CNI, Longhorn, and ArgoCD
|
||
itself are cluster-bootstrap installs.
|
||
- **Single storage node:** only `talos-cp-1` runs workloads/storage, so stateful
|
||
services are single-instance (`ddb-cluster` = 1). See the repo `USAGE.md` and
|
||
root `CLAUDE.md` topology section.
|