Swarm et va donar un clúster amb les idees justes i una corba d'aprenentatge amable. Kubernetes et dona l'estàndard de la indústria a canvi d'un vocabulari nou. La bona notícia és que els conceptes ja els tens: estat desitjat, reconciliació, rèpliques, xarxa entre nodes i secrets muntats com a fitxers. Aquesta lliçó posa noms nous a idees conegudes i hi afegeix les que falten, sense desplegar encara Aurora Libros sencera.
Contingut
- Per què existeix Kubernetes i què hi afegeix
- El model declaratiu i els controladors
- Arquitectura del clúster
- El runtime després de dockershim: CRI i OCI
- Els objectes fonamentals d'un cop d'ull
Pod: la unitat de desplegamentReplicaSetiDeploymentService, els seus tipus iIngressConfigMapiSecretNamespacePersistentVolume,PersistentVolumeClaimiStorageClassJob,CronJob,StatefulSetiDaemonSet- Anatomia d'un manifest: etiquetes i selectors
kubectlessencial, traduït des de Docker- Un clúster local per practicar
- Glossari Docker → Kubernetes
- Per què existeix Kubernetes i què hi afegeix
Kubernetes va néixer a Google a partir de l'experiència de Borg i es va donar a la CNCF el 2015. El seu objectiu no era millorar Swarm, sinó resoldre l'orquestració a escala de milers de nodes i d'equips, i per això el seu disseny és més ambiciós i també més complex.
| Capacitat | Swarm | Kubernetes |
|---|---|---|
| Unitat mínima | Un contenidor per tasca | Pod: diversos contenidors acoblats |
| Extensibilitat | Fixa | CRD i operadors: objectes propis |
| Autoescalat | No | HPA, VPA i autoescalat de nodes (06-06) |
| Emmagatzematge | Volums locals o plugins | CSI, PVC i aprovisionament dinàmic |
| Control d'accés | Rols: manager/worker | RBAC per usuari, recurs i verb |
| Encaminament HTTP | Nginx que hi poses tu | Ingress i Gateway API natius |
| Càrregues amb estat | Sense suport específic | StatefulSet amb identitat estable |
| Corba d'aprenentatge | Dies | Setmanes |
| Ecosistema | Reduït | Enorme: Helm, Argo, Prometheus, malles |
L'última fila és la que decideix a la majoria de les organitzacions. La primera és la que més canvia la teva manera de pensar, i és la que veurem a fons.
- El model declaratiu i els controladors
El cor conceptual és idèntic al de Swarm: descrius l'estat desitjat i el sistema el manté. La diferència és que Kubernetes ho generalitza: tot és un objecte en una base de dades (etcd), i per cada tipus d'objecte hi ha un controlador executant el mateix bucle.
Aquest patró s'anomena reconciliation loop, i explica comportaments que al principi desconcerten:
- Esborres un Pod creat per un Deployment i reapareix: el seu controlador veu que falten rèpliques.
- Edites un Pod a mà i el teu canvi desapareix al desplegament següent: la font de veritat és la plantilla del Deployment.
- Apliques un manifest dues vegades i no passa res: no descrius accions, descrius destins. L'operació és idempotent.
- Arquitectura del clúster
flowchart TB
U["kubectl / CI"] -->|API REST| API
subgraph CP["Pla de control"]
API[kube-apiserver] <--> ETCD[(etcd)]
SCH[kube-scheduler] --> API
CM[kube-controller-manager] --> API
end
API --> K1[kubelet · node-1]
API --> K2[kubelet · node-2]
K1 --> R1["containerd (CRI) → runc"]
K2 --> R2["containerd (CRI) → runc"]
style API fill:#e8f0fe,stroke:#3367d6
| Component | On | Responsabilitat |
|---|---|---|
kube-apiserver |
Pla de control | Única porta d'entrada; valida, autentica i escriu a etcd |
etcd |
Pla de control | Base de dades clau-valor amb tot l'estat. La seva còpia de seguretat és la del clúster |
kube-scheduler |
Pla de control | Decideix en quin node va cada Pod nou segons recursos, afinitats i taints |
kube-controller-manager |
Pla de control | Executa els controladors (Deployment, ReplicaSet, Node...) |
kubelet |
Cada node | Parla amb el runtime perquè els Pods assignats existeixin i en reporta l'estat |
kube-proxy |
Cada node | Programa les regles de xarxa que fan funcionar els Services |
| Runtime (CRI) | Cada node | Executa els contenidors: containerd, CRI-O |
Fixa't en la propietat que fa robust el disseny: ningú no parla amb ningú llevat que sigui amb el kube-apiserver. El scheduler no truca al kubelet; escriu a l'API que un Pod va a cert node, i el kubelet d'aquell node, que està observant l'API, actua. Tot passa pel mateix punt d'autenticació i auditoria.
- El runtime després de dockershim: CRI i OCI
El 2022, Kubernetes 1.24 va eliminar dockershim, l'adaptador que li permetia fer servir Docker Engine com a runtime. Allò va generar titulars del tipus "Kubernetes elimina Docker" que van confondre tothom.
El que va passar en realitat és molt menys dramàtic:
- Kubernetes parla amb els runtimes per una interfície anomenada CRI (Container Runtime Interface).
- Docker Engine no implementa CRI, així que calia un adaptador (
dockershim) mantingut pel mateix projecte. - Aquell adaptador es va retirar, i els nodes fan servir containerd o CRI-O directament, que sí que parlen CRI. I containerd és, precisament, el mateix component que Docker Engine fa servir per dins des de 01-03.
Les teves imatges continuen valent exactament igual. El que construeixes amb docker build és una imatge OCI, un format estàndard, no un format "de Docker". La ghcr.io/auroralibros/aurora-api:2.0.0 que va produir el teu pipeline s'executa a containerd sense cap modificació. L'única cosa que va canviar és quin programa l'arrenca al node.
- Els objectes fonamentals d'un cop d'ull
| Objecte | Per a què | El crees a mà? |
|---|---|---|
Pod |
Un o diversos contenidors que comparteixen xarxa i emmagatzematge | Gairebé mai |
ReplicaSet |
Manté N Pods idèntics | No: el crea el Deployment |
Deployment |
Gestiona ReplicaSets i les actualitzacions progressives | Sí |
Service |
IP i nom DNS estables davant d'un conjunt de Pods | Sí |
Ingress |
Encaminament HTTP/HTTPS d'entrada per host i ruta | Sí |
ConfigMap |
Configuració no sensible | Sí |
Secret |
Configuració sensible (codificada en base64) | Sí |
Namespace |
Espai de noms lògic dins del clúster | Sí |
PersistentVolumeClaim |
Petició d'emmagatzematge persistent | Sí |
StorageClass |
Com s'aprovisiona aquest emmagatzematge | La dona el clúster |
Job / CronJob |
Tasca que acaba / tasca periòdica | Sí |
StatefulSet |
Pods amb identitat i disc estables | Sí |
DaemonSet |
Un Pod per node | Sí |
Pod: la unitat de desplegament
Pod: la unitat de desplegamentUn Pod és un grup de contenidors que comparteixen namespace de xarxa (mateixa IP, es parlen per localhost), namespace IPC i, opcionalment, volums. És la unitat que el scheduler col·loca i que s'escala: a Kubernetes no es repliquen contenidors, es repliquen Pods.
apiVersion: v1
kind: Pod
metadata:
name: aurora-cache
labels: { app: aurora-cache }
spec:
containers:
- name: redis
image: redis:7-alpine
args: ["--maxmemory", "200mb", "--maxmemory-policy", "allkeys-lru"]
ports: [{ containerPort: 6379 }]
resources:
requests: { memory: 64Mi, cpu: 50m }
limits: { memory: 256Mi, cpu: 500m }Gairebé mai no es crea un Pod a mà, per una raó contundent: un Pod no es recupera. Si el node mor, el Pod mor amb ell i ningú no el recrea; és l'equivalent exacte d'un docker run sense --restart. El que es crea és un Deployment, que a través del seu ReplicaSet garanteix que sempre hi hagi tants Pods com vas demanar.
La majoria dels Pods tenen un sol contenidor. El cas multicontenidor és el patró sidecar: un contenidor auxiliar que acompanya el principal —un recol·lector de logs, un proxy de malla de serveis, un sincronitzador de fitxers— i es beneficia de compartir xarxa i volums.
ReplicaSet i Deployment
ReplicaSet i DeploymentapiVersion: apps/v1
kind: Deployment
metadata:
name: aurora-cache
spec:
replicas: 1
selector:
matchLabels: { app: aurora-cache } # quins Pods em pertanyen
template: # la plantilla del Pod (sense apiVersion ni kind)
metadata:
labels: { app: aurora-cache } # HA de casar amb el selector
spec:
containers:
- name: redis
image: redis:7-alpine
ports: [{ containerPort: 6379 }]La cadena de responsabilitat té tres baules, i entendre-la evita molta confusió:
El ReplicaSet només sap comptar: manté N Pods que casin amb el seu selector. El Deployment gestiona ReplicaSets: en canviar la imatge en crea un de nou, l'omple mentre buida l'antic, i conserva el vell amb zero rèpliques per poder desfer. Aquell ReplicaSet buit que veus a kubectl get rs no és brossa: és el teu historial de rollback (06-07).
Un avís: el selector d'un Deployment és immutable. Si el canvies, cal esborrar i recrear l'objecte. Convé triar bé les etiquetes la primera vegada.
Service, els seus tipus i Ingress
Service, els seus tipus i IngressEls Pods són bestiar, no mascotes: neixen i moren amb IPs diferents. Un Service aporta una IP virtual i un nom DNS estables davant d'un conjunt canviant de Pods, seleccionats —de nou— per etiquetes.
apiVersion: v1
kind: Service
metadata:
name: aurora-cache
spec:
type: ClusterIP
selector: { app: aurora-cache } # qualsevol Pod amb aquesta etiqueta entra al balanceig
ports:
- port: 6379 # el port del Service
targetPort: 6379 # el port del contenidorEls Pods hi arriben per DNS amb aurora-cache dins del namespace, o aurora-cache.aurora.svc.cluster.local des de fora: és l'equivalent del DNS per nom de servei de Compose i Swarm.
| Tipus | Què exposa | Abast | Ús a Aurora Libros |
|---|---|---|---|
ClusterIP (per defecte) |
IP virtual interna | Només dins del clúster | aurora-db, aurora-cache, aurora-api |
NodePort |
Un port (30000-32767) a tots els nodes | Extern, tosc | Proves locals |
LoadBalancer |
Un balancejador del proveïdor de núvol | Extern, un per servei | Només aurora-web |
ExternalName |
Un CNAME a un domini extern |
— | Migrar a una BD gestionada |
Headless (clusterIP: None) |
Sense IP virtual: DNS a cada Pod | Intern | aurora-db en StatefulSet |
Un LoadBalancer per servei es torna car de seguida: cadascun és un balancejador facturat del proveïdor. La solució estàndard és un Ingress: un únic punt d'entrada que encamina per host i per ruta cap a diversos Services.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: aurora
annotations: { nginx.ingress.kubernetes.io/proxy-body-size: "8m" }
spec:
ingressClassName: nginx # quin controlador l'atén
rules:
- host: llibres.aurora.example
http:
paths:
- path: /api
pathType: Prefix
backend: { service: { name: aurora-api, port: { number: 3000 } } }
- path: /
pathType: Prefix
backend: { service: { name: aurora-web, port: { number: 80 } } }Detall que causa desconcert la primera vegada: l'objecte Ingress no fa res per si sol. És una declaració d'intencions que necessita un ingress controller instal·lat al clúster (ingress-nginx, Traefik, HAProxy...) que la llegeixi i configuri el proxy real. Sense controlador, l'objecte existeix, no dona error i el trànsit no arriba enlloc.
ConfigMap i Secret
ConfigMap i SecretapiVersion: v1
kind: ConfigMap
metadata: { name: aurora-config }
data:
DB_HOST: aurora-db
DB_NAME: aurora_llibres
CACHE_TTL: "60"
TERMINI_ATURADA_MS: "15000"
---
apiVersion: v1
kind: Secret
metadata: { name: aurora-secrets }
type: Opaque
stringData: # stringData accepta text pla; Kubernetes el codifica
DB_PASSWORD: clau-ficticia-auroraTots dos es consumeixen igual, i hi ha dues maneres amb implicacions diferents:
envFrom: # totes les claus com a variables
- configMapRef: { name: aurora-config }
env: # una clau concreta
- name: DB_PASSWORD
valueFrom:
secretKeyRef: { name: aurora-secrets, key: DB_PASSWORD }
volumeMounts: # o com a fitxers: el patró _FILE de 06-01
- { name: secrets, mountPath: /run/secrets, readOnly: true }Advertència important sobre
Secret. Un Secret de Kubernetes està en base64, que no és xifratge: qualsevol amb permís de lectura sobre l'objecte veu el valor amb unbase64 -d. A més,etcdno xifra en repòs llevat que s'activi explícitament. Per a producció cal activar el xifratge en repòs, restringir l'accés amb RBAC i valorar un gestor extern (Vault, Sealed Secrets, el gestor de secrets del proveïdor de núvol amb el driver CSI). És una decisió que has d'acordar amb el responsable de seguretat de la teva organització, no un ajust per defecte acceptable.
I una diferència pràctica respecte de Swarm: muntat com a volum, un Secret o ConfigMap actualitzat es propaga al fitxer del Pod sense reiniciar-lo (amb un retard de fins a un minut); injectat com a variable d'entorn, no s'actualitza mai fins que el Pod es recrea.
Namespace
NamespaceUn Namespace és una partició lògica del clúster. Dona tres coses: noms que no col·lideixen (pot haver-hi un aurora-api a aurora i un altre a aurora-staging), un àmbit per a RBAC i quotes, i un límit de xarxa si fas servir NetworkPolicy. No és un límit de seguretat fort per si mateix: no aïlla el kernel, només l'API.
Amb kubectl config set-context --current --namespace=aurora t'estalvies escriure -n aurora a cada comanda.
PersistentVolume, PersistentVolumeClaim i StorageClass
PersistentVolume, PersistentVolumeClaim i StorageClassKubernetes separa qui demana emmagatzematge de qui el proporciona, que és justament el que faltava a Swarm.
| Objecte | Qui l'escriu | Què diu |
|---|---|---|
StorageClass |
La plataforma | Com es crea el disc (tipus, política d'esborrat) |
PersistentVolumeClaim (PVC) |
Tu | "Vull 10 GiB amb accés ReadWriteOnce" |
PersistentVolume (PV) |
L'aprovisionador | El disc real, ja creat |
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: aurora-dades }
spec:
accessModes: [ReadWriteOnce]
storageClassName: standard
resources: { requests: { storage: 10Gi } }| Mode d'accés | Significat | Qui el suporta |
|---|---|---|
ReadWriteOnce (RWO) |
Escriptura des d'un node | Discos de bloc: EBS, PD, iSCSI |
ReadOnlyMany (ROX) |
Lectura des de diversos nodes | NFS, emmagatzematge d'objectes |
ReadWriteMany (RWX) |
Escriptura des de diversos nodes | NFS, CephFS, EFS |
Gairebé tots els discos de bloc dels proveïdors de núvol són RWO, i d'aquí surt un límit molt real: un PVC d'aquest tipus no es pot muntar alhora en Pods de nodes diferents. És la raó tècnica per la qual aurora-db no s'escala replicant Pods, i la tractarem a 06-06.
Job, CronJob, StatefulSet i DaemonSet
Job, CronJob, StatefulSet i DaemonSet| Objecte | Garantia | A Aurora Libros |
|---|---|---|
Job |
Executa fins a completar N vegades amb èxit | Migració de l'esquema abans d'un desplegament |
CronJob |
Crea Jobs segons una expressió cron | Còpia nocturna amb pg_dump |
StatefulSet |
Nom i identitat estables (-0, -1), PVC propi per Pod, ordre d'arrencada i aturada |
aurora-db |
DaemonSet |
Un Pod a cada node (i als que s'hi afegeixin) | Promtail, node-exporter |
El DaemonSet és literalment el mode: global de Swarm. El StatefulSet és el que Swarm no tenia: cada rèplica rep un nom estable i el seu propi volum, sobreviu al reinici amb la mateixa identitat i s'arrenca i s'atura en ordre. És el que necessita qualsevol cosa amb estat, i el faràs servir a 06-05.
- Anatomia d'un manifest: etiquetes i selectors
Tots els objectes comparteixen quatre camps de primer nivell:
| Camp | Què és | Exemple |
|---|---|---|
apiVersion |
Grup i versió de l'API | apps/v1, v1, networking.k8s.io/v1 |
kind |
Tipus d'objecte | Deployment, Service |
metadata |
Nom, namespace, etiquetes, anotacions | name: aurora-api |
spec |
L'estat desitjat | replicas: 3 |
status |
L'estat real (l'escriu el sistema, no tu) | readyReplicas: 3 |
I ara el més important de tota la lliçó. A Kubernetes els objectes no es referencien per nom, sinó per etiquetes. Un Service no diu "envia trànsit als Pods del Deployment aurora-api"; diu "envia trànsit a tot Pod amb l'etiqueta app: aurora-api".
flowchart LR
S["Service<br/>selector: app=aurora-api"] -.->|selecciona| P1["Pod app=aurora-api"]
S -.-> P2["Pod app=aurora-api"]
S -.-x P3["Pod app=aurora-web"]
D["Deployment<br/>selector: app=aurora-api"] -->|crea| P1
D --> P2
Aquest acoblament feble és potent i fràgil alhora. Potent perquè permet coses com dirigir trànsit a Pods de dos Deployments diferents alhora, que és la base del blue-green i del canari de 06-07. Fràgil perquè una errada d'escriptura en una etiqueta no dona cap error: el Service simplement no troba Pods, es queda sense endpoints i el trànsit retorna 503. És la fallada silenciosa més comuna, i es diagnostica sempre igual: kubectl get endpoints <servei>.
Les etiquetes recomanades per la comunitat, que convé adoptar des del principi:
labels:
app.kubernetes.io/name: aurora-api
app.kubernetes.io/component: backend
app.kubernetes.io/part-of: aurora-libros
app.kubernetes.io/version: "2.0.0"
kubectl essencial, traduït des de Docker
kubectl essencial, traduït des de Docker| Tasca | Docker / Compose | kubectl |
|---|---|---|
| Crear o actualitzar | docker compose up -d |
kubectl apply -f manifestos/ |
| Llistar | docker ps |
kubectl get pods |
| Veure-ho tot | docker compose ps |
kubectl get all -n aurora |
| Detall i esdeveniments | docker inspect |
kubectl describe pod <p> |
| Logs | docker logs -f |
kubectl logs -f <p> |
| Logs de l'intent anterior | — | kubectl logs <p> --previous |
| Shell a dins | docker exec -it c sh |
kubectl exec -it <p> -- sh |
| Accedir a un port | -p 8080:3000 |
kubectl port-forward svc/aurora-api 8080:3000 |
| Eliminar | docker compose down |
kubectl delete -f manifestos/ |
| Escalar | docker service scale |
kubectl scale deploy/aurora-api --replicas=3 |
| Ús de recursos | docker stats |
kubectl top pods |
| Consultar l'esquema | docker run --help |
kubectl explain deployment.spec.strategy |
kubectl get pods -o wide # afegeix node i IP
kubectl get deploy aurora-api -o yaml # l'objecte sencer, amb el seu status
kubectl get pods -o jsonpath='{.items[*].spec.containers[*].image}'
kubectl get pods -w # observar canvis en directe
kubectl describe pod aurora-api-7d9f -n aurora # la secció Events és el 80 % del diagnòsticDues comandes mereixen destacar-se. kubectl explain és la documentació oficial sense sortir del terminal i serveix per a qualsevol camp de qualsevol objecte, inclosos els que instal·lis després. I kubectl describe acaba amb una llista d'esdeveniments que gairebé sempre conté la causa exacta del problema: per què el scheduler no troba node, per què falla la descàrrega de la imatge o per què una sonda no passa.
- Un clúster local per practicar
| Eina | Com funciona | Arrencada | Multinode | Avantatge |
|---|---|---|---|---|
| kind | Nodes com a contenidors Docker | ~30 s | Sí, trivial | Ideal per a CI; carrega imatges locals |
| minikube | VM o contenidor | ~60 s | Limitat | Addons integrats (Ingress, dashboard) |
| k3d | k3s (Kubernetes lleuger) en contenidors | ~20 s | Sí | El més lleuger; molt ràpid |
| Docker Desktop | Un clúster d'un node integrat | Un clic | No | Zero instal·lació addicional |
# kind-aurora.yaml — tres nodes i el port 80 mapejat per a l'Ingress
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs: { node-labels: "ingress-ready=true" }
extraPortMappings:
- { containerPort: 80, hostPort: 8080, protocol: TCP }
- role: worker
- role: workerkind create cluster --name aurora --config kind-aurora.yaml
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# aurora-control-plane Ready control-plane 48s v1.31.0
# aurora-worker Ready <none> 31s v1.31.0
# aurora-worker2 Ready <none> 31s v1.31.0
# Primer contacte, amb el servei més simple d'Aurora Libros
kubectl create namespace aurora
kubectl run aurora-cache --image=redis:7-alpine -n aurora --port=6379 --labels=app=aurora-cache
kubectl exec -it aurora-cache -n aurora -- redis-cli ping # PONG
- Glossari Docker → Kubernetes
| El que ja saps | A Kubernetes | Matís |
|---|---|---|
| Contenidor | Pod | El Pod pot tenir diversos contenidors |
compose.yaml |
Manifestos YAML | Un per objecte, o diversos separats per --- |
docker compose up -d |
kubectl apply -f . |
Declaratiu en tots dos casos |
docker compose down |
kubectl delete -f . |
|
| Xarxa de Compose / DNS per nom | Service | El DNS el dona el Service, no la xarxa |
| Port publicat | Service + Ingress |
Separació entre exposar i encaminar |
| Volum amb nom | PVC + StorageClass |
Es demana, no es crea |
deploy.replicas de Swarm |
spec.replicas del Deployment |
Concepte idèntic |
mode: global |
DaemonSet | Idèntic |
| Secret de Swarm | Secret | Base64, no xifrat per defecte |
docker service ps |
kubectl get pods + describe |
Els esdeveniments són a describe |
healthcheck |
livenessProbe + readinessProbe + startupProbe |
Les tres de 06-01 |
| Projecte de Compose | Namespace | Espai de noms |
Errors Habituals i Consells
- Crear Pods solts. Ningú no els recrea si el node cau. Llevat que sigui per a una prova de trenta segons, fes servir sempre un Deployment.
- Selector i etiquetes que no casen. L'error més freqüent i el més silenciós: el Deployment crea Pods que el seu Service no troba. Verifica-ho amb
kubectl get endpoints. - Creure que un
Secretestà xifrat. És base64. Activa-li el xifratge en repòs i protegeix-lo amb RBAC. - Esperar que un
Ingressfuncioni sense controlador. L'objecte es crea sense queixar-se i no encamina res. - Oblidar el namespace.
kubectl get podsmira el namespace per defecte i sembla que no hi hagi res. Fixa el context o fes servir-n. - Definir
limitssenserequests. Kubernetes copia el límit a la petició i reserves molt més del que necessites. - Editar Pods amb
kubectl edit. El canvi dura fins al desplegament següent. Modifica el Deployment. - Consell:
kubectl describeabans quekubectl logs. Si el Pod no arrenca, no hi ha logs; la causa és als esdeveniments. - Consell:
kubectl apply --dry-run=server -f .valida els manifestos contra l'API real sense crear res. Fes-ho servir al pipeline.
Exercicis
Exercici 1. Munta un clúster kind de tres nodes, desplega aurora-cache amb un Deployment i el seu Service ClusterIP, i comprova des d'un altre Pod que el nom aurora-cache resol i respon.
Exercici 2. Demostra el bucle de reconciliació i la diferència entre Pod solt i Deployment: crea un Pod a mà i un altre gestionat per un Deployment, esborra'ls i observa què passa amb cadascun.
Exercici 3. Provoca expressament la fallada silenciosa de les etiquetes: canvia el selector d'un Service perquè no casi amb cap Pod i diagnostica-ho sense mirar el manifest.
Solucions
Solució 1.
kind create cluster --name aurora --config kind-aurora.yaml
kubectl create namespace aurora
kubectl config set-context --current --namespace=aurora
kubectl apply -f k8s/cache.yaml # el Deployment i el Service de les seccions 7 i 8
kubectl get deploy,rs,pods,svc
# deployment.apps/aurora-cache 1/1 1 1 12s
# replicaset.apps/aurora-cache-6b8d4c9f7 1 1 1 12s
# pod/aurora-cache-6b8d4c9f7-x2klp 1/1 Running 0 12s
# service/aurora-cache ClusterIP 10.96.184.22 <none> 6379/TCP 12sEn una sola sortida hi ha la cadena sencera de la secció 7: un Deployment que ha creat un ReplicaSet el nom del qual porta el sufix 6b8d4c9f7 —el hash de la plantilla del Pod—, i aquest ReplicaSet ha creat un Pod que hereta el hash i hi afegeix un sufix aleatori. Aquest hash és la peça clau del desplegament progressiu: canviar la imatge produeix un altre hash i, per tant, un ReplicaSet nou.
kubectl run client --rm -it --image=redis:7-alpine --restart=Never -- sh -c '
nslookup aurora-cache | tail -2
redis-cli -h aurora-cache ping
redis-cli -h aurora-cache.aurora.svc.cluster.local set llibre "Rayuela"'
# Name: aurora-cache.aurora.svc.cluster.local
# Address: 10.96.184.22
# PONG
# OKEl nom curt i el complet resolen a la mateixa IP, que és la IP virtual del Service, no la del Pod. Aquesta distinció és la que fa que els Pods puguin morir i renéixer amb altres adreces sense que ningú se n'hagi d'assabentar: el client parla sempre amb el Service. És el mateix servei que et donava el DNS intern de Compose i Swarm, amb una diferència important: aquí el proporciona un objecte explícit que pots inspeccionar i modificar.
Solució 2.
kubectl run solt --image=redis:7-alpine --labels=app=prova
kubectl create deployment gestionat --image=redis:7-alpine --replicas=2
kubectl get pods -o custom-columns=NOM:.metadata.name,PROPIETARI:.metadata.ownerReferences[0].kind
# NOM PROPIETARI
# solt <none>
# gestionat-5f7c8d9b4-hq2vn ReplicaSet
# gestionat-5f7c8d9b4-t8plw ReplicaSet
kubectl delete pod solt gestionat-5f7c8d9b4-hq2vn && sleep 5 && kubectl get pods
# NAME READY STATUS RESTARTS AGE
# gestionat-5f7c8d9b4-t8plw 1/1 Running 0 2m
# gestionat-5f7c8d9b4-vk9xz 1/1 Running 0 5sVas esborrar dos Pods i només en va tornar un, amb nom diferent i cinc segons d'edat. La columna PROPIETARI explica exactament per què: el Pod solt no té ownerReferences, així que en esborrar-lo ningú no va trobar res a faltar; els altres pertanyen a un ReplicaSet el controlador del qual observa l'API, va veure que tenia un Pod on n'hi havia d'haver dos i en va crear un de nou.
Aquesta és la raó pràctica de la regla de la secció 6. Un Pod solt és equivalent a un docker run sense política de reinici: sobreviu mentre res no el molesti, i desapareix amb el seu node. El detall del nom diferent també importa, perquè revela que el Pod no es "reinicia": se'n crea un altre, amb una altra IP, i per això cap configuració no pot dependre de la identitat d'un Pod concret. Quan aquesta identitat sí que cal —com a aurora-db— existeix el StatefulSet.
Solució 3.
kubectl patch svc aurora-cache -p '{"spec":{"selector":{"app":"aurora-cach"}}}' # errada
kubectl run client --rm -it --image=redis:7-alpine --restart=Never -- \
redis-cli -h aurora-cache -t 3 ping
# Could not connect to Redis at aurora-cache:6379: Connection refusedEl diagnòstic, sense obrir cap fitxer:
kubectl get endpoints aurora-cache
kubectl get svc aurora-cache -o jsonpath='{.spec.selector}'; echo
kubectl get pods --show-labelsNAME ENDPOINTS AGE
aurora-cache <none> 14m
{"app":"aurora-cach"}
aurora-cache-6b8d4c9f7-x2klp 1/1 Running app=aurora-cache,pod-template-hash=6b8d4c9f7Els ENDPOINTS a <none> són el símptoma decisiu, i la seqüència de tres comandes és la que cal memoritzar: el Service existeix, té la seva IP virtual, el DNS resol perfectament... i al darrere no hi ha cap Pod. Comparant el selector (app=aurora-cach) amb les etiquetes reals (app=aurora-cache) apareix la lletra que falta.
kubectl patch svc aurora-cache -p '{"spec":{"selector":{"app":"aurora-cache"}}}'
kubectl get endpoints aurora-cache
# aurora-cache 10.244.1.7:6379 15mEl que té de pedagògic l'exercici és el que no va passar: ni el kubectl patch ni el kubectl get svc ni el describe del Deployment van donar el més mínim avís. Kubernetes no valida que un selector trobi res, perquè un Service que encara no té Pods és una situació legítima —els tindrà quan es despleguin—. El preu d'aquest acoblament feble, que a 06-07 et permetrà commutar trànsit entre dues versions canviant una etiqueta, és que una errada d'escriptura es manifesta com un 503 en producció i no com un error en aplicar el manifest.
D'aquí surt la regla d'operació: després de cada kubectl apply que toqui un Service, comprova'n els endpoints. És una comanda i evita la mena d'incident més frustrant d'aquesta plataforma.
Conclusió
Ja parles Kubernetes. Saps que el seu cor és el mateix bucle de reconciliació de Swarm, generalitzat a tots els objectes d'etcd, i que això explica que un Pod esborrat reaparegui i que un apply repetit no faci res. Coneixes l'arquitectura —kube-apiserver com a única porta, etcd com a estat, el planificador, els controladors, i a cada node el kubelet i el kube-proxy— i la propietat que la fa robusta: ningú no parla amb ningú llevat que sigui amb l'API. I tens resolt el malentès de dockershim: Kubernetes executa containerd per CRI, però les teves imatges valen igual perquè són OCI.
Domines el catàleg d'objectes amb el seu YAML mínim: el Pod com a unitat i per què no es crea a mà, la cadena Deployment → ReplicaSet → Pods on el ReplicaSet buit és el teu historial de rollback, el Service amb les seves cinc variants i l'Ingress que no encamina res sense un controlador instal·lat, ConfigMap i Secret —amb l'advertència que base64 no és xifratge—, el Namespace, la tríada PVC/PV/StorageClass amb els modes d'accés que explicaran a 06-06 per què una base de dades no es replica sense més, i els quatre controladors restants, entre ells el DaemonSet que és el mode: global de Swarm i el StatefulSet que Swarm no tenia.
Sobretot, has interioritzat la peça central: etiquetes i selectors. Tot es connecta per etiquetes, mai per nom, i ho has comprovat trencant un selector expressament per veure la fallada més silenciosa de la plataforma —un Service perfectament sa amb ENDPOINTS <none>— i diagnosticant-la amb tres comandes. Tradueixes cada operació de Docker al seu kubectl equivalent, tens un clúster kind de tres nodes amb aurora-cache corrent a dins, i un glossari que converteix el que ja sabies en el que acabes d'aprendre.
A la lliçó següent, Desplegant Contenidors Docker a Kubernetes, arriba l'aplicació sencera. Escriuràs els manifestos reals dels quatre serveis: el StatefulSet d'aurora-db amb els seus volumeClaimTemplates i el seu Service headless, el Deployment d'aurora-api amb les tres sondes de 06-01, el seu securityContext endurit i la seva configuració injectada, l'Ingress d'aurora-web, i els organitzaràs amb Kustomize en bases i overlays per entorn, amb la taula d'estats d'un Pod —Pending, ImagePullBackOff, CrashLoopBackOff, OOMKilled— i la seva comanda de diagnòstic per quan alguna cosa no arrenqui.
Docker: De Principiant a Avançat
Mòdul 1: Introducció a Docker
- Què és Docker?
- Instal·lant Docker
- Arquitectura de Docker
- Comandes Bàsiques de Docker
- Entenent les Imatges de Docker
- Creant el teu Primer Contenidor Docker
- El Projecte del Curs: la Plataforma Aurora Libros
Mòdul 2: Treballant amb Imatges Docker
- Docker Hub i Repositoris
- Construint Imatges Docker
- Conceptes Bàsics de Dockerfile
- Instruccions Avançades del Dockerfile
- Gestionant Imatges Docker
- Etiquetatge i Publicació d'Imatges
Mòdul 3: Contenidors Docker
- Executant Contenidors
- Cicle de Vida del Contenidor
- Gestionant Contenidors
- Inspecció i Depuració de Contenidors
- Xarxes a Docker
- Persistència de Dades amb Volums
- Límits de Recursos i Polítiques de Reinici
Mòdul 4: Docker Compose
- Introducció a Docker Compose
- Definint Serveis a Docker Compose
- Comandes de Docker Compose
- Aplicacions Multi-Contenidor
- Variables d'Entorn a Docker Compose
- Perfils, Overrides i Múltiples Entorns
- Desenvolupament Local amb Docker Compose
Mòdul 5: Conceptes Avançats de Docker
- Aprofundiment en Xarxes Docker
- Opcions d'Emmagatzematge Docker
- Millors Pràctiques de Seguretat a Docker
- Optimitzant Imatges Docker
- Builds Avançades amb BuildKit i Buildx
- Registre i Monitoratge a Docker
- El Runtime per Dins: Namespaces, Cgroups i Capes
Mòdul 6: Docker en Producció
- Preparar una Imatge per a Producció
- CI/CD amb Docker
- Orquestrant Contenidors amb Docker Swarm
- Introducció a Kubernetes
- Desplegant Contenidors Docker a Kubernetes
- Escalat i Balanceig de Càrrega
- Estratègies de Desplegament i Rollback
