Ga naar inhoud

Uitwerking

CI Week 2

CI/CD - Docker Hub Tags

De GitHub Actions workflow bouwt twee images en pusht ze naar stensel8/public-cloud-concepts:

ImageTagPull commando
Bison appbisondocker pull stensel8/public-cloud-concepts:bison
Brightspace appbrightspacedocker pull stensel8/public-cloud-concepts:brightspace

DockerHub tags: latest, brightspace, bison


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:

Deployment running - alle nodes Ready, pods Running met IPs

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-brussels

Opdracht 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.

Pod verwijderd - nieuwe pod aangemaakt met ander IP

# 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-london

Dit 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

ClusterIP service aangemaakt met stabiel virtueel IP

NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
first-service   ClusterIP   10.110.23.98    <none>        80/TCP    0s

Het 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.

curl via ClusterIP vanaf master

curl via ClusterIP vanaf worker-brussels

curl via ClusterIP vanaf worker-london


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)

NodePort service - poort 80:32490/TCP

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

Interne nodes opzoeken:

Interne IP-adressen van de nodes

Testen via intern node-IP:

curl via intern node-IP en NodePort werkt

Externe toegang via NodePort + firewallregel:

GCP blokkeert inkomend verkeer standaard. Een firewallregel is aangemaakt voor TCP poort 32490:

Firewallregel aanmaken in GCP console

Eerst getest zonder firewallregel - geblokkeerd:

Browser geblokkeerd zonder firewallregel

Na het aanmaken van de firewallregel werkt de site:

Website bereikbaar via extern IP en NodePort

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.

kubectl port-forward actief

curl via localhost:8080 werkt via port-forward


Opdracht 2.2f - LoadBalancer op het kubeadm-cluster

LoadBalancer service aangemaakt - EXTERNAL-IP pending

LoadBalancer blijft in pending-status zonder cloud controller

NAME            TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
first-service   LoadBalancer   10.110.23.98   <pending>     80:32490/TCP   26m

Waarom 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.

AanpakHoeWanneer
Juiste manierGKE: cloud controller manager regelt automatisch een Load BalancerProductie
NodePort + firewallregelOpen handmatig een GCP-firewallregel voor de NodePort-poortWorkaround op kubeadm
Ingress controllernginx Ingress Controller routeert meerdere services via één extern IPMeerdere 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.

GKE cluster basis instellingen

GKE node pool configuratie

GKE node machine type e2-medium

GKE week2-cluster provisioning op 33%

gcloud container clusters get-credentials week2-cluster --zone europe-west4-a

GKE cluster verbonden - twee nodes Ready

Na deployen van de deployment en service:

LoadBalancer op GKE - extern IP toegewezen na ~44 seconden

NAME            TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
first-service   LoadBalancer   34.118.232.196   34.12.127.52    80:31275/TCP   44s

Na ~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.

Website bereikbaar via GKE LoadBalancer extern IP


Opdracht 2.2h - Ingress: meerdere services via één load balancer

Twee apps beschikbaar via één Ingress, elk op een eigen hostnaam:

HostnaamBackend service
bison.mysaxion.nlbison-service (poort 80)
brightspace.mysaxion.nlbrightspace-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

nginx Ingress Controller - extern IP 34.91.190.135 toegewezen

Manifesten deployen:

Toegepaste bestanden (zie GitHub):

BestandBeschrijving
bison/deployment.yml2 replicas, image-tag bison
bison/service.ymlClusterIP op poort 80
brightspace/deployment.yml2 replicas, image-tag brightspace
brightspace/service.ymlClusterIP op poort 80
ingress.ymlIngress op basis van Host HTTP-header

nginx Ingress Controller geïnstalleerd - pods Running

Deployments, services en Ingress toegepast

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

Ingress met saxion adres en hostnamen

Hosts-bestand bijgewerkt:

Hosts-bestand met bison.mysaxion.nl en brightspace.mysaxion.nl

bison.mysaxion.nl - Bison-applicatie bereikbaar via Ingress

brightspace.mysaxion.nl - Brightspace-applicatie bereikbaar via Ingress

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?

MetricWat het meet
Deployment FrequencyHoe vaak deploy je succesvol naar productie?
Lead Time for ChangesHoe lang duurt het van een commit totdat het live staat?
Change Failure RateHoeveel procent van je deploys gaat mis?
Time to Restore ServiceHoe 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?

TechniekHoe ik dit toepas
Continuous IntegrationElke push triggert automatisch Dockerfile-lint, image build en een Trivy-scan. Fouten zijn direct zichtbaar, niet pas bij een grote release.
Trunk-Based Developmentmain is altijd deployable. development gebruik ik voor nieuw werk dat ik via de green-slot test voordat ik het live zet.
Deployment AutomationBij 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 ObservabilityDe 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.

Bronnen