Een paar jaar lang draaide mijn homelab cluster zonder veel drama. Nodes kwamen en gingen, workloads schoven rond, en het ergste waar ik mee te maken kreeg was af en toe een pod die vastzat in CrashLoopBackOff. Het soort stabiliteit waarbij je stopt met nadenken over wat er gebeurt als het allemaal verdwijnt.
Toen runde ik op een avond een terraform destroy tegen de verkeerde workspace. Tegen de tijd dat ik het doorhad, was de control plane weg. Niet degraded. Weg.
Dat is het moment waarop disaster recovery ophoudt een planningssheet te zijn en een vraag wordt die je nu meteen moet beantwoorden: het cluster is weg, de hardware is kapot, de regio is down, of je hebt een destroy verkeerd ingetypt. Wat doe je hierna?
Als je GitOps goed hebt gedaan, is het antwoord bijna saai. Je provisiont een vers cluster, wijst ArgoCD naar Git, en wacht. Je infrastructuur bouwt zichzelf opnieuw op vanuit de repository. Dat is de hele belofte: Git is je backup.
Ik wil doorlopen waarom dat werkt, wat waar moet zijn om het te laten werken, en waar het je stilletjes in de steek laat. Want de eerste keer dat ik dit echt testte, leerde ik dat “Git is je backup” waar is voor infrastructuur en een gevaarlijke leugen voor data.
Waarom GitOps disaster recovery verandert
Traditionele DR is een tweede kopie van iets primairs. Je neemt het cluster, je snapshot etcd, je draait Velero, je verstuurt die backups ergens veilig, en je houdt een restore procedure aan die je hopelijk hebt getest. Recovery tijd wordt gemeten in uren, en de meeste van die uren gaan naar het terugkopiëren van state.
GitOps draait om welk ding primair is. Het cluster stopt de bron van waarheid te zijn en wordt een afgeleid artefact. De echte state leeft in Git. Het cluster is gewoon de huidige rendering van die state, en je kunt het weggooien en opnieuw renderen.
flowchart TD
subgraph traditioneel["Traditioneel"]
TC["Cluster<br/>(primair)"] -->|backup| TB["Backup<br/>(secundair)"]
end
subgraph gitops["GitOps"]
GG["Git<br/>(primair)"] -->|sync| GC["Cluster<br/>(afgeleid)"]
end
Zodra je dat internaliseert, verandert recovery van vorm. Je herstelt geen cluster. Je bootstrapt een nieuwe en laat die reconcilen. Recovery tijd daalt naar minuten, omdat er geen aparte backup is om terug te kopiëren. Dit is resilience by design in plaats van resilience er achteraf op geschroefd: het systeem dat je cluster bouwt is hetzelfde systeem dat het herbouwt.
Het past ook bij hoe ik mijn infrastructuur wil begrijpen. Als het hele ding gedeclareerd staat in een repo die ik kan lezen, zijn er geen verborgen stukjes state die alleen bestaan omdat iemand om 2 uur ’s nachts een kubectl apply draaide en het nooit opschreef.
Wat er in Git moet staan om dit te laten werken
Hier zit de adder, en het is waar de meeste GitOps setups stilletjes hun eigen DR test falen: dit werkt alleen als alles echt in Git staat. Alles wat alleen in het draaiende cluster bestaat, ben je kwijt en moet je met de hand opnieuw aanmaken, onder druk, terwijl productie down is.
Dus voordat we recovery doorlopen, lopen we eerst door wat er in de repo hoort te leven.
Application manifests
Al je workload definities. Deployments, Services, ConfigMaps, Helm charts met hun values files, Kustomize overlays. Dit is het makkelijke deel, en de meeste teams hebben dit al. Als je überhaupt GitOps doet, staan je apps hier.
Infrastructuur configuratie
De cluster-level resources die makkelijk te vergeten zijn omdat niemand ze als onderdeel van een release deployt:
- Namespaces
- RBAC (Roles, RoleBindings, ServiceAccounts)
- NetworkPolicies
- ResourceQuotas
- PodSecurityPolicies / Pod Security Standards
Deze definiëren je security grenzen en isolatie. Een cluster dat je herbouwde zonder zijn NetworkPolicies is een cluster met de blast doors open. Behandel ze als eersterangs GitOps content, niet als bijzaak.
Platform componenten
De tools waarvan je applicaties aannemen dat ze er al zijn:
- Ingress controllers
- Cert-manager
- Monitoring stack (Prometheus, Grafana)
- Logging (Loki, Fluentd)
- Service mesh, als je er een draait
Staan deze niet in Git, dan komen je apps terug maar routeert er niets naartoe, heeft niets een certificaat, en vlieg je blind omdat je observability stack nooit deployde.
Secrets strategie
Secrets zijn het ene ding dat je echt niet in plaintext kunt committen, dus ze hebben een strategie nodig die het verlies van het cluster overleeft:
- Sealed Secrets: versleuteld in Git, ontsleuteld in-cluster door een controller
- External Secrets: tijdens runtime opgehaald uit Vault of een cloud secrets manager
- SOPS: bestanden versleuteld met keys die jij beheert
De eigenschap die telt voor DR is reproduceerbaarheid. Een secret dat alleen binnen het dode cluster bestaat, ben je kwijt. Welke tool je ook kiest, de versleutelde of gerefereerde vorm moet in Git staan en de decryptie key moet ergens leven waar je nog bij kunt nadat het cluster weg is.
Persistent data
Hier is de harde waarheid die ik op de vervelende manier leerde: GitOps backupt je data niet.
Databases, object storage, alles met state, niets daarvan staat in Git en niets daarvan komt terug wanneer ArgoCD synct. GitOps herbouwt de infrastructuur die je data draait. De data zelf heeft een aparte, geteste backup strategie nodig, en als je die stap overslaat herbouw je een perfect, leeg cluster en word je er misselijk van.
Het recovery proces
Met dat alles in Git wordt recovery een sequentie die je daadwerkelijk kunt draaien. Dit is het pad dat ik volg.
Stap 0: heb een recovery playbook
Voordat er iets breekt, schrijf op hoe je terugkomt:
- Hoe je een vers cluster provisiont
- De bootstrap commands voor ArgoCD
- Waar Vault leeft en waar zijn backups zijn
- Externe dependencies (DNS, load balancers)
En draai het dan. Een playbook dat je nooit hebt uitgevoerd is fictie, en je wilt de gaten niet ontdekken terwijl productie down is. De eerste keer dat ik de mijne “testte”, vond ik drie ongedocumenteerde handmatige stappen die me een uur hadden gekost op het slechtst denkbare moment.
Stap 1: provision een nieuw cluster
Met welke methode dan ook, zorg voor een werkend Kubernetes cluster:
# Terraform
cd terraform/clusters/production
terraform apply
# Of managed Kubernetes
gcloud container clusters create production-recovery \
--region europe-west1 \
--num-nodes 3
# Of bare metal
talosctl apply-config --nodes 10.0.0.10 --file controlplane.yaml
Hoe je aan het cluster komt staat bewust los van GitOps. GitOps neemt het over zodra je een werkende API server hebt om mee te praten.
Stap 2: installeer ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Wacht tot het ready is
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s
Stap 3: herstel secrets toegang
Verbind External Secrets terug met Vault zodat de rest van de sync de credentials heeft die het nodig heeft:
# Configureer Vault authenticatie voor het nieuwe cluster
kubectl create secret generic vault-token \
-n external-secrets \
--from-literal=token=$VAULT_TOKEN
Stap 4: verbind ArgoCD met Git
# Voeg je GitOps repository toe
argocd repo add https://github.com/yourorg/gitops-repo.git \
--username git \
--password $GIT_TOKEN
# Of via Secret
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: gitops-repo
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
stringData:
url: https://github.com/yourorg/gitops-repo.git
username: git
password: ${GIT_TOKEN}
EOF
Stap 5: apply de root application
Als je het App-of-Apps pattern gebruikt (dat zou je moeten), trekt één manifest al het andere binnen:
kubectl apply -f - <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/yourorg/gitops-repo.git
targetRevision: main
path: clusters/production
destination:
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: true
selfHeal: true
EOF
Stap 6: wacht
Dit is het deel dat voor mij nog steeds een beetje magisch voelt. ArgoCD reconcilet alles op eigen houtje:
- Ontdekt alle applicaties onder de root app
- Maakt namespaces en RBAC aan
- Deployt platform componenten
- Deployt je applicaties
Kijk het aan het werk:
argocd app list
argocd app get root --refresh
Of in de UI: kubectl port-forward svc/argocd-server -n argocd 8080:443
Stap 7: herstel data
Dit is waar je aparte data backup strategie zich terugverdient:
# Herstel database
pg_restore -d myapp < /backups/myapp-latest.dump
# Of Velero restore
velero restore create --from-backup production-daily-latest
Stap 8: update DNS en load balancers
Wijs extern verkeer naar het nieuwe cluster:
- Update DNS records
- Herconfigureer CDN origins
- Update load balancer targets
Hoe snel kun je echt herstellen
Het eerlijke antwoord is “het hangt ervan af, en de afhankelijkheid is je data”. Voor de infrastructuur helft is dit wat ik meestal zie:
| Fase | Tijd |
|---|---|
| Cluster provisioning | 5-15 min |
| ArgoCD installatie | 2-3 min |
| Secrets setup | 1-2 min |
| GitOps sync (klein cluster) | 5-10 min |
| GitOps sync (groot cluster) | 15-30 min |
| Data restore | Varieert sterk |
| DNS propagatie | 5-60 min |
Infrastructuur totaal: 20-60 minuten.
Data restore is de wildcard. Het schaalt met je data volume en je backup ontwerp, en het is bijna altijd het langste stuk van de recovery. Het schone, snelle deel is het GitOps deel. Het trage, angstige deel is het data deel, precies het deel dat GitOps niet aanraakt.
Waar het je bijt
Ik ben de meeste van deze op de harde manier tegengekomen, dus hier is wat er daadwerkelijk misgaat.
Ontbrekende secrets
Als Vault onbereikbaar is, kan External Secrets niets ophalen en starten je workloads nooit. Je herbouwt het hele cluster en kijkt dan toe hoe elke pod wacht op een secret dat niet aankomt.
Mitigatie: draai Vault in HA op infrastructuur die los staat van het cluster dat het bedient, back het regelmatig op, en overweeg het te repliceren naar een tweede locatie.
Externe dependencies
Je cluster leunt waarschijnlijk op dingen die helemaal niet in Git staan:
- Cloud IAM roles
- DNS zones
- SSL certificaten van Let’s Encrypt
- Externe databases
Mitigatie: documenteer elke externe dependency, en beter nog, beheer ze met Terraform of Crossplane in dezelfde repo zodat ze samen met al het andere herbouwen.
Circulaire dependencies
ArgoCD moet draaien voordat het ArgoCD kan deployen. Het klassieke bootstrap probleem, en het laat je struikelen als je enige kopie van “hoe installeer ik ArgoCD” een door-ArgoCD-beheerde applicatie was.
Mitigatie: houd een bootstrap script aan dat werkt met kale kubectl, geen ArgoCD vereist. Zodra ArgoCD draait, laat het zichzelf vanaf daar beheren.
Data verlies
GitOps herbouwt infrastructuur, geen data. Een verouderde database backup betekent dat je het cluster herstelt en alsnog uren of dagen aan data verliest.
Mitigatie: een aparte, geteste data backup strategie met echte restore tests, niet alleen backup jobs waarvan je aanneemt dat ze werken.
Het recovery manifest
Na mijn eigen bijna-ongeluk begon ik één bestand bij te houden dat alles documenteert wat recovery nodig heeft, op één plek. Als de boel in brand staat, wil je niet door een repo grep-en op zoek naar het Vault adres.
# recovery-manifest.yaml
recovery:
cluster:
provisioner: terraform
path: terraform/clusters/production
secrets:
method: external-secrets
vault-address: https://vault.internal.company.com
vault-backup: s3://backups/vault-snapshots/
gitops:
repo: https://github.com/yourorg/gitops-repo.git
path: clusters/production
root-app: apps/root.yaml
data:
databases:
- name: postgres-main
backup-location: s3://backups/postgres/
restore-command: |
pg_restore -d main /backups/latest.dump
volumes:
- name: uploads
backup-location: s3://backups/uploads/
external-dependencies:
- DNS zone: beheerd in Route53, terraform/dns/
- Load balancer: aangemaakt door terraform/clusters/
- Vault: vault.internal.company.com (HA, aparte infra)
contacts:
- oncall: ops-team@company.com
- escalation: platform-lead@company.com
Commit het naar Git. Review het per kwartaal. Test het minstens één keer per jaar.
De mindset die dit vereist
GitOps DR vraagt je om anders over je clusters te denken, en die verschuiving is het waard om hardop te zeggen:
Clusters zijn vee, geen huisdieren. Als recovery goedkoop is, stop je met het krampachtig beschermen van elk individueel cluster.
State hoort in Git. Elke
kubectl applydie je draait zonder de wijziging te committen is DR schuld die op het slechtste moment vervalt.Data is zijn eigen probleem. GitOps handelt infrastructuur netjes af en doet nooit alsof het je data afhandelt. Die grens is het belangrijkste om te onthouden.
Oefen recovery. De eerste keer dat je een echte recovery draait, zou niet tijdens een echte disaster moeten zijn.
Mijn recovery checklist
## Pre-Disaster (doe nu)
- [ ] Alle manifests in Git
- [ ] Secrets strategie gedocumenteerd en getest
- [ ] Vault gebackupt en HA geconfigureerd
- [ ] Recovery playbook geschreven
- [ ] Externe dependencies gedocumenteerd
- [ ] Data backup strategie geïmplementeerd
- [ ] Recovery getest in laatste 90 dagen
## Tijdens Recovery
- [ ] Provision nieuw cluster
- [ ] Installeer ArgoCD
- [ ] Herstel secrets decryptie capability
- [ ] Verbind ArgoCD met Git
- [ ] Apply root application
- [ ] Monitor sync voortgang
- [ ] Herstel data van backups
- [ ] Update DNS/load balancers
- [ ] Verifieer applicatie health
- [ ] Notificeer stakeholders
De beste disaster recovery is degene die je nooit nodig hebt. De op een na beste is degene die je vaak genoeg hebt geoefend dat wanneer het eindelijk echt gebeurt, het voelt als gewoon weer een doordeweekse dag.
