← Terug naar tutorials

DevOps: CI/CD, Infrastructure as Code en Observability

devopsci-cdinfrastructure-as-codekubernetesobservability

DevOps: CI/CD, Infrastructure as Code en Observability

DevOps is geen tool, maar een manier van werken die ontwikkeling (Dev) en beheer (Ops) samenbrengt om sneller, betrouwbaarder en herhaalbaar software te leveren. In deze tutorial bouwen we een praktisch en samenhangend beeld op van drie pijlers die in moderne DevOps-teams vrijwel altijd terugkomen:

  1. CI/CD (Continuous Integration / Continuous Delivery & Deployment) – geautomatiseerd bouwen, testen en uitrollen.
  2. Infrastructure as Code (IaC) – infrastructuur beschrijven als code voor herhaalbaarheid en auditability.
  3. Observability – inzicht in gedrag en gezondheid van systemen via metrics, logs en traces.

De focus ligt op diep begrip én echte commando’s die je kunt uitvoeren. Voorbeelden gebruiken Linux/macOS shell. Waar relevant noem ik alternatieven.


Inhoud


1. Basisprincipes van DevOps

DevOps draait om het verkorten van de feedbackloop: van code schrijven naar veilig in productie draaien, met meetbare kwaliteit. De belangrijkste principes:

Een nuttige mentale keten is:

Code → Build → Test → Package → Release → Deploy → Run → Observe → Improve

CI/CD automatiseert de keten, IaC maakt de “Deploy/Run”-laag reproduceerbaar, en Observability sluit de feedbackloop.


2. CI: Continuous Integration in de praktijk

Continuous Integration betekent: vaak integreren, automatisch bouwen, automatisch testen, en snel feedback. CI is pas waardevol als het betrouwbaar is: een “groene pipeline” moet echt betekenen dat de wijziging veilig is om te promoten.

2.1 Git-strategie en branch policies

Een simpele, effectieve setup:

Handige Git-commando’s:

# Nieuwe feature branch
git checkout -b feature/login-rate-limit

# Wijzigingen committen
git add .
git commit -m "Voeg rate limiting toe aan login endpoint"

# Push naar origin
git push -u origin feature/login-rate-limit

Belangrijk: houd PR’s klein. Als je PR 2000 regels wijzigt, is de kans groot dat review en testen gaten laten vallen.

2.2 Build, unit tests en linting

CI moet minimaal:

  1. Dependencies installeren
  2. Linting/formatting checken
  3. Unit tests draaien
  4. Build maken (binary, package of container)
  5. Resultaten publiceren (test reports, artifacts)

Voorbeeld met Node.js (conceptueel; je kunt dit vertalen naar Java/Maven, .NET, Go, Python):

# Schone install (reproduceerbaar)
npm ci

# Lint
npm run lint

# Unit tests met coverage
npm test -- --coverage

# Build
npm run build

Voor Java met Maven:

mvn -B -DskipTests=false clean test
mvn -B package

Diepgang: waarom “reproduceerbaar” belangrijk is

2.3 Artifact management

Een artifact is de output van CI: een JAR, wheel, container image, of static build. In CD wil je hetzelfde artifact promoten door omgevingen (dev → test → prod), niet steeds opnieuw bouwen. Dit voorkomt “works on staging” problemen door build-variatie.

Praktisch voorbeeld: een buildbestand archiveren:

mkdir -p dist
cp -r build/* dist/
tar -czf app-build.tar.gz dist
sha256sum app-build.tar.gz > app-build.tar.gz.sha256

Je kunt artifacts opslaan in:

2.4 Container builds met Docker

Containers zijn populair omdat ze runtime-omgeving en dependencies bundelen.

Bouw een image:

docker build -t myapp:1.0.0 .

Inspecteer images:

docker images | head
docker history myapp:1.0.0

Run lokaal:

docker run --rm -p 8080:8080 myapp:1.0.0

Push naar een registry (voorbeeld met GitHub Container Registry):

# Login (gebruik bij voorkeur een token)
echo "$GITHUB_TOKEN" | docker login ghcr.io -u USERNAME --password-stdin

# Tag en push
docker tag myapp:1.0.0 ghcr.io/username/myapp:1.0.0
docker push ghcr.io/username/myapp:1.0.0

Best practice: immutable tags


3. CD: Continuous Delivery & Deployment

CD gaat over het veilig en herhaalbaar uitrollen naar omgevingen. Er is een belangrijk onderscheid:

3.1 Omgevingen en promotie

Typische omgevingen:

Belangrijk: promotie betekent hetzelfde artifact. Dus:

  1. CI bouwt myapp:git-abc123
  2. CD deployt myapp:git-abc123 naar dev
  3. Na checks deployt CD hetzelfde image naar staging en prod

3.2 Deploy-strategieën

Rolling update

Blue/Green

Canary

Praktisch: Kubernetes rollout commando’s (als je Kubernetes gebruikt):

# Deploy toepassen
kubectl apply -f k8s/deployment.yaml

# Rollout status volgen
kubectl rollout status deployment/myapp

# Terugdraaien
kubectl rollout undo deployment/myapp

# Huidige versies bekijken
kubectl get rs -l app=myapp
kubectl describe deployment myapp

3.3 Database changes en migraties

Database-migraties zijn vaak de bron van deploy-problemen. Een paar regels die veel ellende voorkomen:

Voorbeeld met Flyway (Java-ecosysteem):

# Migraties uitvoeren (voorbeeld)
flyway -url=jdbc:postgresql://localhost:5432/app \
  -user=app -password=secret \
  migrate

# Status
flyway info

Voorbeeld met PostgreSQL direct:

psql "postgresql://app:secret@localhost:5432/app" -c "SELECT now();"
psql "postgresql://app:secret@localhost:5432/app" -f migrations/2026_04_28_add_index.sql

4. Infrastructure as Code (IaC)

4.1 Waarom IaC essentieel is

Zonder IaC krijg je “snowflake servers”: handmatig ingerichte machines die uniek en onherhaalbaar zijn. IaC maakt infrastructuur:

IaC is niet alleen “servers”; ook:

4.2 Terraform: kernconcepten

Terraform is een veelgebruikte IaC-tool die declaratief beschrijft wat je wilt. Kernbegrippen:

Hoewel Terraform-configuratie zelf in .tf staat, kun je de workflow volledig via commando’s begrijpen en beheersen.

4.3 Terraform workflow met echte commando’s

Installatie check:

terraform version

Initialiseer een project (download providers, initialiseer backend):

terraform init

Valideer syntaxis en basisregels:

terraform validate

Formatteer (consistentie in PR’s):

terraform fmt -recursive

Bekijk wat er gaat gebeuren:

terraform plan

Voer uit:

terraform apply

Met auto-approve (alleen in gecontroleerde CI-contexten):

terraform apply -auto-approve

Resources inspecteren:

terraform state list
terraform show

Een specifieke resource opnieuw aanmaken (voorzichtig!):

terraform taint aws_instance.web
terraform apply

Diepgang: plan/apply discipline

Plan opslaan:

terraform plan -out=tfplan

Apply exact dat plan:

terraform apply tfplan

4.4 State, locking en remote backends

Terraform state is cruciaal. Als je lokaal state bewaart en met meerdere mensen werkt, krijg je race conditions en corrupte state. Daarom:

Handige commando’s:

# Trek state naar lokaal (alleen voor inspectie)
terraform state pull > state.json

# Push state (zeldzaam; alleen bij herstel)
terraform state push state.json

Drift detecteren:

terraform plan -refresh-only

4.5 Secrets en configuratie

Een klassieke valkuil is secrets in Git. Richtlijnen:

Praktisch: environment variables gebruiken voor tools:

export DB_PASSWORD="$(security find-generic-password -a app -s db_password -w 2>/dev/null || true)"

Of met pass (Linux password store):

export DB_PASSWORD="$(pass show prod/db_password)"

Voor Kubernetes secrets (let op: base64 is geen encryptie):

kubectl create secret generic db-secret \
  --from-literal=password="$DB_PASSWORD" \
  -n myapp

5. Observability

Observability is het vermogen om de interne toestand van een systeem af te leiden uit externe signalen. In praktijk draait het om drie soorten telemetry:

5.1 Metrics, logs en traces (en waarom alle drie)

Metrics zijn goed voor:

Logs zijn goed voor:

Traces zijn goed voor:

Alleen metrics geven geen details; alleen logs zijn te veel ruis; alleen traces missen soms brede trends. Samen vormen ze een compleet beeld.

5.2 SLI/SLO/SLA en error budgets

Een volwassen DevOps-organisatie stuurt op betrouwbaarheid met:

Error budget: als je SLO 99,9% is, is je budget 0,1% fouten. Dat budget “mag je opmaken” voor deploys en experimenten. Is het op? Dan focus op stabiliteit.

Voorbeeld: 99,9% per 30 dagen betekent maximaal:

Dit maakt trade-offs concreet: een riskante migratie vlak voor een piekperiode kan je budget opblazen.

5.3 Praktisch: logs inspecteren en metrics opvragen

Logs met journald (Linux):

# Laatste 100 regels van een service
sudo journalctl -u myapp.service -n 100 --no-pager

# Volgen (tail -f)
sudo journalctl -u myapp.service -f

Nginx access logs (voorbeeld):

sudo tail -n 200 /var/log/nginx/access.log
sudo grep " 500 " /var/log/nginx/access.log | tail -n 50

Container logs:

docker logs --tail 200 -f <container_id>

Kubernetes logs:

kubectl logs -n myapp deploy/myapp --tail=200
kubectl logs -n myapp deploy/myapp -c myapp --since=10m

Metrics endpoint (Prometheus-stijl)

Veel apps exposen metrics op /metrics. Je kunt dit direct testen:

curl -s http://localhost:8080/metrics | head -n 40

Als je Prometheus draait, kun je targets debuggen door de endpoint te testen vanaf de Prometheus pod (voorbeeld):

kubectl -n monitoring exec -it deploy/prometheus -- sh -lc \
  "apk add --no-cache curl >/dev/null 2>&1 || true; curl -s http://myapp.myapp.svc.cluster.local:8080/metrics | head"

5.4 Distributed tracing en correlation IDs

In microservices is één user request vaak 10+ interne calls. Zonder tracing zie je alleen losse logs. Met tracing voeg je een trace-id toe die overal terugkomt.

Correlation ID in HTTP

Praktisch testen met curl:

REQ_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
curl -v -H "X-Request-ID: $REQ_ID" http://localhost:8080/api/orders

Zoek vervolgens in logs:

kubectl logs -n myapp deploy/myapp --since=30m | grep "$REQ_ID"

Diepgang: waarom dit helpt

5.5 Alerting zonder ruis

Alert fatigue is echt: te veel alerts → niemand reageert. Richtlijnen:

Praktisch: simpele drempel-check met shell (niet als vervanging, wel als debug):

# Check of endpoint gezond is
curl -fsS http://localhost:8080/health || echo "HEALTHCHECK FAIL"

Latency meten:

curl -o /dev/null -s -w "time_total=%{time_total}\nhttp_code=%{http_code}\n" \
  http://localhost:8080/api/orders

6. Alles samen: een end-to-end flow

Hier is een realistische flow die CI/CD, IaC en Observability verbindt:

  1. Developer maakt feature branch
    • Code + tests + (optioneel) migratie
  2. CI pipeline
    • npm ci / mvn test / go test
    • build container image
    • security checks (dependency scan)
    • push image myapp:git-<sha> naar registry
  3. IaC pipeline (optioneel apart)
    • terraform fmt/validate/plan
    • review van plan in PR
    • terraform apply na approval
  4. CD pipeline
    • deploy image naar dev namespace
    • smoke tests (HTTP checks)
    • promotie naar staging
    • canary of blue/green naar prod
  5. Observability
    • dashboards tonen latency/error rate
    • traces bevestigen dat nieuwe code geen regressie introduceert
    • alerts op SLO burn rate

Praktisch: “smoke test” script na deploy:

set -euo pipefail

BASE_URL="${1:-http://localhost:8080}"

echo "Healthcheck..."
curl -fsS "$BASE_URL/health" >/dev/null

echo "API check..."
curl -fsS "$BASE_URL/api/version"

echo "Latency sample..."
curl -o /dev/null -s -w "time_total=%{time_total}\n" "$BASE_URL/api/orders"

Run:

bash smoke.sh https://myapp-staging.example.com

7. Checklist en best practices

CI/CD

IaC

Observability


Afsluiting

CI/CD, IaC en Observability versterken elkaar: CI/CD levert snel en gecontroleerd, IaC maakt omgevingen voorspelbaar en reproduceerbaar, en Observability zorgt dat je met vertrouwen kunt veranderen omdat je direct ziet wat de impact is. Als je één onderdeel overslaat, wordt de hele keten fragiel: snelle deploys zonder observability zijn gokken, IaC zonder CI discipline wordt drift, en observability zonder betrouwbare releases maakt incidenten eindeloos.

Als je wilt, kan ik deze tutorial uitbreiden met een concreet voorbeeldproject (bijv. een kleine API), inclusief Dockerfile, Terraform-structuur, en commando’s voor een lokale observability stack (Prometheus/Grafana/Jaeger) — allemaal in Markdown.