A la lliçó anterior vam aixecar clústers llencables al portàtil i vam descobrir, gairebé de passada, que kind fa servir kubeadm per dins: aquell camp kubeadmConfigPatches del fitxer de configuració no era casualitat. Ara anem a l'eina directament.

kubeadm és l'eina oficial del projecte Kubernetes per arrencar un clúster conforme sobre màquines que tu controles. És la base sobre la qual estan construïdes gairebé totes les distribucions, i entendre-la et dona una cosa que cap altra via et dona: veure amb els teus propis ulls on són els certificats, on viuen els pods estàtics del pla de control i què passa exactament quan un node s'uneix al clúster. Encara que Rutas Norte S.L. acabi triant un clúster gestionat (10-06), aquest coneixement és el que et permetrà depurar un pla de control trencat i aprovar la certificació CKA (12-01).

Contingut

  1. Què és kubeadm i què no és
  2. Autogestionat davant de gestionat: quan té sentit
  3. Preparació de les màquines
  4. Instal·lació de containerd
  5. Instal·lació de kubeadm, kubelet i kubectl
  6. kubeadm init amb fitxer de configuració
  7. Què crea exactament kubeadm al node
  8. Instal·lar el CNI i per què els nodes estan NotReady
  9. Unir nodes de treball
  10. Pla de control en alta disponibilitat
  11. Gestió de certificats
  12. Actualització de versió
  13. Còpia de seguretat i restauració d'etcd
  14. kubeadm reset i neteja
  15. Errors comuns i consells
  16. Exercicis
  17. Conclusió

  1. Què és kubeadm i què no és

kubeadm fa una sola cosa bé: donades unes màquines ja preparades, hi arrenca un pla de control i uneix nodes de treball, produint un clúster que passa les proves de conformitat de Kubernetes.

kubeadm SÍ que fa kubeadm NO fa
Generar tota la jerarquia de certificats (CA, apiserver, etcd, kubelet) Aprovisionar màquines virtuals o físiques
Escriure els manifestos dels pods estàtics del pla de control Instal·lar el runtime de contenidors
Arrencar etcd (apilat) o connectar-se a un d'extern Instal·lar el complement de xarxa (CNI)
Configurar el kubelet i generar els seus kubeconfig Configurar el sistema operatiu (swap, sysctl, tallafocs)
Emetre tokens d'unió i unir nodes Instal·lar Ingress, monitoratge, emmagatzematge
Actualitzar el pla de control de versió Gestionar còpies de seguretat d'etcd
Renovar certificats Oferir alta disponibilitat del balancejador de l'API

Aquesta llista de la dreta és la clau. Molta gent executa kubeadm init, veu el missatge d'èxit, i després se sorprèn que kubectl get nodes digui NotReady. No hi ha cap error: falta el CNI, i aquesta feina no és de kubeadm.

flowchart TB
    subgraph teva["Responsabilitat teva"]
        A[Màquines i xarxa]
        B[Sistema operatiu:<br/>swap, sysctl, tallafocs]
        C[containerd]
        E[CNI: Calico, Cilium...]
        F[Complements: Ingress,<br/>emmagatzematge, monitoratge]
        G[Còpies d'etcd, pedaços,<br/>rotació de nodes]
    end
    subgraph kubeadm["Responsabilitat de kubeadm"]
        D[Certificats, pla de control,<br/>tokens, unió de nodes,<br/>actualitzacions]
    end
    A --> B --> C --> D --> E --> F --> G
    style kubeadm fill:#e8f4ff
    style teva fill:#fff4e8

  1. Autogestionat davant de gestionat: quan té sentit

Abans d'escriure una sola ordre, la pregunta honesta: hauria Rutas Norte S.L. d'operar el seu propi clúster?

Criteri kubeadm (autogestionat) Gestionat (10-06)
Cost del pla de control Les teves màquines (3 nodes de control mínim per a HA) 0-75 €/mes per clúster, segons proveïdor
Qui arregla un etcd corrupte a les 3 de la matinada El teu equip El proveïdor
Actualitzacions de versió Manuals, node a node, amb finestra de manteniment Un botó, tot i que continua exigint planificació
Control sobre banderes de l'apiserver Total Limitat
Executar en màquines pròpies o en un centre de dades privat No (llevat de variants híbrides)
Requisits normatius de sobirania de la dada Complibles Depèn del proveïdor i la regió
Personal necessari Almenys 2 persones amb coneixement profund i guàrdia Una persona a temps parcial
Temps fins al primer clúster productiu Setmanes Hores

Advertiment seriós: operar un clúster propi en producció exigeix un equip dedicat. No és una tasca que es pugui afegir a la llista d'un altre lloc de treball. Necessites cobertura per a incidents fora d'horari, un procediment provat de restauració d'etcd, una política de pedaços del sistema operatiu, i algú que entengui els certificats quan caduquin. Si la teva organització no pot sostenir això, un clúster gestionat no és una comoditat: és una decisió de gestió de risc.

Quan kubeadm sí que és la resposta correcta: centre de dades propi o maquinari específic (GPU, baixa latència, compliment normatiu); necessitat de configuracions del pla de control que cap gestionat permet; estalvi de cost amb molts nodes; i aprenentatge i certificació, perquè el CKA (12-01) avalua exactament això.

Per a Rutas Norte muntarem un clúster amb kubeadm en un laboratori per aprendre i practicar, amb la conclusió raonada de producció reservada per a 10-06.

  1. Preparació de les màquines

Partim de tres màquines Ubuntu 24.04 LTS al laboratori de Rutas Norte:

Nom Funció vCPU RAM IP
rn-control-1 Pla de control 2 4 GB 10.10.0.11
rn-worker-1 Node de treball 2 4 GB 10.10.0.21
rn-worker-2 Node de treball 2 4 GB 10.10.0.22

Requisits mínims oficials: 2 CPU i 2 GB de RAM per node de control, nom de host únic, adreça MAC única i product_uuid únic (sudo cat /sys/class/dmi/id/product_uuid). Si dues màquines clonades comparteixen product_uuid, el clúster pot confondre-les: regenera'l des de l'hipervisor.

3.1 Desactivar el swap

Tots els passos següents s'executen a les tres màquines.

sudo swapoff -a                                # ara
sudo sed -i '/ swap / s/^/#/' /etc/fstab       # i de forma permanent
free -h                                        # comprovar que Swap està a 0

Per què cal desactivar el swap? Perquè trenca el model de recursos que vam estudiar a 03-04 i 03-05. El planificador decideix on va un pod segons la memòria sol·licitada i disponible; el kubelet expulsa pods quan el node té pressió de memòria, seguint les classes de QoS. Si hi ha swap, un pod que supera el seu límit de memòria no mor: comença a paginar a disc, es torna mil vegades més lent, les seves sondes de liveness comencen a fallar de forma erràtica i el node sencer es degrada sense que cap mètrica ho expliqui. És una fallada molt pitjor que un OOMKilled net.

Kubernetes 1.30 té suport experimental de swap (NodeSwap), però està en beta amb molts advertiments. Per a un clúster de producció, swap desactivat.

3.2 Mòduls del kernel

# Declarar els mòduls que han de carregar-se a cada arrencada
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

# Carregar-los ara sense reiniciar
sudo modprobe overlay
sudo modprobe br_netfilter

# Verificar
lsmod | grep -E 'overlay|br_netfilter'

Què fa cadascun:

  • overlay: el sistema de fitxers per capes que fa servir containerd per muntar les imatges de contenidor. Sense ell, el runtime no pot crear contenidors.
  • br_netfilter: permet que el trànsit que travessa un pont de xarxa de Linux (el que connecta els contenidors del node) sigui visible per a iptables. Sense ell, kube-proxy escriu regles que mai s'apliquen al trànsit entre pods, i els Services simplement no funcionen.

3.3 Paràmetres sysctl

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
sudo sysctl --system                                          # aplicar
sysctl net.ipv4.ip_forward net.bridge.bridge-nf-call-iptables # verificar
  • ip_forward = 1: el node ha de reenviar paquets entre interfícies. Sense això, un pod de rn-worker-1 no pot parlar amb un de rn-worker-2.
  • bridge-nf-call-iptables = 1: activa el que br_netfilter fa possible.

Si sysctl et diu que el paràmetre no existeix, és que br_netfilter no està carregat. L'ordre importa.

3.4 Tallafocs i ports

Node Port/rang Protocol Component Qui hi accedeix
Control 6443 TCP kube-apiserver Tots els nodes, kubectl, balancejador
Control 2379-2380 TCP etcd (client i parell) Només nodes de control
Control 10250 TCP kubelet API Pla de control (logs, exec)
Control 10257 TCP kube-controller-manager Local
Control 10259 TCP kube-scheduler Local
Treball 10250 TCP kubelet API Pla de control
Treball 10256 TCP kube-proxy (health) Balancejadors
Tots dos 30000-32767 TCP Rang de NodePort Segons necessitat
Tots dos Segons CNI UDP/TCP Xarxa de pods Tots els nodes

Sobre l'últim: cada CNI fa servir el seu. Calico amb VXLAN necessita UDP 4789; amb IP-in-IP necessita el protocol IP 4; Cilium amb VXLAN, UDP 8472. Consulta la documentació del CNI que triïs.

Per al laboratori n'hi ha prou amb obrir aquests ports amb ufw i permetre tot el trànsit de la xarxa interna: sudo ufw allow from 10.10.0.0/24. Verifica des d'un altre node amb nc -zv 10.10.0.11 6443.

Consell de laboratori: si estàs aprenent i alguna cosa no connecta, desactiva temporalment el tallafocs (sudo ufw disable) per descartar que sigui el culpable. En producció, mai; allà es documenta cada regla.

  1. Instal·lació de containerd

Kubernetes no executa contenidors directament: parla amb un runtime a través de la interfície CRI. Des de 1.24, Docker no és un runtime vàlid de forma directa; containerd és l'opció estàndard.

# 1. Repositori de Docker (containerd es distribueix des d'allà)
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 2. Instal·lar NOMÉS containerd (no el motor de Docker)
sudo apt-get update && sudo apt-get install -y containerd.io

4.1 SystemdCgroup: l'error més habitual

Aquí hi ha la fallada número u dels clústers amb kubeadm. containerd porta per defecte una configuració amb SystemdCgroup = false, mentre que el kubelet de Kubernetes 1.30 fa servir el controlador de cgroups systemd. Si no coincideixen, el clúster arrenca aparentment bé i després els pods es reinicien de forma aleatòria sota pressió de memòria, perquè hi ha dos gestors de cgroups barallant-se pel mateix node.

# 1. Generar la configuració per defecte (containerd ve sense config.toml complet)
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml > /dev/null

# 2. Canviar SystemdCgroup de false a true
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

# 3. VERIFICAR que el canvi s'ha aplicat (no te'n fiïs) i reiniciar
grep SystemdCgroup /etc/containerd/config.toml     # ha de dir: SystemdCgroup = true
sudo systemctl restart containerd && sudo systemctl enable containerd

Si el grep no retorna res o retorna false, la ruta de l'opció ha canviat a la teva versió. Edita el fitxer a mà i busca la secció [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options].

4.2 Comprovar que containerd respon a CRI

sudo apt-get install -y cri-tools
printf 'runtime-endpoint: unix:///run/containerd/containerd.sock\n' | sudo tee /etc/crictl.yaml
sudo crictl info | head -20

Si això respon, el runtime està llest. crictl és la teva eina de depuració a nivell de node: crictl ps, crictl images, crictl logs. Quan l'apiserver està caigut, kubectl no serveix de res i crictl és l'únic que tens.

  1. Instal·lació de kubeadm, kubelet i kubectl

Tots tres s'instal·len des del repositori oficial de Kubernetes, que des de 1.28 està segmentat per versió menor. Això és important: el repositori de v1.30 només conté pedaços de 1.30, cosa que evita salts accidentals de versió menor.

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | \
  sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] \
https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | \
  sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update

# Instal·lar una versió de pedaç CONCRETA, no l'última
sudo apt-get install -y kubelet=1.30.4-1.1 kubeadm=1.30.4-1.1 kubectl=1.30.4-1.1

5.1 Fixar les versions amb apt-mark hold

sudo apt-mark hold kubelet kubeadm kubectl
apt-mark showhold                      # comprovar

# Habilitar el kubelet (encara fallarà i reintentarà: és normal, no hi ha
# configuració fins que s'executi kubeadm init o join)
sudo systemctl enable --now kubelet

Per què el hold és imprescindible: sense ell, un apt upgrade rutinari de manteniment del sistema actualitzaria el kubelet en un node qualsevol. Aquell kubelet nou podria ser incompatible amb el pla de control, o simplement reiniciar-se enmig del dia i expulsar tots els pods d'aquell node. Les actualitzacions de Kubernetes són un procediment planificat (apartat 12), mai un efecte secundari.

  1. kubeadm init amb fitxer de configuració

Podries arrencar el clúster amb una tirallonga de banderes (kubeadm init --pod-network-cidr=... --control-plane-endpoint=... --apiserver-cert-extra-sans=...), i funcionaria. Però el problema és evident després de tot el que portem de curs: no està versionat, ningú recorda quines banderes es van fer servir fa un any, i en afegir un node de control cal repetir-les exactament. La forma professional és un fitxer de configuració, desat al costat dels manifestos.

# kubeadm/rn-cluster.yaml
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:                    # com anuncia AQUEST node l'apiserver
  advertiseAddress: "10.10.0.11"
  bindPort: 6443
nodeRegistration:
  name: "rn-control-1"
  criSocket: "unix:///run/containerd/containerd.sock"
  taints:                            # taint estàndard del pla de control (06-05)
    - { key: "node-role.kubernetes.io/control-plane", effect: "NoSchedule" }
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: "v1.30.4"
clusterName: "rutas-norte-lab"

# Punt d'entrada ESTABLE. Es declara des del primer moment encara que
# avui només hi hagi un node: canviar-lo després obliga a regenerar certificats.
controlPlaneEndpoint: "api-k8s.rutasnorte.example:6443"

networking:
  # HA DE coincidir amb el que configuris a Calico/Cilium, i NO pot
  # solapar-se amb la xarxa física (10.10.0.0/24) ni amb serviceSubnet.
  podSubnet: "10.244.0.0/16"
  serviceSubnet: "10.96.0.0/12"
  dnsDomain: "cluster.local"

apiServer:
  # Noms i IP vàlids del certificat de l'apiserver. Accedir per un
  # nom no llistat dona "certificate is valid for ..., not ...".
  certSANs: ["api-k8s.rutasnorte.example", "10.10.0.10", "10.10.0.11",
             "localhost", "127.0.0.1"]
  extraArgs:                          # registre d'auditoria (08-06)
    audit-log-path: "/var/log/kubernetes/audit.log"
    audit-log-maxage: "30"

etcd:
  local: { dataDir: "/var/lib/etcd" }  # etcd apilat, pod estàtic aquí
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: "systemd"               # HA DE coincidir amb SystemdCgroup=true
# Recursos reservats al sistema i al kubelet, que NO s'ofereixen al
# planificador. Sense això, un node saturat es queda sense memòria per a sshd.
systemReserved: { cpu: "200m", memory: "256Mi" }
kubeReserved:   { cpu: "200m", memory: "256Mi" }
evictionHard:   { memory.available: "200Mi", nodefs.available: "10%" }
serverTLSBootstrap: true

Executem:

# Primer, descarregar les imatges per endavant (evita timeouts
# durant l'init si la xarxa és lenta)
sudo kubeadm config images pull --config kubeadm/rn-cluster.yaml

# Arrencar
sudo kubeadm init --config kubeadm/rn-cluster.yaml --upload-certs

--upload-certs puja els certificats del pla de control a un Secret temporal del clúster, xifrat, perquè altres nodes de control s'hi puguin unir sense copiar-los a mà. Caduca a les 2 hores.

Sortida (molt abreujada):

[certs] Generating "ca" certificate and key
[certs] apiserver serving cert is signed for DNS names [api-k8s.rutasnorte.example
  kubernetes kubernetes.default rn-control-1] and IPs [10.96.0.1 10.10.0.11 10.10.0.10]
[control-plane] Creating static Pod manifest for "kube-apiserver"
[etcd] Creating static Pod manifest for local etcd
[apiclient] All control plane components are healthy after 12.503 seconds

Your Kubernetes control-plane has initialized successfully!
You should now deploy a pod network to the cluster.

You can now join any number of control-plane nodes:
  kubeadm join api-k8s.rutasnorte.example:6443 --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:1a2b3c... \
    --control-plane --certificate-key 8f3c1a...

Then you can join any number of worker nodes:
  kubeadm join api-k8s.rutasnorte.example:6443 --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:1a2b3c...

Desa aquesta sortida. Les dues ordres join del final són les que necessites als apartats 9 i 10.

# Configurar kubectl per al teu usuari
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

kubectl get nodes
NAME           STATUS     ROLES           AGE   VERSION
rn-control-1   NotReady   control-plane   62s   v1.30.4

NotReady. És correcte, i ho expliquem a l'apartat 8.

6.1 Camps que més importen

Camp Què fa Què passa si el poses malament
podSubnet Rang d'IP dels pods Si se solapa amb la xarxa física o amb serviceSubnet, l'encaminament es trenca de formes molt difícils de depurar
serviceSubnet Rang de les IP virtuals dels Services Igual; a més la IP .1 d'aquest rang és la del Service kubernetes
controlPlaneEndpoint Nom estable del pla de control Sense ell, no pots afegir nodes de control després sense regenerar certificats
certSANs Noms vàlids del certificat de l'apiserver Accedir per un nom no llistat dona error de certificat
kubernetesVersion Versió a instal·lar Si no coincideix amb els binaris instal·lats, init avisa
cgroupDriver Gestor de cgroups del kubelet Si no coincideix amb containerd, pods inestables sota pressió
criSocket Socket del runtime Amb diversos runtimes instal·lats, kubeadm no sap quin fer servir

Sobre controlPlaneEndpoint: encara que avui només tinguis un node de control i no un balancejador, declara'l amb un nom DNS des del principi. Pots fer que aquest nom apunti avui a 10.10.0.11 i demà a la IP virtual del balancejador. Si no ho fas, el certificat i tots els kubeconfig apuntaran a la IP del node, i muntar HA més tard exigeix regenerar certificats a tot el clúster.

  1. Què crea exactament kubeadm al node

Aquest apartat connecta directament amb l'arquitectura que vam veure a 01-02. Ara la veurem al disc.

7.1 Pods estàtics del pla de control

ls -l /etc/kubernetes/manifests/
-rw------- 1 root root 2405 Sep 12 10:02 etcd.yaml
-rw------- 1 root root 3891 Sep 12 10:02 kube-apiserver.yaml
-rw------- 1 root root 3320 Sep 12 10:02 kube-controller-manager.yaml
-rw------- 1 root root 1463 Sep 12 10:02 kube-scheduler.yaml

Això és un pod estàtic: el kubelet vigila aquest directori i arrenca qualsevol pod que hi trobi, sense passar per l'apiserver. És la solució al problema de l'ou i la gallina: com arrenca l'apiserver, si per crear un pod cal un apiserver? Resposta: no es crea com un pod normal, el llegeix el kubelet directament del disc.

Conseqüències pràctiques molt importants:

  • Si edites /etc/kubernetes/manifests/kube-apiserver.yaml, el kubelet detecta el canvi i reinicia l'apiserver automàticament en segons. És la manera d'afegir una bandera a mà.
  • Si esborres aquest fitxer, l'apiserver desapareix i perds el clúster. Còpia de seguretat abans de tocar res.
  • kubectl delete pod kube-apiserver-rn-control-1 -n kube-system no l'elimina de debò: el kubelet el recrea, perquè la font de veritat és el fitxer.
  • Si l'apiserver no arrenca, kubectl no funciona. Depura'l amb crictl ps -a i crictl logs, o amb journalctl -u kubelet.
# Els pods estàtics apareixen com a objectes "mirall" a l'API,
# amb el nom del node com a sufix: aquesta és la seva marca distintiva.
kubectl get pods -n kube-system -l tier=control-plane
etcd-rn-control-1                      1/1   Running   0   3m
kube-apiserver-rn-control-1            1/1   Running   0   3m
kube-controller-manager-rn-control-1   1/1   Running   0   3m
kube-scheduler-rn-control-1            1/1   Running   0   3m

7.2 Certificats

sudo ls -1 /etc/kubernetes/pki/
apiserver.crt  apiserver.key  apiserver-etcd-client.crt  apiserver-etcd-client.key
apiserver-kubelet-client.crt  apiserver-kubelet-client.key  ca.crt  ca.key  etcd/
front-proxy-ca.crt  front-proxy-ca.key  front-proxy-client.crt  front-proxy-client.key
sa.key  sa.pub
Fitxer Funció
ca.crt / ca.key Autoritat certificadora arrel del clúster. És el secret més valuós: qui la té pot emetre credencials d'administrador
apiserver.crt Certificat de servidor de l'apiserver, amb els certSANs
apiserver-kubelet-client.* Amb què s'identifica l'apiserver en parlar amb els kubelets (kubectl logs, exec)
apiserver-etcd-client.* Amb què s'identifica l'apiserver davant d'etcd
etcd/ CA i certificats propis d'etcd (servidor, parell, sanitat)
front-proxy-* Per a l'agregador d'API (mètriques personalitzades de 09-01)
sa.key / sa.pub Parell de claus amb què se signen els tokens de les ServiceAccounts (03-06)

Per inspeccionar els noms vàlids d'un certificat: sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text | grep -A2 'Subject Alternative Name'.

7.3 Fitxers kubeconfig

A /etc/kubernetes/*.conf n'hi ha un per component: controller-manager.conf, scheduler.conf, kubelet.conf (credencials d'aquest node, amb rotació automàtica) i dos d'administració:

  • admin.conf: el que vas copiar a ~/.kube/config. Des de 1.29 pertany al grup kubeadm:cluster-admins, subjecte a RBAC (08-01).
  • super-admin.conf: salta el RBAC completament (system:masters). És el trenca-vidres quan has trencat el RBAC i no el pots ni arreglar. No el reparteixis.

7.4 Dades d'etcd

A /var/lib/etcd/member/ (amb els seus directoris snap i wal) viu l'estat complet del teu clúster: tots els objectes, tots els Secrets, tot. És el que cal copiar (apartat 13).

  1. Instal·lar el CNI i per què els nodes estan NotReady

kubectl get nodes
kubectl describe node rn-control-1 | grep -A5 Conditions
  Type             Status  Reason                       Message
  Ready            False   KubeletNotReady              container runtime network not
                                                        ready: NetworkReady=false
                                                        reason:NetworkPluginNotReady
                                                        message:Network plugin returns
                                                        error: cni plugin not initialized

El missatge és explícit. El kubelet es nega a declarar-se Ready mentre no existeixi un complement de xarxa que sàpiga assignar IP als pods. Recorda de 04-01: Kubernetes defineix el contracte (tot pod té una IP, tots els pods es veuen sense NAT) però no l'implementa. Això és feina del CNI.

També veuràs CoreDNS a Pending, i és normal: CoreDNS és un pod i necessita xarxa per arrencar.

Instal·lem Calico (el detall dels CNI és a 04-01; aquí només el necessari):

kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/tigera-operator.yaml

cat <<EOF | kubectl apply -f -
apiVersion: operator.tigera.io/v1
kind: Installation
metadata: { name: default }
spec:
  calicoNetwork:
    ipPools:
      # HA DE coincidir amb networking.podSubnet del fitxer de kubeadm
      - { cidr: 10.244.0.0/16, encapsulation: VXLANCrossSubnet, natOutgoing: Enabled }
EOF

kubectl get nodes -w      # NotReady -> Ready en menys d'un minut

I CoreDNS arrenca sol. La seqüència completa és:

sequenceDiagram
    participant K as kubeadm init
    participant Kl as kubelet
    participant A as apiserver
    participant C as CNI (Calico)
    K->>Kl: escriu pods estàtics + config
    Kl->>A: arrenca apiserver, etcd, scheduler, cm
    Kl->>A: registra el node (NotReady: sense xarxa)
    Note over A: CoreDNS queda Pending
    K-->>C: (tu apliques els manifestos del CNI)
    C->>Kl: instal·la el binari CNI a /opt/cni/bin
    Kl->>A: NetworkReady=true → node Ready
    A->>A: CoreDNS es planifica i arrenca

  1. Unir nodes de treball

A rn-worker-1 i rn-worker-2, ja preparats amb els apartats 3, 4 i 5:

sudo kubeadm join api-k8s.rutasnorte.example:6443 \
  --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:1a2b3c4d5e6f...
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster.

Què signifiquen els dos paràmetres:

  • --token: una credencial temporal (caduca a les 24 hores) que autoritza el node a demanar el seu certificat. És un Secret de tipus bootstrap.kubernetes.io/token a kube-system.
  • --discovery-token-ca-cert-hash: el hash SHA-256 de la clau pública de la CA del clúster. Serveix perquè el node verifiqui l'apiserver, no a l'inrevés. Sense ell, un atacant podria suplantar el pla de control i capturar el token. No facis servir mai --discovery-token-unsafe-skip-ca-verification fora d'un laboratori.

9.1 Generar un token nou quan caduca

És la situació més habitual: vols afegir un node tres setmanes després i el token original ja no val.

# En un node de control: una sola ordre que imprimeix tot el necessari
sudo kubeadm token create --print-join-command
kubeadm join api-k8s.rutasnorte.example:6443 --token 7t8u9i.qwertyuiopasdfgh \
  --discovery-token-ca-cert-hash sha256:1a2b3c4d5e6f...

Ordres relacionades:

sudo kubeadm token list                            # tokens vius i caducitat
sudo kubeadm token create --ttl 2h --print-join-command
sudo kubeadm token delete 7t8u9i.qwertyuiopasdfgh

# Recalcular el hash de la CA a mà, si has perdut la sortida de l'init
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | \
  openssl rsa -pubin -outform der 2>/dev/null | \
  openssl dgst -sha256 -hex | sed 's/^.* //'

9.2 Etiquetar els nodes

kubeadm no posa l'etiqueta de rol als nodes de treball per seguretat (un kubelet no pot autoassignar-se etiquetes de rol). Es fa des del pla de control:

kubectl label node rn-worker-1 node-role.kubernetes.io/worker=
kubectl label node rn-worker-2 node-role.kubernetes.io/worker=
# I les etiquetes de topologia de Rutas Norte (09-05)
kubectl label node rn-worker-1 topology.kubernetes.io/zone=lab-a
kubectl label node rn-worker-2 topology.kubernetes.io/zone=lab-b
kubectl get nodes                 # els tres Ready, amb els seus rols

  1. Pla de control en alta disponibilitat

Amb un sol node de control, si rn-control-1 cau, el clúster continua servint trànsit (els pods continuen corrent, kube-proxy continua encaminant) però perds tota capacitat de gestió: no hi ha reprogramació de pods, ni escalat, ni desplegaments, ni HPA. I si el disc d'etcd es perd, perds el clúster sencer.

10.1 El punt d'entrada estable

Tots els nodes de control publiquen el mateix port 6443. Necessites alguna cosa al davant que reparteixi:

flowchart TB
    K[kubectl / kubelets<br/>dels nodes] --> LB["api-k8s.rutasnorte.example:6443<br/>(IP virtual 10.10.0.10)"]
    LB --> C1[rn-control-1<br/>:6443]
    LB --> C2[rn-control-2<br/>:6443]
    LB --> C3[rn-control-3<br/>:6443]
    C1 -.-> E[(etcd apilat<br/>quòrum de 3)]
    C2 -.-> E
    C3 -.-> E

Opcions: HAProxy + keepalived amb una IP virtual flotant (el més comú en centre de dades propi), el balancejador de capa 4 del núvol si estàs en màquines virtuals, o kube-vip com a pod estàtic si no vols infraestructura externa. El que no val és un DNS amb diversos registres A: el client fa memòria cau i no detecta caigudes.

Configuració mínima d'HAProxy:

# /etc/haproxy/haproxy.cfg
frontend k8s-api
    bind *:6443
    mode tcp
    default_backend k8s-control-plane
backend k8s-control-plane
    mode tcp
    option tcp-check
    balance roundrobin
    server rn-control-1 10.10.0.11:6443 check fall 3 rise 2
    server rn-control-2 10.10.0.12:6443 check fall 3 rise 2
    server rn-control-3 10.10.0.13:6443 check fall 3 rise 2

És TCP pur (mode tcp), no HTTP: la connexió a l'apiserver és TLS d'extrem a extrem i el balancejador no l'ha de terminar. Amb check, si un node deixa de respondre al 6443, HAProxy deixa d'enviar-li trànsit automàticament.

10.2 etcd apilat davant d'etcd extern

Aspecte etcd apilat etcd extern
On corre Com a pod estàtic a cada node de control En màquines dedicades
Màquines necessàries 3 (control + etcd junts) 3 de control + 3 d'etcd = 6
Configuració Automàtica amb kubeadm Manual: certificats, unitats systemd
Aïllament de fallades Perdre un node perd un control i un membre d'etcd Independents
Rendiment sota càrrega etcd competeix amb l'apiserver per E/S i CPU etcd té el seu disc per a ell
Recomanació Per defecte, per a la majoria Clústers molt grans o amb requisits estrictes

etcd necessita quòrum: 1 membre tolera 0 fallades, 2 membres també en toleren 0 (pitjor que 1!), 3 en toleren 1, 4 en toleren 1, i 5 en toleren 2. Per això el nombre de nodes de control és sempre senar: 1 (laboratori), 3 (producció normal) o 5 (clústers grans).

Per a etcd extern, es declara al fitxer de kubeadm:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
etcd:
  external:
    endpoints:
      - https://10.10.0.31:2379
      - https://10.10.0.32:2379
      - https://10.10.0.33:2379
    caFile: /etc/kubernetes/pki/etcd/ca.crt
    certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
    keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key

10.3 Unir nodes de control addicionals

# A rn-control-2 i rn-control-3 (ja preparats amb 3, 4 i 5)
sudo kubeadm join api-k8s.rutasnorte.example:6443 \
  --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:1a2b3c... \
  --control-plane \
  --certificate-key 8f3c1a...

La --certificate-key ve de --upload-certs. Si han passat més de 2 hores, es regenera:

# A rn-control-1
sudo kubeadm init phase upload-certs --upload-certs
[upload-certs] Using certificate key:
f0e1d2c3b4a5968778695a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d

Per verificar la salut del clúster d'etcd, kubectl -n kube-system exec etcd-rn-control-1 -- etcdctl ... endpoint status --cluster -w table ha de mostrar els tres membres, amb la mateixa versió, mida de base de dades semblant i exactament un amb IS LEADER = true.

  1. Gestió de certificats

Aquí hi ha el parany que sorprèn més equips: els certificats que genera kubeadm caduquen a l'any. Un clúster muntat al març deixa de funcionar al març de l'any següent, sense previ avís, amb un missatge així:

Unable to connect to the server: x509: certificate has expired or is not yet valid

Kubernetes renova automàticament els certificats durant kubeadm upgrade. Com que molts equips actualitzen almenys un cop l'any, mai ho veuen. Qui no actualitza, s'emporta el sobresalt.

11.1 Comprovar la caducitat

sudo kubeadm certs check-expiration
CERTIFICATE                 EXPIRES                  RESIDUAL TIME
admin.conf                 Sep 12, 2026 10:02 UTC    364d
apiserver                  Sep 12, 2026 10:02 UTC    364d
apiserver-etcd-client      Sep 12, 2026 10:02 UTC    364d
apiserver-kubelet-client   Sep 12, 2026 10:02 UTC    364d
etcd-server, etcd-peer, scheduler.conf, controller-manager.conf ... 364d

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME
ca, etcd-ca, front-proxy-ca   Sep 10, 2035 10:02 UTC   9y

Les CA duren 10 anys; els certificats que signen, 1 any.

Recomanació d'operació: no ho deixis al calendari humà. L'apiserver exposa apiserver_client_certificate_expiration_seconds_bucket, sobre la qual es pot muntar una alerta a Alertmanager (07-04). Més simple i més fiable encara: un CronJob (06-03) o una tasca del sistema que executi kubeadm certs check-expiration setmanalment i avisi si queda menys d'un mes.

11.2 Renovar

# A CADA NODE DE CONTROL
sudo cp -r /etc/kubernetes/pki /root/pki-copia-$(date +%F)      # còpia primer
sudo kubeadm certs renew all              # o només un: kubeadm certs renew apiserver

# Reiniciar els pods estàtics: n'hi ha prou amb treure'ls i tornar-los
sudo mkdir -p /tmp/manifests-aturats
sudo mv /etc/kubernetes/manifests/*.yaml /tmp/manifests-aturats/
sleep 20
sudo mv /tmp/manifests-aturats/*.yaml /etc/kubernetes/manifests/

# Actualitzar el teu kubeconfig personal, que també s'ha renovat
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

sudo kubeadm certs check-expiration && kubectl get nodes

Nota sobre el kubelet: el seu certificat es rota sol, automàticament, si rotateCertificates: true (per defecte). Els certificats que cal renovar a mà són els del pla de control.

  1. Actualització de versió

12.1 La regla del salt d'una versió menor

Només pots saltar una versió menor cada vegada. De 1.28 a 1.30 no es pot anar directe: cal fer 1.28 → 1.29 → 1.30. Cada salt és un procediment complet.

A més, la política de desfasament (version skew) exigeix:

Component Desfasament permès respecte a l'apiserver
kube-controller-manager, kube-scheduler Fins a 1 menor per sota
kubelet Fins a 3 menors per sota
kube-proxy Fins a 3 menors per sota
kubectl 1 per sobre o 1 per sota

D'aquí l'ordre obligatori: primer el pla de control, després els nodes de treball. Mai a l'inrevés.

12.2 Abans de començar

Tres coses, per aquest ordre: còpia de seguretat d'etcd (apartat 13, no negociable), llegir les notes de la versió, i comprovar que res faci servir APIs que desapareixen.

kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis
apiserver_requested_deprecated_apis{group="flowcontrol.apiserver.k8s.io",
  removed_release="1.32",resource="flowschemas",version="v1beta3"} 1

Qualsevol línia aquí és feina que cal fer abans d'actualitzar. Eines com kubent (kube-no-trouble) o pluto escanegen els teus manifestos buscant el mateix.

12.3 Primer node de control

# --- A rn-control-1 ---

# 1. Alliberar el hold i actualitzar NOMÉS kubeadm
sudo apt-mark unhold kubeadm
sudo apt-get update

# Canviar el repositori a la nova versió menor
sudo sed -i 's|v1.30|v1.31|' /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | \
  sudo gpg --dearmor --yes -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo apt-get update

sudo apt-get install -y kubeadm=1.31.1-1.1
sudo apt-mark hold kubeadm
kubeadm version
# 2. Veure el pla: què s'actualitzarà i a què
sudo kubeadm upgrade plan
COMPONENT                 CURRENT   TARGET
kube-apiserver            v1.30.4   v1.31.1
kube-controller-manager   v1.30.4   v1.31.1
kube-scheduler            v1.30.4   v1.31.1
kube-proxy                v1.30.4   v1.31.1
CoreDNS                   v1.11.1   v1.11.3
etcd                      3.5.14    3.5.15

Components that must be upgraded manually: kubelet als 3 nodes.
You can now apply the upgrade by executing:  kubeadm upgrade apply v1.31.1
# 3. Aplicar. Actualitza els manifestos dels pods estàtics i
#    RENOVA ELS CERTIFICATS de passada.
sudo kubeadm upgrade apply v1.31.1
[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.31.1".
# 4. Drenar el node: moure els seus pods a un altre lloc i no acceptar-ne de nous.
#    Respecta els PodDisruptionBudgets de 09-05.
kubectl drain rn-control-1 --ignore-daemonsets --delete-emptydir-data

# 5. Actualitzar kubelet i kubectl
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet=1.31.1-1.1 kubectl=1.31.1-1.1
sudo apt-mark hold kubelet kubectl

sudo systemctl daemon-reload
sudo systemctl restart kubelet

# 6. Tornar el node al servei
kubectl uncordon rn-control-1
kubectl get nodes
NAME           STATUS   ROLES           AGE   VERSION
rn-control-1   Ready    control-plane   3d    v1.31.1
rn-worker-1    Ready    worker          3d    v1.30.4
rn-worker-2    Ready    worker          3d    v1.30.4

12.4 Resta de nodes, un a un

Per als altres nodes de control i per als de treball, el procediment és el mateix de l'apartat anterior amb una sola diferència: es fa servir sudo kubeadm upgrade node en comptes d'upgrade apply.

# A cada node restant, d'un en un:
sudo apt-mark unhold kubeadm && sudo apt-get install -y kubeadm=1.31.1-1.1 && sudo apt-mark hold kubeadm
sudo kubeadm upgrade node                                    # <-- la diferència
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data --timeout=300s
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet=1.31.1-1.1 kubectl=1.31.1-1.1
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload && sudo systemctl restart kubelet
kubectl uncordon <node>
kubectl get nodes    # confirmar Ready i v1.31.1 ABANS de passar al següent

Un a un, verificant pel mig. Si drain es bloqueja, és un PodDisruptionBudget fent la seva feina: postgres-reserves no es pot quedar sense rèpliques. Investiga amb kubectl get pdb -A abans de forçar res.

  1. Còpia de seguretat i restauració d'etcd

A 05-06 vam fer còpies de les dades de les aplicacions amb Velero. Això és una altra cosa: és la còpia de la definició del clúster. Tots els Deployments, Secrets, ConfigMaps, RBAC, CRDs... tot viu a etcd.

Sense còpia d'etcd, una fallada del disc del node de control t'obliga a recrear el clúster des de zero i tornar a aplicar tots els manifestos. Amb GitOps (10-05) això seria recuperable, però perdries tot el que no sigui a Git.

13.1 Fer la còpia

sudo apt-get install -y etcd-client

# En un node de control
sudo ETCDCTL_API=3 etcdctl snapshot save /var/copies/etcd-$(date +%F-%H%M).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# Verificar (una còpia no verificada no és una còpia!)
sudo etcdutl --write-out=table snapshot status /var/copies/etcd-2026-09-12-1042.db
+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| 4f2a91bc |   184920 |       1847 |      28 MB |
+----------+----------+------------+------------+

Aquesta mateixa ordre a /etc/cron.d/copia-etcd, a les 03:15 diàries i encadenada amb un find /var/copies -name 'etcd-*.db' -mtime +14 -delete, cobreix l'automatització amb retenció de dues setmanes.

Tres coses imprescindibles: copia també /etc/kubernetes/pki/ (el snapshot sense la CA és inútil), treu-les del node (una còpia al disc que s'espatllarà no és una còpia) i xifra-les, perquè contenen tots els Secrets de rutas-norte-pro, incloses les credencials de postgres-reserves i les de la passarel·la de pagaments: recorda de 03-02 que els Secrets estan en base64, no xifrats.

13.2 Restaurar

Procediment d'emergència, per quan el pla de control és irrecuperable:

# 1. Aturar el pla de control traient els pods estàtics
sudo mkdir -p /tmp/manifests-aturats
sudo mv /etc/kubernetes/manifests/*.yaml /tmp/manifests-aturats/
sudo crictl ps                      # esperar que no quedi res

# 2. Apartar les dades actuals (NO esborrar-les: per si de cas)
sudo mv /var/lib/etcd /var/lib/etcd-trencat-$(date +%F)

# 3. Restaurar el snapshot en un directori nou
sudo etcdutl snapshot restore /var/copies/etcd-2026-09-12-1042.db \
  --data-dir=/var/lib/etcd --name=rn-control-1 \
  --initial-cluster=rn-control-1=https://10.10.0.11:2380 \
  --initial-advertise-peer-urls=https://10.10.0.11:2380

# 4. Tornar els pods estàtics i verificar
sudo mv /tmp/manifests-aturats/*.yaml /etc/kubernetes/manifests/
sleep 60 && kubectl get nodes && kubectl get pods -A

Punts crítics:

  • En un clúster HA, cal restaurar als tres nodes de control, amb el snapshot idèntic i cadascun amb el seu --name i les seves URL. Si les dades difereixen, etcd no formarà quòrum.
  • L'estat torna al moment de la còpia. Tot el creat després es perd.
  • Els pods que estaven corrent continuen corrent (el kubelet no se n'ha assabentat), però poden aparèixer objectes "fantasma": pods que existeixen al node però no a etcd, o a l'inrevés. Després de restaurar, revisa kubectl get pods -A contra crictl ps a cada node.

Assaja la restauració. Una còpia de seguretat que mai s'ha restaurat no és una còpia de seguretat: és un fitxer. Fes-ho al laboratori, cronometra't, i escriu el procediment en un runbook (11-06).

  1. kubeadm reset i neteja

Quan alguna cosa surt malament i vols tornar a començar, o retirar un node:

# 1. Des del pla de control: drenar i treure el node del clúster
kubectl drain rn-worker-2 --ignore-daemonsets --delete-emptydir-data
kubectl delete node rn-worker-2

# 2. Al mateix node: desfer el que va fer kubeadm
sudo kubeadm reset -f
[reset] Deleting contents of directories: [/etc/kubernetes/manifests
  /var/lib/kubelet /etc/kubernetes/pki]
The reset process does not clean CNI configuration. To do so, you must
remove /etc/cni/net.d
The reset process does not reset or clean up iptables rules.

reset és honest sobre el que no neteja. Cal rematar-ho a mà:

sudo rm -rf /etc/cni/net.d /opt/cni/bin/calico*        # configuració del CNI
sudo iptables -F && sudo iptables -t nat -F            # regles de kube-proxy
sudo iptables -t mangle -F && sudo iptables -X
sudo ipvsadm -C 2>/dev/null || true                    # si feia servir mode IPVS
for i in cni0 flannel.1 vxlan.calico; do               # interfícies virtuals
  sudo ip link delete "$i" 2>/dev/null || true
done
rm -rf $HOME/.kube

Si et saltes els passos 3, 4 i 5, el següent kubeadm init o join en aquella màquina pot semblar que funciona i després donar fallades de xarxa inexplicables: regles velles d'iptables encaminant a IP de pods que ja no existeixen. És una de les causes més freqüents de «vaig reinstal·lar i continua fallant».

Errors Comuns i Consells

1. SystemdCgroup = false a containerd. És l'error número u. El clúster arrenca, tot sembla bé, i dies després els pods es reinicien sota pressió de memòria sense explicació. Verifica sempre amb grep SystemdCgroup /etc/containerd/config.toml i reinicia containerd després de canviar-ho.

2. Oblidar swapoff -a a /etc/fstab. El desactives, funciona, reinicies la màquina un mes després i el kubelet no arrenca. Comenta la línia a /etc/fstab sempre.

3. podSubnet que se solapa amb la xarxa física. Si el teu laboratori fa servir 10.10.0.0/24 i declares podSubnet: 10.0.0.0/8, l'encaminament es trenca de formes que semblen màgia negra. Tria rangs que no col·lisionin amb res: ni amb la xarxa de nodes, ni amb serviceSubnet, ni amb la xarxa corporativa.

4. El rang del CNI diferent de podSubnet. Declares 10.244.0.0/16 a kubeadm i instal·les Calico amb el seu valor per defecte 192.168.0.0/16. Els pods obtenen IP del rang del CNI, però el controller-manager assigna blocs de l'altre. Han de coincidir.

5. No declarar controlPlaneEndpoint des del principi. Afegir alta disponibilitat després obliga a regenerar certificats a tot el clúster. Declara un nom DNS des del primer dia, encara que apunti a una sola IP.

6. Certificats caducats a l'any. Real i molt comú. Alerta al calendari, o millor, al monitoratge.

7. Saltar dues versions menors, o actualitzar els nodes abans que el pla de control. kubeadm upgrade apply v1.32.0 des de 1.30 és rebutjat; i un kubelet 1.31 contra un apiserver 1.30 està fora de la política de desfasament. Una menor cada vegada, pla de control primer.

8. kubectl drain bloquejat i forçar-lo amb --force. El que et bloqueja sol ser un PDB (09-05) protegint postgres-reserves o redis-cache. Forçar-ho pot provocar pèrdua de dades. Investiga abans amb kubectl get pdb -A.

9. Còpies d'etcd sense /etc/kubernetes/pki. El snapshot sense la CA no et torna un clúster funcional. Copia tots dos, junts, xifrats i fora del node.

10. kubeadm reset sense netejar iptables i CNI. Restes que provoquen fallades de xarxa a la reinstal·lació. Executa la neteja completa de l'apartat 14.

11. No fixar apt-mark hold. Un apt upgrade rutinari que actualitzi el kubelet en producció és un incident esperant a passar.

Exercicis

Exercici 1: script de preparació de node

Escriu un script preparar-node.sh idempotent (que es pugui executar dues vegades sense trencar res) que deixi una màquina Ubuntu llesta per a kubeadm join: swap desactivat de forma permanent, mòduls i sysctl, containerd amb SystemdCgroup=true verificat, i kubeadm/kubelet/kubectl 1.30.4 fixats. L'script ha de fallar amb un missatge clar si alguna verificació no passa.

Exercici 2: fitxer de configuració per a HA

Escriu el fitxer de configuració de kubeadm per al clúster rutas-norte-lab amb aquests requisits: tres nodes de control darrere d'api-k8s.rutasnorte.example (IP virtual 10.10.0.10), podSubnet 172.16.0.0/16, etcd apilat, auditoria activada, i el kubelet reservant 500m de CPU i 512Mi de memòria per al sistema. Explica per què vas triar 172.16.0.0/16 i què comprovaries abans.

Exercici 3: pla d'actualització i recuperació

Redacta el procediment complet, en ordre i amb les ordres exactes, per actualitzar el clúster de 1.30.4 a 1.31.1 amb un node de control i dos de treball, incloent-hi la còpia de seguretat prèvia. Afegeix el criteri de "punt sense retorn" i què faries si l'apiserver no arrenca després d'upgrade apply.

Solucions

Solució 1

#!/usr/bin/env bash
# preparar-node.sh — idempotent
set -euo pipefail
VER="1.30.4-1.1"

sudo swapoff -a
sudo sed -i '/\sswap\s/ s/^\([^#]\)/#\1/' /etc/fstab     # no recomenta el ja comentat
[[ $(swapon --show | wc -l) -eq 0 ]] || { echo "ERROR: swap actiu"; exit 1; }

printf 'overlay\nbr_netfilter\n' | sudo tee /etc/modules-load.d/k8s.conf >/dev/null
sudo modprobe overlay; sudo modprobe br_netfilter
printf 'net.bridge.bridge-nf-call-iptables=1\nnet.bridge.bridge-nf-call-ip6tables=1\nnet.ipv4.ip_forward=1\n' \
  | sudo tee /etc/sysctl.d/k8s.conf >/dev/null
sudo sysctl --system >/dev/null
[[ $(sysctl -n net.ipv4.ip_forward) == 1 ]] || { echo "ERROR: ip_forward"; exit 1; }

command -v containerd >/dev/null || sudo apt-get install -y containerd.io
sudo mkdir -p /etc/containerd
[[ -s /etc/containerd/config.toml ]] || \
  containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
grep -q 'SystemdCgroup = true' /etc/containerd/config.toml || { echo "ERROR: cgroup"; exit 1; }
sudo systemctl restart containerd && sudo systemctl enable containerd

sudo apt-mark unhold kubelet kubeadm kubectl 2>/dev/null || true
sudo apt-get install -y kubelet="$VER" kubeadm="$VER" kubectl="$VER"
sudo apt-mark hold kubelet kubeadm kubectl
sudo systemctl enable --now kubelet

Claus d'idempotència: el sed que no recomenta línies ja comentades, el [[ -s ]] abans de regenerar la configuració de containerd, i l'unhold abans d'instal·lar.

Solució 2

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: "v1.30.4"
clusterName: "rutas-norte-lab"
controlPlaneEndpoint: "api-k8s.rutasnorte.example:6443"
networking:
  podSubnet: "172.16.0.0/16"
  serviceSubnet: "10.96.0.0/12"
apiServer:
  certSANs: ["api-k8s.rutasnorte.example", "10.10.0.10",
             "10.10.0.11", "10.10.0.12", "10.10.0.13"]
  extraArgs:
    audit-log-path: "/var/log/kubernetes/audit.log"
    audit-policy-file: "/etc/kubernetes/audit-policy.yaml"
    audit-log-maxage: "30"
  extraVolumes:                       # muntar la política al pod estàtic
    - { name: audit-policy, hostPath: /etc/kubernetes/audit-policy.yaml,
        mountPath: /etc/kubernetes/audit-policy.yaml, readOnly: true, pathType: File }
etcd:
  local: { dataDir: "/var/lib/etcd" }
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: "systemd"
systemReserved: { cpu: "500m", memory: "512Mi" }
kubeReserved:   { cpu: "200m", memory: "256Mi" }
evictionHard:   { memory.available: "200Mi", nodefs.available: "10%" }

Per què 172.16.0.0/16: és un rang privat (RFC 1918) que no col·lisiona amb la xarxa del laboratori (10.10.0.0/24) ni amb serviceSubnet (10.96.0.0/12), i dona 65 536 IP, suficients. Comprovacions prèvies: que 172.16/16 no es faci servir a la xarxa corporativa ni a la VPN, i configurar el CNI amb aquest mateix CIDR.

Solució 3

# --- FASE 0: còpia prèvia (punt de retorn) ---
sudo etcdctl snapshot save /var/copies/pre-131.db --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key
sudo tar czf /var/copies/pki-pre-131.tgz /etc/kubernetes/pki
sudo etcdutl --write-out=table snapshot status /var/copies/pre-131.db     # verificar
kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis     # APIs obsoletes

# --- FASE 1: rn-control-1 ---
sudo apt-mark unhold kubeadm
sudo sed -i 's|v1.30|v1.31|' /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | \
  sudo gpg --dearmor --yes -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo apt-get update && sudo apt-get install -y kubeadm=1.31.1-1.1 && sudo apt-mark hold kubeadm
sudo kubeadm upgrade plan
sudo kubeadm upgrade apply v1.31.1        # <-- PUNT SENSE RETORN
kubectl drain rn-control-1 --ignore-daemonsets --delete-emptydir-data
sudo apt-mark unhold kubelet kubectl && sudo apt-get install -y kubelet=1.31.1-1.1 kubectl=1.31.1-1.1
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload && sudo systemctl restart kubelet
kubectl uncordon rn-control-1 && kubectl get nodes

# --- FASE 2 i 3: rn-worker-1, després rn-worker-2 (un a un) ---
# repositori + kubeadm + `sudo kubeadm upgrade node` + drain + kubelet + uncordon

Punt sense retorn: kubeadm upgrade apply, perquè migra l'esquema d'etcd. Abans, n'hi ha prou amb revertir els paquets; després, la marxa enrere exigeix restaurar el snapshot.

Si l'apiserver no arrenca: sudo crictl ps -a | grep apiserver, sudo crictl logs <id> i journalctl -u kubelet -f. Causes típiques: una bandera nova no suportada o un certificat mal generat. Si no es resol dins la finestra de manteniment, restaurar pre-131.db i pki-pre-131.tgz amb el procediment de l'apartat 13.2 i revertir els paquets a 1.30.4.

Conclusió

Ja saps muntar un clúster de Kubernetes des de zero sobre màquines pròpies. L'essencial:

  • kubeadm arrenca un clúster conforme; no aprovisiona màquines ni instal·la el CNI. Aquesta frontera explica el 90 % dels dubtes inicials, inclòs el node NotReady.
  • La preparació de les màquines —swap, overlay i br_netfilter, ip_forward, ports, i sobretot SystemdCgroup = true a containerd— és on neixen les fallades més difícils de diagnosticar.
  • Un fitxer de configuració versionat (ClusterConfiguration, InitConfiguration, KubeletConfiguration) és infinitament millor que una tirallonga de banderes; controlPlaneEndpoint i certSANs ben posats des del primer dia t'estalvien regenerar certificats més tard.
  • kubeadm deixa al node pods estàtics a /etc/kubernetes/manifests, els certificats a /etc/kubernetes/pki i els kubeconfig: l'arquitectura de 01-02, feta fitxers.
  • Els nodes s'uneixen amb token i hash de la CA; el token caduca a les 24 hores i es regenera amb kubeadm token create --print-join-command. L'alta disponibilitat exigeix un punt d'entrada estable i un nombre senar de membres d'etcd, normalment 3 apilats.
  • Els certificats caduquen a l'any, les actualitzacions van una versió menor cada vegada amb el pla de control abans que els nodes de treball, i les còpies d'etcd (snapshot + pki, xifrades, fora del node, amb restauració assajada) són la diferència entre un mal dia i la fi de l'empresa.

I l'advertiment que cal repetir: operar això en producció exigeix un equip dedicat amb guàrdia. A 10-06 veurem què t'estalvia un clúster gestionat i a quin preu.

Però abans hem de resoldre el problema amb què va començar aquest mòdul. Ja sabem crear clústers, locals i propis; el que continua sense resoldre's són els 120 fitxers YAML duplicats de Rutas Norte. A la lliçó següent, Helm, coneixerem el gestor de paquets de Kubernetes: charts, valors, plantilles i releases. És l'eina amb què ja vam instal·lar cert-manager (04-05) i kube-prometheus-stack (07-03) sense explicar-la; toca entendre-la de debò i fer-la servir per empaquetar la plataforma Rutas Norte en un únic chart amb un fitxer de valors per entorn.

Curs de Kubernetes

Mòdul 1: Introducció a Kubernetes

Mòdul 2: Components Principals de Kubernetes

Mòdul 3: Gestió de Configuració i Secrets

Mòdul 4: Xarxes a Kubernetes

Mòdul 5: Emmagatzematge a Kubernetes

Mòdul 6: Conceptes Avançats de Kubernetes

Mòdul 7: Monitoratge i Registre

Mòdul 8: Seguretat a Kubernetes

Mòdul 9: Escalat i Rendiment

Mòdul 10: Ecosistema i Eines de Kubernetes

Mòdul 11: Estudis de Cas i Aplicacions del Món Real

Mòdul 12: Preparació per a la Certificació de Kubernetes

© Copyright 2026. Tots els drets reservats