# Kubernetes: De eerste applicatie deployen > Praktische gids om een applicatie te deployen op Kubernetes. Van minikube-installatie tot Deployments, Services en ConfigMaps met concrete voorbeelden. - Published: 2026-02-12 - Updated: 2026-04-28 - Author: SharpSkill - Tags: kubernetes, k8s, container orchestration, devops, deployment - Reading time: 16 min --- Kubernetes (K8s) is uitgegroeid tot de de facto standaard voor container-orchestratie. Ontworpen door Google en momenteel onderhouden door de CNCF, automatiseert Kubernetes het deployen, schalen en beheren van gecontaineriseerde applicaties. Deze gids loopt door het opzetten van een lokaal cluster en het deployen van een eerste applicatie. > **Vereisten** > > Basiskennis van Docker wordt aanbevolen voordat dieper in Kubernetes wordt gedoken. Containers zijn de fundamentele bouwstenen die Kubernetes orkestreert. De Docker-gids vooraf lezen maakt de begrippen die hier worden gepresenteerd veel eenvoudiger te begrijpen. ## De Kubernetes-architectuur begrijpen Kubernetes berust op een master-worker-architectuur. De Control Plane neemt de globale beslissingen over het cluster, terwijl de Nodes de workloads uitvoeren. ``` # Simplified Kubernetes Architecture ┌─────────────────────────────────────────────────────────────┐ │ CONTROL PLANE │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ API Server │ │ Scheduler │ │ Controller Manager │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────┐│ │ │ etcd ││ │ └─────────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────────┘ │ ┌────────────────────┼────────────────────┐ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ NODE 1 │ │ NODE 2 │ │ NODE 3 │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ kubelet │ │ │ │ kubelet │ │ │ │ kubelet │ │ │ ├───────────┤ │ │ ├───────────┤ │ │ ├───────────┤ │ │ │ kube-proxy│ │ │ │ kube-proxy│ │ │ │ kube-proxy│ │ │ ├───────────┤ │ │ ├───────────┤ │ │ ├───────────┤ │ │ │ Pods │ │ │ │ Pods │ │ │ │ Pods │ │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ ``` De API Server is het toegangspunt voor alle commando's. etcd slaat de clusterstatus op. De Scheduler wijst Pods toe aan Nodes. De Controllers houden de gewenste systeemstatus in stand. ## De lokale omgeving opzetten Voor het experimenteren met Kubernetes op lokale machines bestaan verschillende opties: minikube, kind, k3d of Docker Desktop. Minikube blijft de populairste oplossing om mee te leren. ```bash # terminal # Install kubectl (Kubernetes client) curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" chmod +x kubectl sudo mv kubectl /usr/local/bin/ # Verify installation kubectl version --client # Client Version: v1.31.0 # Install minikube curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube # Start the local cluster minikube start --driver=docker --cpus=2 --memory=4096 # Check status minikube status # minikube: Running # cluster: Running # kubectl: Configured ``` Minikube creëert een Kubernetes-cluster met één node binnen een virtuele machine of een Docker-container. De toegewezen resources (CPU, geheugen) kunnen worden aangepast aan de behoefte. ```bash # terminal # Access the Kubernetes dashboard (web interface) minikube dashboard # Check cluster nodes kubectl get nodes # NAME STATUS ROLES AGE VERSION # minikube Ready control-plane 5m v1.31.0 # Detailed cluster information kubectl cluster-info ``` > **Alternatieven voor Minikube** > > Kind (Kubernetes in Docker) start sneller en past beter bij CI/CD-tests. K3d gebruikt k3s, een lichte Kubernetes-distributie. Docker Desktop integreert Kubernetes direct, maar verbruikt meer resources. ## Pods: de basiseenheid Een Pod is de kleinste deploybare eenheid in Kubernetes. Een Pod omvat een of meer containers die hetzelfde netwerk en dezelfde opslag delen. ```yaml # pod-simple.yaml apiVersion: v1 kind: Pod metadata: # Unique Pod name within the namespace name: nginx-pod # Labels for organization and selection labels: app: nginx environment: development spec: containers: # Main container definition - name: nginx # Docker image to use image: nginx:1.25-alpine # Ports exposed by the container ports: - containerPort: 80 # Container resource allocation resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" ``` Dit YAML-manifest declareert een Pod die één enkele nginx-container bevat. Labels maken het mogelijk om Pods te identificeren en te selecteren. Resources definiëren de minimumgaranties (requests) en maximale limieten (limits). ```bash # terminal # Create the Pod kubectl apply -f pod-simple.yaml # pod/nginx-pod created # List Pods kubectl get pods # NAME READY STATUS RESTARTS AGE # nginx-pod 1/1 Running 0 30s # Full Pod details kubectl describe pod nginx-pod # Container logs kubectl logs nginx-pod # Execute a command inside the Pod kubectl exec -it nginx-pod -- /bin/sh # Delete the Pod kubectl delete pod nginx-pod ``` Pods zijn van nature kortstondig. Bij een crash of verwijdering worden ze niet automatisch opnieuw aangemaakt door Kubernetes. Deployments lossen deze beperking op. ## Deployments: declaratief beheer Een Deployment definieert de gewenste status voor een set identieke Pods. Kubernetes onderhoudt deze status automatisch door Pods aan te maken, bij te werken of te verwijderen wanneer dat nodig is. ```yaml # deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: # Deployment name name: webapp-deployment labels: app: webapp spec: # Desired number of replicas replicas: 3 # Selector to identify managed Pods selector: matchLabels: app: webapp # Template for Pod creation template: metadata: labels: app: webapp spec: containers: - name: webapp image: nginx:1.25-alpine ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" # Liveness probe: restarts container on failure livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 10 periodSeconds: 10 # Readiness probe: removes Pod from Service on failure readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 5 ``` Het Deployment maakt een ReplicaSet aan dat 3 identieke Pods in stand houdt. Probes controleren de status van de containers en stellen Kubernetes in staat om automatisch te reageren op problemen. ```bash # terminal # Create the Deployment kubectl apply -f deployment.yaml # deployment.apps/webapp-deployment created # Verify the Deployment kubectl get deployments # NAME READY UP-TO-DATE AVAILABLE AGE # webapp-deployment 3/3 3 3 1m # List Pods created by the Deployment kubectl get pods -l app=webapp # NAME READY STATUS RESTARTS AGE # webapp-deployment-7d9f8b6c4-abc12 1/1 Running 0 1m # webapp-deployment-7d9f8b6c4-def34 1/1 Running 0 1m # webapp-deployment-7d9f8b6c4-ghi56 1/1 Running 0 1m # Manual scaling kubectl scale deployment webapp-deployment --replicas=5 # Deployment history kubectl rollout history deployment webapp-deployment ``` Het verwijderen van een Pod activeert automatisch het aanmaken van een nieuwe Pod om het gewenste aantal replicas te behouden. ## Services: netwerkblootstelling Pods hebben kortstondige IP-adressen. Services bieden een stabiel adres om toegang te krijgen tot een set Pods, met ingebouwde load balancing. ```yaml # service.yaml apiVersion: v1 kind: Service metadata: name: webapp-service spec: # Service type: ClusterIP (internal), NodePort, LoadBalancer type: ClusterIP # Selector to identify target Pods selector: app: webapp ports: # Port exposed by the Service - port: 80 # Target container port targetPort: 80 # Protocol (TCP by default) protocol: TCP ``` Deze ClusterIP-Service is alleen toegankelijk vanuit het cluster. Verzoeken naar `webapp-service:80` worden verdeeld over de Pods met het label `app: webapp`. ```bash # terminal # Create the Service kubectl apply -f service.yaml # service/webapp-service created # List Services kubectl get services # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE # webapp-service ClusterIP 10.96.123.456 80/TCP 30s # Test from a temporary Pod kubectl run curl-test --rm -it --image=curlimages/curl -- curl webapp-service # Detailed Service description kubectl describe service webapp-service ``` Om de applicatie buiten het cluster bloot te stellen, is een NodePort- of LoadBalancer-type vereist. ```yaml # service-nodeport.yaml apiVersion: v1 kind: Service metadata: name: webapp-nodeport spec: type: NodePort selector: app: webapp ports: - port: 80 targetPort: 80 # Port on each Node (30000-32767) nodePort: 30080 ``` Met minikube opent het commando `minikube service webapp-nodeport` automatisch de browser op de juiste URL. ## ConfigMaps: geëxternaliseerde configuratie ConfigMaps scheiden configuratie van code. Waarden worden geïnjecteerd als omgevingsvariabelen of gemount als bestanden. ```yaml # configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: webapp-config data: # Simple key-value pairs APP_ENV: "production" LOG_LEVEL: "info" MAX_CONNECTIONS: "100" # Multiline configuration (complete file) nginx.conf: | server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; } location /health { return 200 'OK'; add_header Content-Type text/plain; } } ``` ConfigMaps slaan niet-gevoelige gegevens op. Voor geheimen (wachtwoorden, tokens) zijn Kubernetes Secrets geschikter. ```yaml # deployment-with-config.yaml apiVersion: apps/v1 kind: Deployment metadata: name: webapp-configured spec: replicas: 2 selector: matchLabels: app: webapp-configured template: metadata: labels: app: webapp-configured spec: containers: - name: webapp image: nginx:1.25-alpine ports: - containerPort: 80 # Inject environment variables envFrom: - configMapRef: name: webapp-config # Or individual variables env: - name: SPECIFIC_VAR valueFrom: configMapKeyRef: name: webapp-config key: LOG_LEVEL # Mount configuration file volumeMounts: - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf volumes: - name: nginx-config configMap: name: webapp-config ``` Deze configuratie injecteert omgevingsvariabelen en mount het bestand nginx.conf in de container. ```bash # terminal # Apply resources kubectl apply -f configmap.yaml kubectl apply -f deployment-with-config.yaml # Verify environment variables kubectl exec deployment/webapp-configured -- printenv | grep APP_ENV # APP_ENV=production # Verify mounted file kubectl exec deployment/webapp-configured -- cat /etc/nginx/conf.d/default.conf ``` > **ConfigMap-updates** > > Het wijzigen van een ConfigMap herstart de Pods niet automatisch. Om de wijzigingen toe te passen is een handmatige herstart nodig: `kubectl rollout restart deployment webapp-configured`. Tools zoals Reloader automatiseren dit proces. ## Secrets: gevoelige gegevens Secrets slaan gevoelige informatie op zoals wachtwoorden, tokens of SSH-sleutels. Hoewel ze base64-gecodeerd zijn, worden ze standaard niet versleuteld in rust. ```yaml # secret.yaml apiVersion: v1 kind: Secret metadata: name: webapp-secrets type: Opaque # Values must be base64-encoded data: # echo -n 'admin' | base64 username: YWRtaW4= # echo -n 'supersecretpassword' | base64 password: c3VwZXJzZWNyZXRwYXNzd29yZA== --- # Alternative: stringData accepts plain text values apiVersion: v1 kind: Secret metadata: name: webapp-secrets-plain type: Opaque stringData: username: admin password: supersecretpassword ``` Secrets worden op dezelfde manier geïnjecteerd als ConfigMaps. ```yaml # deployment-with-secrets.yaml apiVersion: apps/v1 kind: Deployment metadata: name: webapp-secure spec: replicas: 1 selector: matchLabels: app: webapp-secure template: metadata: labels: app: webapp-secure spec: containers: - name: webapp image: nginx:1.25-alpine env: - name: DB_USERNAME valueFrom: secretKeyRef: name: webapp-secrets key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: webapp-secrets key: password ``` ```bash # terminal # Create the Secret kubectl apply -f secret.yaml # List Secrets (values are not displayed) kubectl get secrets # NAME TYPE DATA AGE # webapp-secrets Opaque 2 10s # Decode a value kubectl get secret webapp-secrets -o jsonpath='{.data.password}' | base64 -d # supersecretpassword ``` ## Namespaces: logische isolatie Namespaces partitioneren een cluster in geïsoleerde virtuele omgevingen. Deze scheiding maakt het mogelijk om meerdere teams of omgevingen op hetzelfde cluster te beheren. ```yaml # namespace.yaml apiVersion: v1 kind: Namespace metadata: name: development labels: environment: development --- apiVersion: v1 kind: Namespace metadata: name: staging labels: environment: staging ``` Elke resource kan worden aangemaakt in een specifieke namespace. ```bash # terminal # Create namespaces kubectl apply -f namespace.yaml # List namespaces kubectl get namespaces # NAME STATUS AGE # default Active 1d # development Active 10s # staging Active 10s # Create a resource in a specific namespace kubectl apply -f deployment.yaml -n development # List Pods in a namespace kubectl get pods -n development # Change default namespace kubectl config set-context --current --namespace=development ``` Resources uit verschillende namespaces zijn standaard van elkaar geïsoleerd. Communicatie tussen namespaces verloopt via de interne DNS: `service-name.namespace.svc.cluster.local`. ## Volledige applicatie: de resources samenstellen Hieronder een volledige applicatie die alle gepresenteerde concepten combineert. ```yaml # complete-app.yaml --- # Dedicated Namespace apiVersion: v1 kind: Namespace metadata: name: myapp --- # ConfigMap for configuration apiVersion: v1 kind: ConfigMap metadata: name: myapp-config namespace: myapp data: APP_NAME: "MyApp" LOG_LEVEL: "info" --- # Secret for sensitive data apiVersion: v1 kind: Secret metadata: name: myapp-secrets namespace: myapp type: Opaque stringData: api-key: "sk-1234567890abcdef" --- # Deployment with 3 replicas apiVersion: apps/v1 kind: Deployment metadata: name: myapp namespace: myapp spec: replicas: 3 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: myapp image: nginx:1.25-alpine ports: - containerPort: 80 envFrom: - configMapRef: name: myapp-config env: - name: API_KEY valueFrom: secretKeyRef: name: myapp-secrets key: api-key resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 5 --- # Service for internal exposure apiVersion: v1 kind: Service metadata: name: myapp-service namespace: myapp spec: type: ClusterIP selector: app: myapp ports: - port: 80 targetPort: 80 --- # NodePort Service for external access (development) apiVersion: v1 kind: Service metadata: name: myapp-nodeport namespace: myapp spec: type: NodePort selector: app: myapp ports: - port: 80 targetPort: 80 nodePort: 30100 ``` Dit ene bestand deployt een volledige applicatie met geëxternaliseerde configuratie, secrets, hoge beschikbaarheid en netwerkblootstelling. ```bash # terminal # Deploy the complete application kubectl apply -f complete-app.yaml # Verify all resources kubectl get all -n myapp # NAME READY STATUS RESTARTS AGE # pod/myapp-7d9f8b6c4-abc12 1/1 Running 0 30s # pod/myapp-7d9f8b6c4-def34 1/1 Running 0 30s # pod/myapp-7d9f8b6c4-ghi56 1/1 Running 0 30s # # NAME TYPE CLUSTER-IP PORT(S) AGE # service/myapp-service ClusterIP 10.96.123.456 80/TCP 30s # service/myapp-nodeport NodePort 10.96.123.789 80:30100/TCP 30s # # NAME READY UP-TO-DATE AVAILABLE AGE # deployment.apps/myapp 3/3 3 3 30s # Access the application with minikube minikube service myapp-nodeport -n myapp ``` ## Updates en rollbacks Kubernetes vergemakkelijkt geleidelijke updates en rollbacks. ```bash # terminal # Update the Deployment image kubectl set image deployment/myapp myapp=nginx:1.26-alpine -n myapp # Track deployment in real-time kubectl rollout status deployment/myapp -n myapp # Waiting for deployment "myapp" rollout to finish: 1 out of 3 new replicas updated # Waiting for deployment "myapp" rollout to finish: 2 out of 3 new replicas updated # deployment "myapp" successfully rolled out # Revision history kubectl rollout history deployment/myapp -n myapp # REVISION CHANGE-CAUSE # 1 # 2 # Rollback to previous revision kubectl rollout undo deployment/myapp -n myapp # Rollback to a specific revision kubectl rollout undo deployment/myapp --to-revision=1 -n myapp ``` De standaard updatestrategie (RollingUpdate) vervangt de oude Pods geleidelijk door nieuwe en garandeert zo een continue beschikbaarheid. ## Essentiële kubectl-commando's ```bash # terminal # ======================================== # General Information # ======================================== kubectl cluster-info # Cluster info kubectl get nodes -o wide # Nodes with details kubectl api-resources # List resource types # ======================================== # Resource Management # ======================================== kubectl get all # All namespace resources kubectl get pods -A # Pods from all namespaces kubectl get pods -o wide # Pods with IP and Node kubectl get pods -w # Watch mode (real-time) # ======================================== # Inspection and Debugging # ======================================== kubectl describe pod # Full details kubectl logs -f # Streaming logs kubectl logs -c # Specific container logs kubectl exec -it -- /bin/sh # Interactive shell kubectl port-forward 8080:80 # Local tunnel to Pod # ======================================== # Editing and Deletion # ======================================== kubectl edit deployment # Live editing (vi) kubectl delete -f manifest.yaml # Delete via file kubectl delete pod --force # Force deletion ``` ## Conclusie Kubernetes transformeert het beheer van gecontaineriseerde applicaties door een declaratief, veerkrachtig en uitbreidbaar framework te bieden. De fundamentele concepten die hier worden gepresenteerd vormen de basis voor productieklaar deployments. ### Checklist voor een eerste Kubernetes-deployment - ✅ Werkend lokaal cluster (minikube, kind of k3d) - ✅ kubectl geïnstalleerd en geconfigureerd - ✅ Deployment met replicas en health probes - ✅ Service voor netwerkblootstelling - ✅ ConfigMap voor geëxternaliseerde configuratie - ✅ Secret voor gevoelige gegevens - ✅ Namespace voor isolatie - ✅ Resourcelimieten gedefinieerd (requests/limits) - ✅ Updatestrategie en rollback beheerst Kubernetes beheersen opent de deur naar schaalbare cloud-native architecturen. Volgende stappen omvatten het verkennen van Ingress Controllers voor HTTP-routing, PersistentVolumes voor opslag en Helm voor pakketbeheer. Kubernetes wordt een belangrijke troef tijdens DevOps- en SRE-sollicitatiegesprekken. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/devops/kubernetes-deploying-first-application