← Terug naar tutorials

DevOps: CI/CD, Infrastructure as Code en Observability

devopsci-cdinfrastructure-as-codekubernetesobservabilitymonitoringterraformsre

DevOps: CI/CD, Infrastructure as Code en Observability

DevOps is geen tool en ook geen functietitel; het is een manier van werken waarin development en operations samenwerken om software sneller, betrouwbaarder en herhaalbaar te leveren. In deze tutorial bouwen we een end-to-end beeld van drie kernpijlers:

  1. CI/CD (Continuous Integration / Continuous Delivery): automatisch bouwen, testen en uitrollen.
  2. Infrastructure as Code (IaC): infrastructuur definieer je als code, versiebeheerbaar en reproduceerbaar.
  3. Observability: je systeem “begrijpen” via metrics, logs en traces, zodat je problemen sneller vindt en performance kunt verbeteren.

We gaan diep in op concepten én laten echte commando’s zien met gangbare tooling: Git, Docker, GitHub Actions, Terraform, Kubernetes (optioneel), Prometheus, Grafana, Loki en OpenTelemetry.


Inhoud


1. Voorbereiding en uitgangspunten

Wat je nodig hebt (lokaal)

Controleer je installaties:

git --version
docker --version
terraform version

Voorbeeldapplicatie (simpel maar realistisch)

We nemen een kleine webservice als voorbeeld (bijv. Node.js/Express of Python/FastAPI). De exacte code is minder belangrijk dan de pipeline eromheen. Stel dat je repo deze structuur heeft:

.
├── app/
│   ├── src/
│   ├── package.json
│   └── package-lock.json
├── Dockerfile
├── .github/workflows/
└── terraform/

Een simpele Dockerfile (Node.js):

FROM node:20-alpine

WORKDIR /app
COPY app/package*.json ./
RUN npm ci --omit=dev

COPY app/src ./src
EXPOSE 3000

CMD ["node", "src/index.js"]

Build lokaal:

docker build -t demo-app:local .
docker run --rm -p 3000:3000 demo-app:local
curl -i http://localhost:3000/health

2. CI: Continuous Integration in de praktijk

Continuous Integration betekent: elke wijziging wordt automatisch gebouwd en getest, liefst bij elke push en bij elke pull request. Doelen:

CI-stappen die je vrijwel altijd wilt

  1. Checkout van de code.
  2. Dependencies installeren (met caching).
  3. Linting (stijl en eenvoudige fouten).
  4. Unit tests (snel, deterministisch).
  5. Build (artifact of container image).
  6. Security checks (SCA, secrets scanning).
  7. Publiceer artifact (bijv. Docker image naar registry).

Voorbeeld: GitHub Actions CI (commando’s zijn echt)

Maak .github/workflows/ci.yml met stappen zoals:

Belangrijke commando’s die je lokaal ook kunt draaien:

cd app
npm ci
npm run lint
npm test

Container build:

docker build -t ghcr.io/<org>/<repo>:sha-$(git rev-parse --short HEAD) .

Tests in containers (voordelen en valkuilen)

Voordeel: je test in een omgeving die dichter bij productie ligt.
Valkuil: tests kunnen trager worden en je moet goed omgaan met caching.

Een patroon is om tests “host-based” te doen (snel) en de container build apart.

Security in CI: dependency scanning en secrets

Voor Node kun je minimaal draaien:

cd app
npm audit --audit-level=high

En voor container images (met Trivy):

trivy image demo-app:local

3. CD: Continuous Delivery/Deployment

Continuous Delivery: je kunt op elk moment deployen met één druk op de knop (of automatisch), omdat je pipeline betrouwbaar is.
Continuous Deployment: elke change die door CI komt gaat automatisch naar productie.

In de praktijk kiezen teams vaak voor:

Release-strategieën

  1. Rolling update: geleidelijk vervangen van pods/instances.
  2. Blue/Green: twee omgevingen, switch verkeer in één keer.
  3. Canary: klein percentage verkeer naar nieuwe versie, daarna opschalen.

Versioning en immutability

Gebruik immutable artifacts: een image-tag die exact één build representeert (bijv. git SHA). “latest” is handig, maar onbetrouwbaar voor rollback.

Voorbeeld tags:

Taggen:

git tag -a v1.4.2 -m "Release v1.4.2"
git push origin v1.4.2

Deploy commando’s (voorbeeld Kubernetes)

Stel je hebt een deployment:

kubectl get ns
kubectl -n demo get deploy,po,svc

Update image:

kubectl -n demo set image deployment/demo-app demo-app=ghcr.io/org/repo:sha-1a2b3c4
kubectl -n demo rollout status deployment/demo-app

Rollback:

kubectl -n demo rollout undo deployment/demo-app
kubectl -n demo rollout status deployment/demo-app

Database migraties in CD

Migrations zijn vaak de bron van downtime. Basisregels:

Voorbeeld (conceptueel) stap:

# voorbeeld: migration job in cluster
kubectl -n demo apply -f k8s/migrate-job.yaml
kubectl -n demo logs job/demo-migrate -f

4. Infrastructure as Code met Terraform

IaC betekent: je definieert infrastructuur (netwerken, VM’s, databases, IAM, load balancers) in code. Voordelen:

Terraform kernconcepten

Belangrijke commando’s:

terraform init
terraform fmt -recursive
terraform validate
terraform plan
terraform apply
terraform destroy

State management (kritisch!)

Gebruik in teams vrijwel altijd remote state met locking, anders krijg je race conditions.

Zonder YAML te gebruiken, conceptueel:

Voorbeeld: simpele resource (AWS S3) (conceptueel HCL)

In terraform/main.tf (HCL, geen YAML):

terraform {
  required_version = ">= 1.6.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

resource "aws_s3_bucket" "artifacts" {
  bucket = "${var.project}-artifacts-${var.env}"
}

resource "aws_s3_bucket_versioning" "artifacts" {
  bucket = aws_s3_bucket.artifacts.id
  versioning_configuration {
    status = "Enabled"
  }
}

Variabelen in terraform/variables.tf:

variable "project" {
  type = string
}

variable "env" {
  type = string
}

variable "aws_region" {
  type    = string
  default = "eu-west-1"
}

Run:

cd terraform
terraform init
terraform fmt -recursive
terraform validate
terraform plan -var="project=demo" -var="env=dev"
terraform apply -var="project=demo" -var="env=dev"

Modules en omgevingen

Een volwassen structuur:

Belangrijk: voorkom “copy-paste infra” die uiteen gaat lopen. Gebruik modules en variabelen.

Drift detection

Drift = infra is handmatig aangepast buiten Terraform om. Detecteer dit door regelmatig:

terraform plan

In CI kun je een scheduled job draaien die een plan maakt en waarschuwt bij drift.


5. Configuratie, secrets en omgevingen

Config ≠ code

Secrets management

Opties:

Praktische regels:

Voorbeeld: secret als environment variable (runtime), niet baked in image:

docker run --rm -e DATABASE_URL="postgres://..." demo-app:local

Feature flags

Feature flags maken deployments veiliger:


6. Observability: metrics, logs en traces

Monitoring vertelt je dat iets stuk is; observability helpt je begrijpen waarom. De drie pilaren:

  1. Metrics: tijdreeksen (CPU, latency, error rate).
  2. Logs: gebeurtenissen (structured logging).
  3. Traces: request flows door services (distributed tracing).

6.1 Metrics met Prometheus

Prometheus scrapt metrics endpoints (meestal /metrics) en slaat tijdreeksen op. Je definieert alerts op basis van queries (PromQL).

Voorbeeld: draai Prometheus lokaal met Docker (simpel):

docker network create obs || true
docker run -d --name prometheus --network obs -p 9090:9090 prom/prometheus

In productie gebruik je meestal Helm of een operator, maar het concept blijft:

Wat meet je minimaal?

6.2 Logs: structured logging en centralisatie

Structured logs (JSON) zijn beter doorzoekbaar dan vrije tekst. Basisvelden:

Lokaal kun je logs bekijken:

docker logs -f <container>

Voor centralisatie kun je een stack gebruiken zoals Loki + Grafana of ELK/EFK.

Voorbeeld: Loki draaien:

docker run -d --name loki --network obs -p 3100:3100 grafana/loki:2.9.0

In Kubernetes stuur je logs vaak via Promtail/Fluent Bit naar Loki/Elastic.

6.3 Traces met OpenTelemetry

Distributed tracing is essentieel zodra je meerdere services hebt. Met OpenTelemetry (OTel) instrumenteer je je app zodat elke request een trace krijgt met spans.

Belangrijke begrippen:

Een OTel Collector kan traces ontvangen en doorsturen naar backends zoals Jaeger, Tempo of commerciële APM.

Voorbeeld: Jaeger all-in-one lokaal:

docker run -d --name jaeger --network obs \
  -p 16686:16686 -p 4317:4317 -p 4318:4318 \
  jaegertracing/all-in-one:1.57

Dan configureer je je app om OTLP te exporteren naar http://jaeger:4318 (HTTP) of jaeger:4317 (gRPC), afhankelijk van je SDK.

Correlatie: logs + traces + metrics

De echte winst komt als je kunt klikken:

Daarvoor heb je nodig:

Alerting: wat is een goede alert?

Een alert moet:

Voorbeelden van nuttige alerts:


7. SLO’s, incidenten en feedback loops

SLI/SLO/SLA in het kort

Een praktisch SLO-voorbeeld:

Error budgets

Als je SLO 99.9% is, heb je 0.1% “budget” voor fouten. Dat budget helpt beslissen:

Incident response

Minimale onderdelen:

Runbooks zijn cruciaal: korte stappen die iemand om 03:00 kan volgen.

Voorbeeld runbook-stappen bij verhoogde 5xx:

1) Check dashboard: error rate per route en per versie
2) Check recente deploys: welke image SHA draait?
3) Rollback indien correlatie met deploy
4) Check dependency health (DB, cache, externe API)
5) Verzamel: trace_id voorbeelden + relevante logs
6) Maak incident timeline en actiepunten

8. Praktische checklist en next steps

CI/CD checklist

IaC checklist

Observability checklist


Conclusie

CI/CD, IaC en observability versterken elkaar:

Als je dit als één systeem benadert (in plaats van losse tools), krijg je een delivery machine die niet alleen snel is, maar ook stabiel en goed te opereren.


Extra: handige commando’s (spiekbrief)

Git

git status
git diff
git log --oneline --decorate -n 20
git tag -l

Docker

docker build -t demo-app:local .
docker run --rm -p 3000:3000 demo-app:local
docker ps
docker logs -f <container>
docker exec -it <container> sh

Kubernetes

kubectl get nodes
kubectl get ns
kubectl -n demo get all
kubectl -n demo describe pod <pod>
kubectl -n demo logs -f deploy/demo-app
kubectl -n demo rollout status deploy/demo-app
kubectl -n demo rollout undo deploy/demo-app

Terraform

terraform init
terraform fmt -recursive
terraform validate
terraform plan -out tfplan
terraform apply tfplan

Als je wilt, kan ik deze tutorial uitbreiden met een volledig uitgewerkt voorbeeld (repo-structuur, concrete GitHub Actions workflow, Terraform modules, en een lokale observability stack met Prometheus + Grafana + Loki + Jaeger/Tempo), inclusief alle configuratiebestanden en commando’s om het stap voor stap te draaien.