01 Why two repositories
Infrastructure provisioning and in-cluster desired state have different lifecycles, different blast radii, and different tools. Keeping them apart means a Terraform change can never accidentally alter a running workload, and an app change never touches cloud infrastructure.
flowchart LR
subgraph infra["eks-infrastructure (Terraform)"]
direction TB
tf["VPC · EKS control plane · node group
IAM / IRSA · backend state"]
argo["Installs ArgoCD"]
root["Creates root ArgoCD app
bootstrap/root-app.yaml"]
tf --> argo --> root
end
subgraph apps["eks-gitops-apps (Git = source of truth)"]
direction TB
kust["apps/kustomization.yaml"]
a1["aws-load-balancer-controller"]
a2["sample-app · flask-app"]
a3["monitoring-chart"]
a4["monitoring-resources"]
a5["telegram-alerts"]
kust --> a1 & a2 & a3 & a4 & a5
end
root ==>|"points ArgoCD to apps/ path"| kust
classDef i fill:#22303f,stroke:#4493f8,color:#e6edf3;
classDef a fill:#12351f,stroke:#3fb950,color:#e6edf3;
class tf,argo,root i;
class kust,a1,a2,a3,a4,a5 a;
root-app.yaml tells ArgoCD to watch the
apps/ path in the GitOps repo. From there, Git is the source of truth — not manual
kubectl.
02 Bootstrap & handoff sequence
The infrastructure repo is applied first. Its deploy script provisions AWS resources, wires up cluster access, installs ArgoCD (plus the Prometheus Operator CRDs the monitoring layer needs), and creates the root application — the moment control passes to GitOps.
sequenceDiagram
actor Eng as Engineer
participant TF as Terraform
participant EKS as EKS cluster
participant Argo as ArgoCD
participant Git as eks-gitops-apps
Eng->>TF: apply (VPC, EKS, node group, IAM)
TF->>EKS: cluster provisioned
Eng->>EKS: update kubeconfig + apply aws-auth.yaml
Eng->>EKS: install ArgoCD + Prometheus Operator CRDs
Eng->>Argo: apply bootstrap/root-app.yaml (root app)
Argo->>Git: read apps/kustomization.yaml
loop continuous reconciliation
Argo->>Git: detect desired state
Argo->>EKS: sync cluster to match Git
end
Note over Argo,EKS: From here, changes ship via git commit + push
skipCrds: true, the deploy script
pre-installs the Prometheus Operator CRDs before ArgoCD reconciles — so ServiceMonitor,
PrometheusRule, and AlertmanagerConfig apply cleanly without sync failures.
03 The GitOps reconciliation loop
Changes are never applied by hand. An engineer edits a manifest, commits, and pushes; ArgoCD detects the drift between Git and the cluster and reconciles. A CI pipeline validates manifests before they reach the cluster.
flowchart LR
dev(["Edit manifest"]) --> commit["git commit + push"]
commit --> ci{"CI validation"}
ci -->|"kubectl kustomize
(render)"| render["Rendered manifests"]
render -->|"kubeconform
(schema check)"| ok{"Valid?"}
ok -->|no| fail["Blocked before cluster"]
ok -->|yes| merged["Merged to Git"]
merged --> argo["ArgoCD detects drift"]
argo --> sync["Sync cluster to desired state"]
classDef n fill:#1c2330,stroke:#ff9900,color:#e6edf3;
class dev,commit,render,merged,argo,sync n;
-ignore-missing-schemas lets valid
CRDs pass while still strictly checking core Kubernetes resources.
04 Observability: platform vs. configuration split
Monitoring is deliberately split into two ArgoCD applications. monitoring-chart
installs the kube-prometheus-stack platform (Prometheus, Alertmanager, Grafana) via Helm with
skipCrds: true; monitoring-resources layers the project-specific
ServiceMonitor, alert rules (PrometheusRule), and Alertmanager routing on top. This keeps the Helm chart
clean while allowing custom observability to evolve independently.
flowchart LR m1["Flask /metrics endpoint"] --> sm["ServiceMonitor"] sm --> prom["Prometheus"] prom --> rule["PrometheusRule
(alert rules)"] rule --> am["Alertmanager"] am --> hook["telegram-alerts
webhook service"] hook --> tg(["Telegram"]) classDef plat fill:#22303f,stroke:#4493f8,color:#e6edf3; classDef res fill:#12351f,stroke:#3fb950,color:#e6edf3; class prom,am plat; class sm,rule,hook res;
05 Design decisions & tradeoffs
terraform plan/apply. Running plan in PR CI would
require the remote S3 backend, live AWS credentials, and environment variables — coupling
validation to real infrastructure. PR checks stay fast and deterministic (fmt, validate, tflint,
tfsec); apply happens in controlled deployment pipelines.