Primeres passes amb Kubernetes

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.

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.

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

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.

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.

És un conjunt de nodes.

Eina CLI que permet connectar-se a un cluster i administrar-lo. Es conecta al cluster via Rest API per HTTPS.

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:

bash

$ sudo pacman -S minikube

I iniciarem minikube (jo ho faig amb el driver kvm), així que la comanda és:

bash

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

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,…

Per aconseguir informació sobre el que tenim actualment desplegat, utilitzarem kubectl. Per informació sobre el cluster, farem:

bash

$ 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:

    bash

      $ kubectl get nodes
      NAME       STATUS   ROLES           AGE   VERSION
      minikube   Ready    control-plane   12m   v1.33.1
  • Per llistar els pods:

    bash

      $ kubectl get pods
      No resources found in default namespace.
  • Per llistar els namespaces:

    bash

      $ 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:

bash

$ 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

Per crear un pod, utilitzarem la comanda kubectl run, passant com a argument el nom del pod i la imatge:

bash

$ kubectl run pnginx --image=nginx
pod/pnginx created

I ara per veure l’estat farem:

bash

$ 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:

bash

$ 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:

bash

$ 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:

bash

$ kubectl delete pod pnginx
pod "pnginx" deleted

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:

bash

$ kubectl create deployment dnginx --image=nginx
deployment.apps/dnginx created

I ara si volem veure el desplegament fem:

bash

$ kubectl get deployments
NAME     READY   UP-TO-DATE   AVAILABLE   AGE
dnginx   1/1     1            1           28s

I per inspeccionar-lo més a fons:

bash

$ 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:

bash

$ 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:

bash

$ 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:

bash

$ kubectl expose deployment dnginx --port=8080 --target-port=80
service/dnginx exposed

Ara, per veure el servei desplegat, fem:

bash

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

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:

bash

$ kubectl delete service dnginx
service "dnginx" deleted

I ara exposarem el port del deployment, convertint-lo a un servei NodePort:

bash

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

bash

$ 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:

bash

$ 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!