Primeres passes amb Kubernetes
Introducció
En l’era del desplegament continu i de la virtualització, els contenidors s’han convertit en una eina essencial per empaquetar i executar aplicacions. Tanmateix, quan gestionem dotzenes o centenars de contenidors distribuïts entre diferents màquines, cal una eina que ens ajudi a orquestrar-los: aquí és on entra Kubernetes.
Aquest article és una introducció pràctica i clara als conceptes fonamentals de Kubernetes, pensada per a qui comença. Aprendrem què és, com s’estructura, i farem una primera presa de contacte amb minikube per practicar localment.
Què és Kubernetes?
Kubernetes ens permet controlar i administrar contenidors com si fossin un únic sistema distribuït, amb eines per programar la seva execució, vigilar-ne l’estat, escalar-los segons la càrrega o substituir-los automàticament si fallen.
Va ser creat inicialment per Google basant-se en la seva experiència amb sistemes com Borg, i ara és un projecte de codi obert mantingut per la Cloud Native Computing Foundation.
Amb Kubernetes podem:
-
Desplegar aplicacions de forma repetible i fiable.
-
Gestionar rèpliques i escalat automàtic.
-
Organitzar serveis i xarxes dins un clúster.
-
Detectar i recuperar errors automàticament.
Kubernetes no és un sistema senzill, però permet estructurar aplicacions complexes amb flexibilitat i robustesa.
Conceptes i arquitectura
Pod
És la unitat mínima a Kubernetes, on corren els contenidors.
Dins un pod podem tenir volums, namespaces i IP compartits entre els diferents recursos dins seu.
Normalment un pod conté un contenidor.
Si tenim diferents contenidors dins un pod, aquests hauran d’estar al mateix node.
Node
Servidors (físics o virtuals) que contenen una instància de Kubernetes.
Hi ha dos tipus principals:
-
Node master
És el node que administra els nodes worker. S’encarrega de tasques com load-balancing, monitoritzar l’estat del cluster,…
Només corre pods del sistema, no corre l’aplicació client.
Alguns dels serveis que conté (a més dels del worker node) són:
-
API server
-
Scheduler
-
Kube Controller Manager
-
Cloud Controller Manager
-
etcd
-
DNS
-
-
Node worker
Aquests nodes són on es despleguen els pods i corren els serveis.
Serveis dins els nodes
Cada node té sempre una sèrie de serveis, que són:
-
kubelet
S’encarrega de comunicar-se amb el node master via API.
-
kube-proxy
Responsable de la comunicació de xarxa entre el propi node i entre altres nodes.
-
Container runtime
Motor que corre el contenidor.
Cluster
És un conjunt de nodes.
kubectl
Eina CLI que permet connectar-se a un cluster i administrar-lo. Es conecta al cluster via Rest API per HTTPS.
Practicant amb minikube
Per practicar amb funcionalitats bàsiques de Kubernetes utilitzarem minikube, un programa que crea un cluster amb un sol node al nostre equip.
Primer l’instal·larem, amb ArchLinux farem:
$ sudo pacman -S minikube
I iniciarem minikube (jo ho faig amb el driver kvm), així que la comanda és:
$ minikube start --driver=kvm2
😄 minikube v1.36.0 on Cachyos
✨ Using the kvm2 driver based on user configuration
👍 Starting "minikube" primary control-plane node in "minikube" cluster
🔥 Creating kvm2 VM (CPUs=2, Memory=3900MB, Disk=20000MB) ...
❗ Image was not built for the current minikube version. To resolve this you can delete and recreate your minikube cluster using the latest images. Expected minikube version: v1.35.0 -> Actual minikube version: v1.36.0
🐳 Preparing Kubernetes v1.33.1 on Docker 28.0.4 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔗 Configuring bridge CNI (Container Networking Interface) ...
🔎 Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟 Enabled addons: default-storageclass, storage-provisioner
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
Si falla en iniciar minikube amb el driver kvm, pot ser que no tingueu el servei libvirtd iniciat.
Un cop iniciat minikube, podem veure l’estat amb minikube status.
A partir d’aqui, minikube ha configurat l’eina kubectl per operar amb el nou cluster. Per veure la IP del cluster amb minikube podem fer minikube ip.
Entrant al node
Si entrem al node per ssh (amb la comanda minikube ssh per exemple) podem inspeccionar quins contenidors corren.
Fent un docker ps, veiem que, encara que no hem desplegat res, ja té contenidors en execució. Aquests són els serveis que hem mencionat abans: kubeproxy, DNS, scheduler,…
kubectl
Per aconseguir informació sobre el que tenim actualment desplegat, utilitzarem kubectl. Per informació sobre el cluster, farem:
$ kubectl cluster-info
Kubernetes control plane is running at https://192.168.39.118:8443
CoreDNS is running at https://192.168.39.118:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
Per llistar recursos ho farem amb kubectl get recurs:
-
Per llistar els nodes:
$ kubectl get nodes NAME STATUS ROLES AGE VERSION minikube Ready control-plane 12m v1.33.1
-
Per llistar els pods:
$ kubectl get pods No resources found in default namespace.
-
Per llistar els namespaces:
$ kubectl get namespaces NAME STATUS AGE default Active 12m kube-node-lease Active 12m kube-public Active 12m kube-system Active 12m
Per defecte, tots els pods es crearan al namespace default. Si volem veure només els pods dins un namespace, podem especificar-ho amb:
$ kubectl get pods --namespace=kube-system
NAME READY STATUS RESTARTS AGE
coredns-674b8bbfcf-nqsn7 1/1 Running 0 13m
etcd-minikube 1/1 Running 0 14m
kube-apiserver-minikube 1/1 Running 0 14m
kube-controller-manager-minikube 1/1 Running 2 (14m ago) 14m
kube-proxy-shsmh 1/1 Running 0 13m
kube-scheduler-minikube 1/1 Running 0 14m
storage-provisioner 1/1 Running 1 (12m ago) 13m
Interactuant amb pods
Per crear un pod, utilitzarem la comanda kubectl run, passant com a argument el nom del pod i la imatge:
$ kubectl run pnginx --image=nginx
pod/pnginx created
I ara per veure l’estat farem:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
pnginx 0/1 ContainerCreating 0 25s
Com podem veure, encara està creant el pod.
Si volem veure informació més profunda sobre el pod, farem kubectl describe pod:
$ kubectl describe pod pnginx
Name: pnginx
Namespace: default
Priority: 0
Service Account: default
Node: minikube/192.168.39.118
Start Time: Sun, 29 Jun 2025 17:26:00 +0200
Labels: run=pnginx
Annotations: <none>
Status: Pending
IP:
IPs: <none>
Containers:
pnginx:
Container ID:
Image: nginx
Image ID:
Port: <none>
Host Port: <none>
State: Waiting
Reason: ContainerCreating
Ready: False
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-xddhn (ro)
Conditions:
Type Status
PodReadyToStartContainers False
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
kube-api-access-xddhn:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Optional: false
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 61s default-scheduler Successfully assigned default/pnginx to minikube
Normal Pulling 58s kubelet Pulling image "nginx"
On podem veure l’estat, el node on es troba desplegat, la imatge utilitzada, els volums i un registre d’events al final, entre altres.
Quan tinguem el pod desplegat, si ens connectem al node per ssh veurem que s’han creat dos contenidors amb nginx, la imatge que hem desplegat.
Sempre es creen dos contenidors per defecte: un conté l’aplicació (com Nginx) i l’altre és un contenidor auxiliar anomenat pause, que manté l’entorn del pod actiu.
Per veure més informació sobre els pods (com les seves IPs), podem fer:
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pnginx 1/1 Running 0 8m47s 10.244.0.3 minikube <none> <none>
I, si volem eliminar un pod, ho fem amb kubectl delete:
$ kubectl delete pod pnginx
pod "pnginx" deleted
Interactuant amb deployments
Un deployment és una manera de crear diferents pods que poden estar a diferents nodes dins un cluster. Es pot utilitzar per desplegar diferents rèpliques d’un contenidor desplegades en diferents nodes i tenir redundància o per desplegar diferents serveis interconnectats.
Per crear un deployment, ho farem com creant un pod, amb la comanda:
$ kubectl create deployment dnginx --image=nginx
deployment.apps/dnginx created
I ara si volem veure el desplegament fem:
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
dnginx 1/1 1 1 28s
I per inspeccionar-lo més a fons:
$ kubectl describe deployment dnginx
Name: dnginx
Namespace: default
CreationTimestamp: Sun, 29 Jun 2025 18:13:33 +0200
Labels: app=dnginx
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=dnginx
Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=dnginx
Containers:
nginx:
Image: nginx
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Node-Selectors: <none>
Tolerations: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: dnginx-6f4d745775 (1/1 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 65s deployment-controller Scaled up replica set dnginx-6f4d745775 from 0 to 1
Aquí podem veure el nom, namespace, eevnts, política d’actualització dels pods (StrategyType) i les rèpliques dels contenidors que volem.
Ara només tenim una replica desitjada (i disponible, ja que la acabem de desplegar). Si volem escalar i afegir rèpliques, farem:
$ kubectl scale deployment dnginx --replicas=5
deployment.apps/dnginx scaled
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
dnginx-6f4d745775-6l8kv 0/1 ContainerCreating 0 13s
dnginx-6f4d745775-6wqwk 1/1 Running 0 18m
dnginx-6f4d745775-fsbv7 0/1 ContainerCreating 0 13s
dnginx-6f4d745775-vhrg4 0/1 ContainerCreating 0 14s
dnginx-6f4d745775-zg278 0/1 ContainerCreating 0 13s
I veiem que s’estan creant 4 pods nous.
I si volem reduir el número de rèpliques, el procediment és el mateix:
$ kubectl scale deployment dnginx replicas=3
deployment.apps/dnginx scaled
$ k get pods
NAME READY STATUS RESTARTS AGE
dnginx-6f4d745775-6l8kv 1/1 Running 0 5m10s
dnginx-6f4d745775-6wqwk 1/1 Running 0 23m
dnginx-6f4d745775-vhrg4 1/1 Running 0 5m11s
Si volem eliminar un pod, automàticament es crearà un altre, ja que al deployment hem dit que voliem 3 rèpliques actives.
Fins ara hem desplegat contenidors amb un servidor web nginx, però encara no hi hem pogut accedir, ja que les xarxes dels contenidors estan aïllades del host.
Per poder accedir als pods des de l’exterior, necessitarem desplegar un servei, que assignarà una IP a un deployment. Hi ha diferents tipus de serveis, el que utilitzarem avui és ClusterIP (assigna una IP al deployment i kubernetes la distribueix pels pods, balancejant la càrrega).
Per això, utilitzarem el deployment ja creat i n’exposarem el port 80 dels serveis contra el 8080 de la IP del servei:
$ kubectl expose deployment dnginx --port=8080 --target-port=80
service/dnginx exposed
Ara, per veure el servei desplegat, fem:
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dnginx ClusterIP 10.103.106.204 <none> 8080/TCP 27s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 140m
Ara tenim el servei exposat NOMÉS dins el cluster. Si entrem al cluster amb minikube ssh i fem un curl de la IP pel port, ens retornarà l’html de nginx.
Exposant serveis
Fins ara tenim un cluster IP que ens permet accedir des de dins del cluster. Si volem accedir-hi des de fora, hem de convertir el deployment a NodePort o LoadBalancer.
Primer eliminarem el servei dnginx, mantenint el deployment:
$ kubectl delete service dnginx
service "dnginx" deleted
I ara exposarem el port del deployment, convertint-lo a un servei NodePort:
$ kubectl expose deployment dnginx --type=NodePort --port=80
service/dnginx exposed
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dnginx NodePort 10.101.109.45 <none> 80:30176/TCP 10m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 169m
I ja tenim el port exposat. Ara si fem un curl de la IP del cluster (no la del NodePort, la podem trobar fent minikube IP) pel port aleatori que ha assignat (30176), ens retornarà el contingut d’nginx.
$ curl 192.168.39.118:30176
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
I amb un LoadBalancer fariem el mateix: eliminarem el servei, el crearem igual que el NodePort canviant el type a LoadBalancer:
$ kubectl delete service dnginx
service "dnginx" deleted
$ kubectl expose deployment dnginx --type=LoadBalancer --port=80
service/dnginx exposed
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dnginx LoadBalancer 10.96.231.244 <pending> 80:30388/TCP 26s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 176m
$ curl 192.168.39.118:30388
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
I si tinguessim diferents pàgines als contenidors, veuriem que si fem diferents curls cada cop ens retornaria la pàgina d’un pod diferent.
Com que ha quedat un post bastant dens, per aquesta setmana ho deixarem aqui. Encara han quedat coses per veure, així que faré una segona part ensenyant com es desplega Kubernetes amb arxius de text i altres utilitats.
I fins aquí el post d’avui. Si t’ha semblat útil pots deixar un comentari i compartir-lo. Ens veiem al següent!