Uitwerking
CI/CD - Docker Hub Tags
De GitHub Actions workflow bouwt twee images en pusht ze naar stensel8/public-cloud-concepts:
| Image | Tag | Pull commando |
|---|---|---|
| Bison app | bison | docker pull stensel8/public-cloud-concepts:bison |
| Brightspace app | brightspace | docker pull stensel8/public-cloud-concepts:brightspace |

2.2 Kubernetes
Opdracht 2.2a - Deployment draait
De Week 1 deployment (first-deployment) draait op het kubeadm-cluster met beide pods actief in twee regio’s:

NAME STATUS ROLES AGE VERSION
master-amsterdam Ready control-plane 9d v1.35.1
worker-brussels Ready <none> 9d v1.35.1
worker-london Ready <none> 9d v1.35.1
NAME READY STATUS IP NODE
first-deployment-5ffbd9444c-5hkzs 1/1 Running 10.244.2.3 worker-london
first-deployment-5ffbd9444c-s4xdb 1/1 Running 10.244.1.3 worker-brusselsOpdracht 2.2b - Pod verwijderen en opnieuw aanmaken
Een pod werd verwijderd terwijl de Deployment actief bleef. Kubernetes maakte automatisch een vervangende pod aan met een ander IP-adres, wat aantoont dat pod-IPs tijdelijk zijn.

# Voor verwijdering:
first-deployment-5ffbd9444c-5hkzs IP: 10.244.2.3 worker-london
# Na verwijdering - nieuwe pod:
first-deployment-5ffbd9444c-pdrw0 IP: 10.244.2.4 worker-londonDit is precies waarom een Service nodig is: pods zijn wegwerpbaar en hun IPs veranderen.
Opdracht 2.2c - ClusterIP Service
service.yml - Een Service koppelt via selector aan pods met het label app: my-container. De eerste versie was ClusterIP: alleen bereikbaar binnen het cluster, geen extern IP:
+apiVersion: v1
+kind: Service
+metadata:
+ name: first-service
+spec:
+ type: ClusterIP # stabiel virtueel IP, alleen intern
+ selector:
+ app: my-container # koppelt aan pods met dit label
+ ports:
+ - port: 80
+ targetPort: 80

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
first-service ClusterIP 10.110.23.98 <none> 80/TCP 0sHet ClusterIP is alleen bereikbaar vanuit het cluster. Verkeer wordt load-balanced over alle pods met selector app: my-container.
Opdracht 2.2d - ClusterIP bereikbaar vanaf elke node
Alle drie nodes gaven de HTML-respons terug via curl 10.110.23.98.



Opdracht 2.2e - NodePort Service
Voor externe toegang is het type gewijzigd naar NodePort en een vaste poort toegevoegd:
spec:
- type: ClusterIP
+ type: NodePort
ports:
- port: 80
targetPort: 80
+ nodePort: 32490 # vaste poort op alle nodes (bereik: 30000–32767)

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
first-service NodePort 10.110.23.98 <none> 80:32490/TCP 8m39sInterne nodes opzoeken:

Testen via intern node-IP:

Externe toegang via NodePort + firewallregel:
GCP blokkeert inkomend verkeer standaard. Een firewallregel is aangemaakt voor TCP poort 32490:

Eerst getest zonder firewallregel - geblokkeerd:

Na het aanmaken van de firewallregel werkt de site:

kubectl port-forward is een developer-tool voor lokaal testen, geen externe toegangsoplossing. De tunnel is alleen bereikbaar op de machine waar het commando draait en stopt bij Ctrl+C.


Opdracht 2.2f - LoadBalancer op het kubeadm-cluster


NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
first-service LoadBalancer 10.110.23.98 <pending> 80:32490/TCP 26mWaarom blijft het pending?
Een LoadBalancer service vraagt de cloud controller manager om een externe load balancer te provisionen. Op een zelfbeheerd kubeadm-cluster is er geen cloud controller manager aanwezig - er is geen component dat namens Kubernetes een GCP load balancer kan aanvragen. Het externe IP wordt nooit toegewezen.
| Aanpak | Hoe | Wanneer |
|---|---|---|
| Juiste manier | GKE: cloud controller manager regelt automatisch een Load Balancer | Productie |
| NodePort + firewallregel | Open handmatig een GCP-firewallregel voor de NodePort-poort | Workaround op kubeadm |
| Ingress controller | nginx Ingress Controller routeert meerdere services via één extern IP | Meerdere apps (opdracht 2.2h) |
Opdracht 2.2g - LoadBalancer op GKE
Een GKE-cluster week2-cluster aangemaakt: e2-medium, 2 nodes, europe-west4-a, Regular release channel.




gcloud container clusters get-credentials week2-cluster --zone europe-west4-a
Na deployen van de deployment en service:

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
first-service LoadBalancer 34.118.232.196 34.12.127.52 80:31275/TCP 44sNa ~44 seconden had GKE een Google Cloud Load Balancer geprovisioneert en het externe IP 34.12.127.52 toegewezen. Dit is het kernverschil met het kubeadm-cluster.

Opdracht 2.2h - Ingress: meerdere services via één load balancer
Twee apps beschikbaar via één Ingress, elk op een eigen hostnaam:
| Hostnaam | Backend service |
|---|---|
bison.mysaxion.nl | bison-service (poort 80) |
brightspace.mysaxion.nl | brightspace-service (poort 80) |
nginx Ingress Controller installeren:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.0/deploy/static/provider/cloud/deploy.yaml
Manifesten deployen:
Toegepaste bestanden (zie GitHub):
| Bestand | Beschrijving |
|---|---|
| bison/deployment.yml | 2 replicas, image-tag bison |
| bison/service.yml | ClusterIP op poort 80 |
| brightspace/deployment.yml | 2 replicas, image-tag brightspace |
| brightspace/service.yml | ClusterIP op poort 80 |
| ingress.yml | Ingress op basis van Host HTTP-header |


NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-saxion nginx bison.mysaxion.nl,brightspace.mysaxion.nl 34.91.190.135 80 25s
Hosts-bestand bijgewerkt:



Waarom Ingress?
Zonder Ingress heeft elke applicatie een aparte LoadBalancer service nodig (eigen IP, eigen kosten). Met Ingress stuurt één load balancer op basis van de Host HTTP-header verkeer naar de juiste service.
2.3 DORA
DORA staat voor DevOps Research and Assessment. Het is een meerjarig onderzoeksproject dat kijkt naar wat succesvolle softwareteams anders doen dan teams die moeite hebben om software goed te leveren. Daar zijn vier meetbare metrics uit gekomen.
Wat zijn de DORA-metrics?
| Metric | Wat het meet |
|---|---|
| Deployment Frequency | Hoe vaak deploy je succesvol naar productie? |
| Lead Time for Changes | Hoe lang duurt het van een commit totdat het live staat? |
| Change Failure Rate | Hoeveel procent van je deploys gaat mis? |
| Time to Restore Service | Hoe snel ben je weer up na een incident? |
Waarom zijn ze belangrijk?
Elke metric zegt iets over hoe je werkt. Als je zelden deployt, betekent dat vaak dat je releases groot en riskant zijn. Dat maakt elke deploy spannend, want er gaat veel tegelijk live.
Een hoge Change Failure Rate wijst op iets dat niet klopt in hoe je test of hoe je pipeline is ingericht. Teams die dit goed op orde hebben, deployen klein en vaak. Als er dan iets misgaat, is het snel gevonden en opgelost.
DORA heeft ook aangetoond dat dit doorwerkt op het team zelf. Minder grote deploys betekent minder brandjes, minder crisismode, en minder stress. Dat is niet soft: het is gewoon het gevolg van goed ingericht werk.
Hoe pas ik dit toe?
| Techniek | Hoe ik dit toepas |
|---|---|
| Continuous Integration | Elke push triggert automatisch Dockerfile-lint, image build en een Trivy-scan. Fouten zijn direct zichtbaar, niet pas bij een grote release. |
| Trunk-Based Development | main is altijd deployable. development gebruik ik voor nieuw werk dat ik via de green-slot test voordat ik het live zet. |
| Deployment Automation | Bij elke push naar main deployt de pipeline automatisch naar zowel Docker Hub als Google Artifact Registry, en vanaf daar naar GKE. Geen handmatige stappen. |
| Monitoring en Observability | De monitoring stack uit Week 5 maakt Time to Restore kort: als er iets misgaat zie ik dat direct in Grafana. |
De blue-green strategie sluit hier goed op aan. Terugschakelen is makkelijk, dus de drempel om iets live te zetten is laag. Je weet dat je altijd snel terug kunt als het toch niet goed is.