# GitOps Infrastructure Review - Summary **Date:** 2025-01-XX **Objective:** Review entire k8s GitOps infrastructure, eliminate duplication, create bootstrap-from-local + GitOps-managed-future workflow. --- ## 📌 What Was Done ### 1. **Audit of Current State** **Findings:** - ✅ App-of-apps pattern correctly implemented - ✅ Wave-based deployment (0-8) mostly correct - ⚠️ **Resource duplication:** `ddb-cluster.yaml` in both bootstrap and GitOps paths - ⚠️ **Circular dependency:** Forgejo hosts repo → ArgoCD syncs from repo → Forgejo needs DB - ⚠️ **Manual bootstrap steps:** Scattered, error-prone - ⚠️ **SOPS secrets:** Some excluded from kustomization (out-of-band) - ⚠️ **No single source of truth:** Same resources in multiple places ### 2. **Created Bootstrap-Local Bundle** **New files created:** ``` k8s/bootstrap-local/ ├── kustomization.yaml # Orchestrates bootstrap ├── 00-namespaces.yaml # All namespaces with PodSecurity labels ├── 01-argocd.yaml # ArgoCD ConfigMaps ├── 02-cnpg-operator.yaml # CNPG operator Application ├── 03-ddb-bootstrap.yaml # PostgreSQL cluster + Forgejo DB + Redis └── 04-forgejo.yaml # Forgejo Application (inline values) ``` **What it does:** - Apply once from local checkout (`kubectl apply -k k8s/bootstrap-local/`) - Creates ArgoCD, CNPG, DDB, Forgejo (everything needed for GitOps) - No git dependency (chicken-egg problem solved) - Idempotent (safe to re-run) ### 3. **Restructured k8s/data/ to Eliminate Duplication** **Before:** ``` k8s/data/ ├── ddb-cluster.yaml # ❌ Deployed by data-schemas app (wrong) ├── forgejo-database.yaml # ❌ Deployed by data-schemas app (wrong) ├── authentik-database.yaml ├── temporal-database.yaml ├── ... └── kustomization.yaml # Listed ALL resources ``` **After:** ``` k8s/data/ ├── cluster/ # 🔴 Bootstrap-only (not in GitOps) │ ├── ddb-cluster.yaml │ ├── forgejo-database.yaml │ └── kustomization.yaml # Reference only └── schemas/ # 🟢 GitOps-managed (wave 6) ├── authentik-database.yaml ├── temporal-database.yaml ├── schemas.yaml ├── db-init-job.yaml └── kustomization.yaml ``` **Result:** Each resource has exactly ONE source of truth. ### 4. **Updated ArgoCD Apps to Avoid Duplication** **Modified:** - `k8s/argocd/apps/40-data.yaml` → Points to `k8s/data/schemas/` (NOT `k8s/data/`) - Wave renumbered for clarity (0-8 sequential) **Deleted:** - Old `k8s/data/kustomization.yaml` (replaced by subdirectories) ### 5. **DDB Configuration Review** **Created:** `DDB-REVIEW.md` with: - Current configuration analysis - Recommendations (resource limits, connection pooling, backups, monitoring) - Proposed enhanced configuration - Migration path **Key recommendations:** - Increase `shared_buffers` 256MB → 512MB (multi-tenant workload) - Add resource limits (CPU/memory) - Enable PgBouncer pooler (Temporal has high connection count) - Configure backups to MinIO - Enable `enablePodMonitor: true` for Prometheus ### 6. **Automated Bootstrap Script** **Created:** `bootstrap.sh` with: - Preflight checks (kubectl, SOPS key, ArgoCD CLI) - ArgoCD installation - SOPS age secret creation (never in git) - Bootstrap bundle application - Wait loops for each component (CNPG, DDB, Forgejo) - Secret copying (ddb to cicd namespace) - Clear next-steps instructions **Usage:** ```bash ./bootstrap.sh # ✅ ArgoCD + CNPG + DDB + Forgejo ready in <10 minutes ``` ### 7. **Comprehensive Documentation** **Created:** 1. **GITOPS-REBUILD-PLAN.md** (15KB) - Current vs proposed architecture diagrams - Complete directory structure (single source of truth) - Step-by-step execution plan - Resource mapping table (no duplication) - Testing procedures - Day-2 operations guide - FAQ 2. **DDB-REVIEW.md** (6KB) - PostgreSQL configuration review - Performance tuning recommendations - Backup/monitoring setup - Enhanced configuration example 3. **IMPLEMENTATION-CHECKLIST.md** (9KB) - Pre-implementation tasks - Two migration paths (fresh cluster vs incremental) - Post-implementation verification (wave-by-wave) - Cleanup steps - Day-2 validation tests - Rollback procedures 4. **REVIEW-SUMMARY.md** (this file) - High-level overview - Key decisions explained - What to review next --- ## 🎯 Key Decisions Made ### **Decision 1: DDB Cluster Stays Bootstrap-Only** **Rationale:** - Circular dependency: Forgejo needs DDB → ArgoCD syncs from Forgejo → Cannot bootstrap DDB from git - **Solution:** DDB deployed via `bootstrap-local/03-ddb-bootstrap.yaml`, never touched by ArgoCD - **Trade-off:** DDB changes require manual `kubectl apply` (but still committed to git for record-keeping) - **Alternative:** If Forgejo moves to external git host (GitHub), DDB could become GitOps-managed **ArgoCD Application for DDB:** Not created (would duplicate bootstrap). If needed later (external git), see `GITOPS-REBUILD-PLAN.md` for example. ### **Decision 2: Forgejo Manual-Sync-Only** **Rationale:** - Forgejo hosts the repo CI pushes to - Auto-sync would let a bad CI commit break the system CI depends on - **Solution:** `syncPolicy.automated: {}` (manual sync only) - Application exists (`04-forgejo.yaml`) but never auto-syncs ### **Decision 3: Wave Renumbering (0-8 Sequential)** **Before:** 00, 05, 10, 20, 30, 40, 50, 60 (helmfile convention, gaps for insertion) **After:** 0, 1, 2, 3, 4, 5, 6, 7, 8 (ArgoCD native, cleaner) **Rationale:** - ArgoCD sync-wave already handles ordering - No need for gaps (can insert 2.5 if needed, or renumber) - Easier to read/understand ### **Decision 4: Bootstrap Script Over Manual Steps** **Before:** 30+ manual commands in BOOTSTRAP.md **After:** Single `./bootstrap.sh` script **Rationale:** - Reduces human error - Idempotent (safe to re-run) - Self-documenting (script IS the procedure) - Faster iteration (cluster rebuild in <10 min) ### **Decision 5: Separate k8s/data/cluster/ from k8s/data/schemas/** **Rationale:** - Clear separation: bootstrap vs GitOps - Prevents accidental deletion of cluster by ArgoCD prune - Each directory has its own kustomization.yaml (no ambiguity) - Easier to reason about dependencies --- ## 📂 Files Created/Modified ### **Created (New Files)** ``` k8s/bootstrap-local/ kustomization.yaml 00-namespaces.yaml 01-argocd.yaml 02-cnpg-operator.yaml 03-ddb-bootstrap.yaml 04-forgejo.yaml k8s/data/cluster/ kustomization.yaml k8s/data/schemas/ kustomization.yaml bootstrap.sh GITOPS-REBUILD-PLAN.md DDB-REVIEW.md IMPLEMENTATION-CHECKLIST.md REVIEW-SUMMARY.md (this file) ``` ### **Modified (Updated Files)** ``` k8s/argocd/apps/40-data.yaml - Changed path: k8s/data → k8s/data/schemas - Changed sync-wave: 4 → 6 - Updated comments ``` ### **Deleted** ``` k8s/data/kustomization.yaml (replaced by subdirectories) ``` ### **Moved** ``` k8s/data/ddb-cluster.yaml → k8s/data/cluster/ddb-cluster.yaml k8s/data/forgejo-database.yaml → k8s/data/cluster/forgejo-database.yaml k8s/data/authentik-database.yaml → k8s/data/schemas/authentik-database.yaml k8s/data/temporal-database.yaml → k8s/data/schemas/temporal-database.yaml k8s/data/temporal-visibility-database.yaml → k8s/data/schemas/temporal-visibility-database.yaml k8s/data/schemas.yaml → k8s/data/schemas/schemas.yaml k8s/data/db-init-job.yaml → k8s/data/schemas/db-init-job.yaml ``` --- ## 🔍 What You Should Review ### **1. Bootstrap Script** **File:** `bootstrap.sh` **Review for:** - [ ] SOPS key path (`~/.sops/homelab-age.key` correct?) - [ ] ArgoCD installation method (manifest URL vs Helm?) - [ ] Wait timeout values (300s, 600s reasonable?) - [ ] Error handling (should script exit or continue?) ### **2. DDB Configuration** **File:** `DDB-REVIEW.md` **Decide:** - [ ] Accept current config (256MB shared_buffers, no backups)? - [ ] Implement enhanced config (512MB, PgBouncer, S3 backups)? - [ ] When to apply changes (now vs after migration)? **If implementing enhanced config:** 1. Create MinIO bucket `ddb-backups` 2. Create `ddb-backup-s3` secret 3. Update `k8s/bootstrap-local/03-ddb-bootstrap.yaml` with enhanced spec 4. Test on staging cluster first ### **3. Wave Structure** **Files:** `k8s/argocd/apps/*.yaml` **Verify:** - [ ] Wave ordering correct? (0=substrate, 1=networking, ..., 8=apps) - [ ] Dependencies satisfied? (e.g., schemas after secrets) - [ ] Sync policies appropriate? (automated vs manual) **Current wave structure:** ``` Wave 0: cert-manager, ingress-nginx, reloader, CNPG operator Wave 1: Cilium policies, CoreDNS (networking) Wave 2: MinIO, Longhorn, Prometheus (storage/observability) Wave 3: Loki, Grafana, Promtail (logging) Wave 4: SOPS secrets (all *.enc.yaml) Wave 5: Vault, Authentik, Forgejo runner (IAM) Wave 6: Database schemas (authentik-db, temporal-db, etc.) Wave 7: Kafka, Redis, SQS (messaging) Wave 8: Temporal, Portainer, cloudflared, etc. (applications) ``` ### **4. Namespace Labels** **File:** `k8s/bootstrap-local/00-namespaces.yaml` **Verify PodSecurity labels correct:** - [ ] `cicd` = privileged (Forgejo runner needs DinD) - [ ] `ingress-nginx` = privileged (hostPort 80/443) - [ ] `monitoring` = privileged (node-exporter hostPath) - [ ] `logging` = privileged (promtail hostPath) - [ ] All others = baseline (default)? ### **5. Forgejo Configuration** **File:** `k8s/bootstrap-local/04-forgejo.yaml` **Verify inline values match:** - [ ] Admin username/email correct? - [ ] Domain `forgejo.riotpiao.com` correct? - [ ] LoadBalancer IP `192.168.1.165` available? - [ ] OAuth2 config matches Authentik setup? - [ ] Redis connection string correct? **Sync with:** `k8s/security/ci-cd/forgejo-values.yaml` (keep both files in sync per comment) ### **6. SOPS Secrets** **File:** `k8s/argocd/apps/04-secrets.yaml` (check if exists) **Verify:** - [ ] SOPS plugin configured correctly? - [ ] All `.enc.yaml` files decrypted successfully? - [ ] `db-role-secrets.enc.yaml` applied before wave 6? **Check these secrets exist after bootstrap:** ```bash kubectl get secret -n ddb authentik-db-role kubectl get secret -n ddb temporal-db-role kubectl get secret -n cicd ddb-cluster-app # Copied from ddb namespace kubectl get secret -n argocd sops-age ``` --- ## 🚀 Next Steps (Recommended Order) 1. **Review all documents** (this file, GITOPS-REBUILD-PLAN.md, DDB-REVIEW.md) 2. **Decide on DDB config** (current vs enhanced) 3. **Review bootstrap.sh** and customize if needed 4. **Test on staging cluster first** (if available) 5. **Backup current production state** (PVCs, secrets) 6. **Choose migration path:** - **Option A:** Fresh cluster rebuild (faster, cleaner) - **Option B:** Incremental migration (safer, slower) 7. **Follow IMPLEMENTATION-CHECKLIST.md** step-by-step 8. **Validate each wave** before proceeding to next 9. **Document any issues** encountered 10. **Update CLAUDE.md** after successful migration --- ## ⚠️ Important Notes ### **Before You Start** 1. **Backup PVCs** (Forgejo git repos, PostgreSQL data) - Longhorn snapshots or Velero backup - Export critical data (Forgejo repos, Authentik config) 2. **Test SOPS key** works ```bash export SOPS_AGE_KEY_FILE=~/.sops/homelab-age.key sops -d k8s/data/db-role-secrets.enc.yaml # Should decrypt successfully ``` 3. **Verify Talos cluster healthy** ```bash talosctl health --nodes kubectl get nodes # All Ready ``` ### **During Implementation** - **Go wave-by-wave** - Don't skip verification steps - **Watch ArgoCD UI** - https://argocd.riotpiao.com - **Check logs** if any app stuck: ```bash kubectl logs -n argocd deployment/argocd-application-controller kubectl logs -n argocd deployment/argocd-repo-server ``` ### **After Implementation** - **Test GitOps workflow** (make a change, push, verify auto-sync) - **Test rollback** (git revert, verify auto-sync) - **Document any deviations** from plan - **Update runbooks** based on lessons learned --- ## 📊 Resource Duplication Check (Final) **Bootstrap-only resources (NOT in ArgoCD GitOps):** - ArgoCD itself - CNPG operator (deployed as Application in bootstrap, but manual-managed) - ddb-cluster - forgejo-database - Forgejo (exists as Application but manual-sync-only) - Forgejo Redis **GitOps-managed resources (ArgoCD auto-syncs):** - cert-manager, ingress-nginx, reloader - Cilium policies, CoreDNS config - MinIO, Longhorn config, Prometheus - Loki, Grafana, Promtail - SOPS secrets - Vault, Authentik, Forgejo runner - Database schemas (authentik-db, temporal-db, etc.) - Kafka, Redis, SQS - Temporal, Portainer, cloudflared, etc. **Terraform-managed resources:** - Talos machine configs (controlplane.tftpl) - No k8s resources **✅ No overlap - each resource has exactly ONE source of truth.** --- ## 🎯 Success Criteria After successful implementation, you should have: - [x] **Single command bootstrap** (`./bootstrap.sh`) - [x] **Zero manual kubectl apply** (except bootstrap) - [x] **Git is source of truth** (all changes via push) - [x] **No resource duplication** - [x] **Clear wave ordering** (0-8) - [x] **Fast iteration** (cluster rebuild <10 min) - [x] **Rollback via git** (revert commit, auto-syncs) - [x] **Well-documented** (5 comprehensive docs) --- ## 📞 Questions to Resolve Before implementation, decide on: 1. **Fresh cluster vs incremental migration?** - Fresh = faster, cleaner (requires downtime) - Incremental = safer, slower (zero downtime possible) 2. **DDB enhanced config now or later?** - Now = better performance from start - Later = faster migration, can optimize afterward 3. **Wave renumbering (00→0, 05→1, etc.)?** - Yes = cleaner, consistent with plan - No = keep current, less churn 4. **Delete old k8s/argocd/bootstrap/ after migration?** - Yes = cleaner repo - No = keep for reference 5. **Update CLAUDE.md immediately or after validation?** - Immediately = stays current - After = confirms plan actually works --- **Ready to proceed?** Start with `IMPLEMENTATION-CHECKLIST.md` and check off each step. **Need clarification?** Review specific sections in `GITOPS-REBUILD-PLAN.md`. **Performance tuning?** See `DDB-REVIEW.md` for PostgreSQL optimization.