Central ArgoCD GitOps Repository¶
Einordnung ins Overtime-Projekt
Für Overtime wurde bewusst zwischen Anwendungscode und Deployment-Konfiguration getrennt. Dieses ADR beschreibt, warum alle Kubernetes-Manifeste in einem zentralen GitOps-Repository statt verteilt über die einzelnen Service-Repos verwaltet werden.
1. Kontext & Problemstellung (Deutsch)¶
Kubernetes-Manifeste für deployte Applikationen müssen versioniert und verwaltet werden. In der Praxis sind zwei Muster verbreitet: Die Manifeste liegen direkt im Quellcode-Repository der jeweiligen Anwendung ("Single Repo" Ansatz), oder sämtliche Deployment-Manifeste werden in einem dedizierten Repository, strikt getrennt vom Anwendungscode, gebündelt ("GitOps Repo" Ansatz).
Diese Architektur-Entscheidung hat weitreichende Konsequenzen für die Zugriffssteuerung (Access Control), die Nachvollziehbarkeit (Auditability) und die konzeptionelle Trennung zwischen "Was macht der Code?" (Applikation) und "Wo und wie läuft der Code?" (Infrastruktur). Da sich in Overtime mehrere unabhängige Applikationen und Infrastruktur-Komponenten dieselben Ziel-Cluster teilen, muss eine skalierbare und sichere Struktur gefunden werden.
2. Architecture Decision Record (English)¶
ADR-009: Central ArgoCD GitOps Repository¶
Status¶
Accepted
Context and Problem Statement¶
Kubernetes manifests for deployed applications need a version-controlled home. Two patterns are common: colocating manifests in each application's source repo or keeping all deployment manifests in a dedicated repository separate from application code. The choice directly impacts access control, auditability, and the clean separation between application logic and deployment state.
Decision Drivers¶
- Independent Auditability: The deployment history must be independently auditable from the application code history.
- Least Privilege CI: CI pipelines must be able to update image tags programmatically without requiring write access to the application source repositories.
- Single Source of Truth: ArgoCD must be able to watch a single, trusted source of truth for the entire cluster state.
- Co-Management: Multiple unrelated applications and infrastructure components (Django backend, MySQL, MinIO, mysql-backup) share the same clusters — their manifests should be co-managed efficiently.
Considered Options¶
- Manifests in each app repo — Simpler to start. However, CI pipelines need write access to the app repos to bump versions, deployment history gets mixed with code history, and ArgoCD has to be configured to watch dozens of disparate repositories.
- Central GitOps repo — All Kubernetes manifests live in one central repository. Provides clean access control, a single ArgoCD source of truth, and CI runners only need a scoped deploy token for this specific repository.
Decision Outcome¶
Chosen: Central overtime-gitops repository.
By centralizing the state, we achieve a strict separation of concerns. Developers work in the overtime-backend repository. When a release is cut, the CI pipeline pushes solely the new image tag to the overtime-gitops repository. ArgoCD monitors only this GitOps repository and synchronizes the state to the clusters.
Consequences & Trade-offs¶
Positive Consequences¶
- Absolute Source of Truth: ArgoCD relies on one central repository. Cluster drift is always detectable by comparing the live cluster state directly to the
overtime-gitops/mainbranch. - Clean Audit Log: The deployment history is a clean git log consisting of
deploy: update X to <sha>commits. This can be reviewed and audited entirely separately from application feature commits. - Granular Security: Access to production manifests can be restricted via GitLab repository permissions independently of the application source code repositories.
- Frictionless Onboarding: Adding a new service simply requires adding an
ApplicationSetand anapps/{service}/directory in the GitOps repo — zero changes to CI templates or the app repositories are necessary.
Negative Consequences / Trade-offs¶
- Single Point of Failure:
overtime-gitopsbecomes a strict dependency for all deployments. It must remain highly available and accessible from the management cluster's ArgoCD controller.
3. Implementation & Repository Structure (English)¶
The GitOps repository utilizes Kustomize and ArgoCD's App-of-Apps / ApplicationSet pattern to dynamically map folders to target clusters.
Directory Layout¶
(Note: In the structure below, test and production overlays are consistently mapped to overtime-staging and overtime-production across both apps and infrastructure for clarity).
overtime-gitops/
├── apps/
│ ├── django-backend/
│ │ ├── base/ # Shared manifest set
│ │ │ ├── deployment.yaml
│ │ │ └── kustomization.yaml
│ │ └── overlays/
│ │ ├── overtime-production/ # Prod environment patches
│ │ └── overtime-staging/ # Staging environment patches
│ └── example-app/
├── infra/
│ ├── minio/
│ │ ├── base/
│ │ └── overlays/
│ │ ├── overtime-production/
│ │ └── overtime-staging/
│ ├── mysql/
│ │ ├── base/
│ │ └── overlays/
│ │ ├── overtime-production/
│ │ └── overtime-staging/
│ └── mysql-backup/
│ ├── base/
│ └── overlays/
│ ├── overtime-production/
│ └── overtime-staging/
└── root-app/ # ArgoCD ApplicationSets
├── appproject-apps.yaml # AppProject: application workloads
├── appproject-infrastructure.yaml# AppProject: infra components
├── appset-django-backend.yaml # ApplicationSet → edge clusters
├── appset-minio.yaml
├── appset-mysql.yaml
├── appset-mysql-backup.yaml
└── appset-sealed-secrets.yaml # ApplicationSet → all clusters
Here is an example of the Django backend ApplicationSet:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: django-backend
namespace: argocd
spec:
generators:
# Targets only clusters with the label cluster-role=edge (set by ansible edge-register.yml).
# Cluster names must match overlay directory names: overtime-staging, overtime-production
- clusters:
selector:
matchLabels:
cluster-role: edge
template:
metadata:
name: 'django-backend-{{name}}' # e.g. django-backend-overtime-staging
labels:
category: apps
target-cluster: '{{name}}'
annotations:
# Only refresh this app when files in its own overlay path change.
# Prevents a single monorepo commit from fanning out to all apps.
argocd.argoproj.io/manifest-generate-paths: "."
spec:
project: apps
source:
repoURL: 'https://gitlab.com/alexb-overtime/overtime-argocd.git'
targetRevision: HEAD
path: 'apps/django-backend/overlays/{{name}}'
destination:
server: '{{server}}'
namespace: django-backend
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
The App-of-Apps Pattern¶
ArgoCD is bootstrapped exactly once to watch the root-app/ directory. Each ApplicationSet defined in root-app/ acts as a generator. It creates one ArgoCD Application per matching target cluster using Kubernetes cluster label selectors.
Adding a new cluster to the corresponding label group automatically provisions all assigned applications to it — no manual ArgoCD UI configuration is required.
Write Access Model¶
Only one credential requires write access to overtime-gitops: the GITOPS_DEPLOY_TOKEN. This is a GitLab deploy token used exclusively by the .update-gitops CI job. Application source repos (e.g., overtime-backend) have absolutely no write access to overtime-gitops.
[overtime-backend CI] → writes to → [overtime-gitops] (Image tag update only)
↓ (ArgoCD polls git)
[Target Cluster State]
Rollback Strategy¶
Rolling back a deployment is entirely Git-native. It simply requires a git revert of the specific automated image tag commit within overtime-gitops. ArgoCD syncs the previous tag automatically within its standard polling interval. No kubectl rollout undo or Helm rollback mechanics are needed or permitted.
4. Compliance Einordnung (Deutsch)¶
Das detaillierte Mapping auf BSI IT-Grundschutz, CRA und CIS Benchmarks wird später ergänzt.