# GitOps Flow This project follows the **GitOps** model: the Git repository is the single source of truth for all cluster configuration. ArgoCD continuously reconciles the cluster state with what is defined in the repository. ## The golden rule **Never modify cluster resources directly.** Any change made via `kubectl apply`, `kubectl patch`, or `kubectl edit` will either: - Be **immediately reverted** by ArgoCD's self-heal, or - Create **drift** that obscures the true state of the system The correct workflow for any fix is always: 1. Edit the relevant file(s) in the repository. 2. `git commit` and `git push`. 3. ArgoCD detects the change and reconciles the cluster automatically. ## How ArgoCD manages state ```mermaid flowchart TB subgraph Git["Git Repository"] ROOT["kubernetes-services/
Chart.yaml + values.yaml"] TMPL["templates/
One Application per service"] ADD["additions/
Extra manifests"] end subgraph ArgoCD["ArgoCD (in cluster)"] ROOTAPP["Root App
all-cluster-services"] CHILD1["cert-manager App"] CHILD2["ingress-nginx App"] CHILD3["grafana App"] CHILDN["... more apps"] end ROOT --> ROOTAPP ROOTAPP -->|"renders templates"| CHILD1 & CHILD2 & CHILD3 & CHILDN TMPL --> CHILD1 & CHILD2 & CHILD3 & CHILDN ADD --> CHILD1 & CHILD2 & CHILD3 & CHILDN ``` ### The app-of-apps pattern The `cluster` Ansible role installs ArgoCD and creates a single root Application called `all-cluster-services`. This root app points at the `kubernetes-services/` directory in the repository — which is a Helm chart. When ArgoCD renders this Helm chart, each template in `kubernetes-services/templates/` becomes a **child ArgoCD Application**. Each child independently syncs its own Helm chart or raw manifests. ### Sync policies All child Applications are configured with: ```yaml syncPolicy: automated: prune: true # Delete resources removed from Git selfHeal: true # Revert manual changes to match Git syncOptions: - CreateNamespace=true ``` - **Auto-sync** — ArgoCD applies changes within seconds of detecting a Git change - **Prune** — resources deleted from Git are automatically removed from the cluster - **Self-heal** — manual changes to managed resources are reverted to match Git - **CreateNamespace** — namespaces are created automatically if they don't exist ## The one exception: Sealed Secrets `kubeseal` reads the cluster's public key to encrypt secrets. This is the one operation that necessarily interacts with the live cluster. However, the resulting SealedSecret YAML is committed to Git — so it still follows the GitOps flow: ```mermaid flowchart TB A["kubeseal
(reads cluster public key)"] --> B["SealedSecret YAML"] B -->|"git push"| C["Git Repo"] C -->|"ArgoCD sync"| D["Cluster"] D -->|"controller decrypts"| E["Plain Secret"] ``` ## Read-only kubectl is fine The only legitimate `kubectl` commands during normal operation are **read-only**: ```bash kubectl get pods -A kubectl logs -n monitoring deployment/grafana kubectl describe node node02 kubectl get applications -n argo-cd ``` These commands inspect cluster state without modifying it. ## What if ArgoCD is down? If ArgoCD itself is broken, the `cluster` Ansible role can reinstall it: ```bash ansible-playbook pb_all.yml --tags cluster -e cluster_force=true ``` Once ArgoCD is back, it re-syncs all services from Git automatically.