Tanquem el mòdul 6 amb un diagnòstic incòmode: la plataforma Rutas Norte ja és potent —té estat, és extensible i està automatitzada— però és completament opaca. No sabem si api-reserves està realment sana, ni quanta memòria consumeix de debò postgres-reserves, ni què va passar ahir a la nit a les tres de la matinada. El mòdul 7 es dedica a obrir aquesta caixa negra, i el primer pas no és mesurar: és ensenyar a Kubernetes a distingir un contenidor viu d'una aplicació que funciona.
Aquesta lliçó tracta de les sondes (probes): els tres mecanismes amb què Kubernetes pregunta periòdicament a cada contenidor si està viu, si està preparat per rebre trànsit i si ha acabat d'arrencar. Són la peça que faltava des del mòdul 2: allà vam deixar dit explícitament que un desplegament sense tall de servei no és possible sense sondes, i aquí saldem aquest deute. També són, de llarg, la funcionalitat de Kubernetes que més incidents de producció causa quan es configura malament: una livenessProbe massa agressiva pot tombar una plataforma sencera en pocs minuts. Anem a entendre-les a fons.
Contingut
- El problema: "el procés és viu" no vol dir "l'aplicació funciona"
- Les tres sondes i què fa Kubernetes amb cada resultat
- Els quatre manejadors:
httpGet,tcpSocket,execigrpc - Els paràmetres temporals i el càlcul exacte del temps de detecció
- L'error més car del mòdul: la liveness en cascada
startupProbe: la solució als arrencades lentes depostgres-reserves- Sondes i desplegaments sense tall: tancant el deute de 02-04
- El tancament net:
terminationGracePeriodSeconds,preStopi els 502 - Disseny de les sondes de cada component de Rutas Norte
- Errors comuns i consells
- Exercicis
- El problema: "el procés és viu" no vol dir "l'aplicació funciona"
Fins ara, l'única senyal de salut que Kubernetes tenia dels nostres contenidors era brutalment simple: continua executant-se el procés principal (PID 1)? Si el procés acaba, el kubelet aplica la restartPolicy que vam estudiar a 02-01 i reinicia el contenidor. Si el procés no acaba, Kubernetes assumeix que tot va bé.
Aquesta suposició és falsa la major part del temps. Vegem el cas real d'api-reserves.
api-reserves és una API Node.js que manté un pool de connexions contra postgres-reserves, amb un màxim de 20 connexions. Un dimarts a la tarda, una consulta mal indexada de la pantalla d'"històric de viatges" comença a trigar 40 segons. Els usuaris recarreguen. En dos minuts, les 20 connexions del pool estan ocupades per consultes lentes. A partir d'aquí:
- El procés Node.js continua viu: el PID 1 existeix, no ha llançat cap excepció no capturada.
- El servidor HTTP continua acceptant connexions: el sòcol està obert.
- Però tota petició que necessiti la base de dades es queda esperant un lloc lliure al pool i acaba fent timeout.
Des del punt de vista de Kubernetes, el pod està perfecte. Des del punt de vista del client que vol comprar un bitllet Bilbao–Santander, la plataforma està caiguda. I el pitjor: el Service api-reserves continua enviant trànsit a aquest pod, i als altres quatre que estan exactament igual.
flowchart LR
C[Client] --> S[Service api-reserves]
S --> P1[Pod 1<br/>procés viu<br/>pool exhaurit]
S --> P2[Pod 2<br/>procés viu<br/>pool exhaurit]
S --> P3[Pod 3<br/>procés viu<br/>pool exhaurit]
P1 -.timeout.-> DB[(postgres-reserves)]
P2 -.timeout.-> DB
P3 -.timeout.-> DB
Les sondes existeixen precisament per tancar aquesta escletxa. Donen a l'aplicació l'oportunitat de respondre per si mateixa a dues preguntes diferents:
- Estic tan trencada que el millor és que em reiniciïs? →
livenessProbe. - Puc atendre peticions ara mateix? →
readinessProbe.
Són preguntes diferents, amb conseqüències diferents, i confondre-les és l'origen de gairebé tots els desastres d'aquesta lliçó.
- Les tres sondes i què fa Kubernetes amb cada resultat
Kubernetes defineix tres sondes per contenidor. Totes són opcionals i totes es declaren dins de l'especificació del contenidor, no del pod.
livenessProbe — continues viu?
Comprova si el contenidor està en un estat del qual ja no es pot recuperar tot sol. Si falla el nombre de vegades configurat, el kubelet mata el contenidor i aplica la restartPolicy (normalment Always, així que el reinicia). El pod no es recrea: és el mateix pod, amb el mateix nom i la mateixa IP, amb el seu comptador RESTARTS incrementat.
Casos legítims: un interbloqueig (deadlock) del qual el procés no surt, un bucle infinit que no respon, una fuita de memòria coneguda sense arranjament a curt termini. Si la teva aplicació no té cap estat del qual només se surti reiniciant, és perfectament vàlid no posar livenessProbe.
readinessProbe — pots atendre trànsit?
Comprova si el contenidor pot servir peticions ara mateix. Si falla, Kubernetes no reinicia res: simplement marca el pod com a no llest (Ready=False) i el controlador d'endpoints retira la seva adreça IP de l'EndpointSlice del Service. Deixa d'arribar-li trànsit, però continua viu. Quan la sonda torna a passar, la IP s'hi reincorpora automàticament.
Aquest és el mecanisme correcte per al cas del pool exhaurit: el pod s'aparta un moment, deixa de rebre peticions que no pot atendre, drena les seves consultes lentes i torna.
startupProbe — has acabat d'arrencar?
És una sonda d'arrencada. Mentre s'està executant i no ha passat, les altres dues sondes queden deshabilitades. Quan passa per primera vegada, deixa d'executar-se per sempre i liveness i readiness entren en joc. Si no passa mai dins del seu pressupost de temps, el contenidor es mata i es reinicia.
Serveix per a aplicacions amb arrencades lentes o de durada molt variable, on posar un initialDelaySeconds enorme a la liveness faria que la detecció d'errors reals fos lentíssima la resta de la vida del contenidor.
Taula comparativa
| Aspecte | livenessProbe |
readinessProbe |
startupProbe |
|---|---|---|---|
| Pregunta que respon | T'he de reiniciar? | Et mano trànsit? | Has acabat d'arrencar? |
| Acció en fallar | Mata i reinicia el contenidor | Treu la IP dels Endpoints | Mata i reinicia el contenidor |
| Acció en passar | Res (continua vigilant) | Torna la IP als Endpoints | Es desactiva per sempre |
| Quan s'executa? | Tota la vida del contenidor | Tota la vida del contenidor | Només fins al primer èxit |
| Pot comprovar dependències externes? | Mai | Sí, i ho ha de fer | Només el mínim per arrencar |
| Afecta el Service? | No directament | Sí, directament | Indirectament (bloqueja readiness) |
| És obligatòria? | No | Pràcticament sí, si hi ha Service | Només si l'arrencada és lenta |
| Risc de mal ús | Molt alt (reinicis en cascada) | Baix | Baix |
Una regla que convé memoritzar: la liveness protegeix el contenidor d'ell mateix; la readiness protegeix els clients del contenidor.
- Els quatre manejadors:
httpGet, tcpSocket, exec i grpc
httpGet, tcpSocket, exec i grpcCada sonda utilitza exactament un d'aquests quatre mecanismes per fer la comprovació.
httpGet
El més habitual i el més recomanable per a serveis HTTP. El kubelet fa una petició GET a la IP del pod, al port i la ruta indicats.
livenessProbe:
httpGet:
path: /salut # ruta que atendrà api-reserves
port: 8080 # número o nom de port declarat a ports
scheme: HTTP # HTTP (per defecte) o HTTPS
httpHeaders:
- name: X-Origen-Sonda
value: kubeletPunts importants per a principiants:
- Es considera èxit qualsevol codi de resposta entre 200 i 399, tots dos inclosos. Un
204és èxit. Un301també, i això sorprèn: si la teva aplicació redirigeix/saluta/login, la sonda passarà encara que l'aplicació estigui trencada. Un400,404,500o503és error. - La petició la fa el kubelet del node, no un altre pod. Per això les NetworkPolicies de
rutas-norte-prono la bloquegen: no passa per la xarxa de pods normal. - El camp
portadmet el nom d'un port declarat aports, cosa que és molt més llegible i resistent a canvis. - Les capçaleres
httpHeadersserveixen per a dues coses molt pràctiques: identificar als logs de l'aplicació quines peticions vénen del kubelet (i així poder excloure-les de les mètriques de trànsit) i passar capçaleres que l'aplicació exigeixi, com unHostconcret. - El kubelet no envia galetes ni segueix cap autenticació: l'endpoint de salut ha de ser accessible sense credencials des de dins del pod.
tcpSocket
El kubelet intenta obrir una connexió TCP al port indicat. Si l'handshake es completa, és èxit; si la connexió es rebutja o expira, és error.
És l'opció per a serveis que no parlen HTTP, com redis-cache o el mateix PostgreSQL a baix nivell. La seva gran limitació: només comprova que hi ha alguna cosa escoltant. PostgreSQL pot tenir el port obert i estar rebutjant connexions per haver arribat a max_connections, o estar en mode recuperació. La sonda TCP passaria igualment.
exec
El kubelet executa una ordre dins del contenidor. Èxit si el codi de sortida és 0, error en qualsevol altre cas.
És el més flexible i el més car. Cada execució implica crear un procés nou dins del contenidor: consumeix CPU i memòria del pod (comptabilitzats contra els seus limits, cosa que pot provocar un OOMKilled si vas just) i afegeix càrrega al runtime del node. Amb periodSeconds: 5 en 60 pods estàs llançant 12 processos per segon al clúster només per preguntar com estan.
Consells:
- Fes servir
execnomés quan no hi hagi alternativa HTTP o TCP. - Puja el
periodSeconds(10-30 s) respecte al que faries servir ambhttpGet. - El binari ha d'existir a la imatge. És un error habitual escriure una sonda amb
curlen una imatgedistrolessque no en té: la sonda falla sempre i el contenidor entra enCrashLoopBackOff.
grpc
Des de Kubernetes 1.24 és una funcionalitat estable. El kubelet actua com a client del protocol estàndard de health checking de gRPC.
livenessProbe:
grpc:
port: 9000
service: reserves.Disponibilitat # opcional: nom del servei a consultarL'aplicació ha d'implementar el servei grpc.health.v1.Health. És èxit si respon SERVING. Evita haver d'instal·lar grpc_health_probe com a binari a la imatge, que era la solució anterior amb exec.
Comparativa de manejadors
| Manejador | Quan fer-lo servir | Cost | Risc principal |
|---|---|---|---|
httpGet |
Serveis HTTP/REST (api-reserves, botiga-web) |
Molt baix | Els 3xx compten com a èxit |
tcpSocket |
Serveis TCP sense HTTP (redis-cache) |
Molt baix | Superficial: només mira el sòcol |
exec |
Comprovacions que exigeixen lògica local (pg_isready) |
Alt | Consum de recursos i binaris absents |
grpc |
Serveis gRPC | Baix | Requereix implementar el servei de salut |
- Els paràmetres temporals i el càlcul exacte del temps de detecció
Les cinc variables temporals són idèntiques per a les tres sondes. Entendre-les de debò significa poder respondre a la pregunta "quant triga Kubernetes a adonar-se que aquest contenidor és mort?" amb un número, no amb una intuïció.
| Paràmetre | Per defecte | Mínim | Significat |
|---|---|---|---|
initialDelaySeconds |
0 | 0 | Segons d'espera des que arrenca el contenidor fins a la primera comprovació |
periodSeconds |
10 | 1 | Cada quants segons es repeteix la comprovació |
timeoutSeconds |
1 | 1 | Segons que s'espera la resposta abans de comptar-la com a error |
successThreshold |
1 | 1 | Èxits consecutius per passar d'error a èxit |
failureThreshold |
3 | 1 | Errors consecutius per donar la sonda per fallida |
Dos matisos que s'obliden sempre:
successThresholdha de valer 1 obligatòriament alivenessProbeistartupProbe. Només lareadinessProbeadmet valors més grans. Té sentit: no pots "reiniciar a mitges".timeoutSeconds: 1(el valor per defecte) és perillosament baix per a una aplicació real sota càrrega. Un endpoint de salut que normalment triga 20 ms pot trigar 1,5 s quan el node està saturat, i llavors la sonda falla per timeout encara que l'aplicació estigui bé.
El càlcul
El temps màxim des que un contenidor deixa de respondre fins que el kubelet el mata és:
T_deteccio = initialDelaySeconds (només la primera vegada)
+ (failureThreshold - 1) * periodSeconds
+ timeoutSecondsMés un marge de fins a periodSeconds perquè l'error pot passar just després d'una comprovació correcta. A la pràctica es fa servir la fórmula pessimista:
Exemple amb la configuració que farem servir a api-reserves:
livenessProbe:
httpGet:
path: /salut
port: http
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3És a dir: si api-reserves es penja, passaran fins a 33 segons fins que el kubelet el reiniciï, i després caldrà sumar el temps d'arrencada del contenidor. Si això és inacceptable per al negoci, baixes periodSeconds a 5 i failureThreshold a 3 → 18 segons. Però com més agressiva la sonda, més gran el risc de falsos positius, i aquest risc és el tema de l'apartat següent.
I per a la readiness, el mateix càlcul determina quant de temps continuarà arribant trànsit a un pod que ja no el pot atendre:
readinessProbe:
httpGet:
path: /preparat
port: http
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 2Regla pràctica: la readiness ha de ser més ràpida i més sensible que la liveness. Apartar un pod és una acció barata i reversible; reiniciar-lo, no.
- L'error més car del mòdul: la liveness en cascada
Aquest és l'apartat que cal llegir dues vegades. És l'error que més vegades ha tombat plataformes senceres de producció, i sempre pel mateix motiu.
L'escenari
Un enginyer de Rutas Norte, amb la millor intenció, configura així la liveness d'api-reserves:
# INCORRECTE — no ho copieu
livenessProbe:
httpGet:
path: /salut/complet # comprova API + PostgreSQL + Redis + passarel·la de pagaments
port: 8080
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 2L'endpoint /salut/complet fa un SELECT 1 contra postgres-reserves, un PING a redis-cache i una crida a pagos.proveedorexterno.example. Sembla completíssim. És una bomba.
Arriba un pont de maig. El trànsit es multiplica per sis. postgres-reserves comença a respondre en 1,2 s en lloc de en 5 ms. Aleshores:
- La sonda de salut fa
timeout(1 s) en els deu pods d'api-reservesalhora, perquè tots depenen de la mateixa base de dades. - Als 10 segons (
2 * 5), el kubelet mata els deu contenidors simultàniament. - Els deu arrenquen de nou. En arrencar, cadascun obre el seu pool de 20 connexions contra PostgreSQL: 200 connexions noves de cop contra una base de dades que ja anava ofegada.
- PostgreSQL se satura encara més. Les sondes tornen a fallar. Tornem al pas 2.
- Kubernetes aplica backoff exponencial als reinicis, així que els pods entren en
CrashLoopBackOff. La plataforma passa de lenta a totalment caiguda.
flowchart TD
A[Pic de trànsit] --> B[postgres-reserves respon lent]
B --> C[La liveness comprova PostgreSQL i fa timeout]
C --> D[kubelet mata els 10 pods d'api-reserves]
D --> E[10 pods arrenquen alhora i obren 200 connexions]
E --> B
D --> F[CrashLoopBackOff: caiguda total]
Fixa't en l'essencial: la base de dades estava lenta, no caiguda. Sense sondes, la plataforma hauria anat lenta durant vint minuts i s'hauria recuperat sola. Amb aquella liveness, va caure del tot. La sonda va amplificar l'error en lloc de mitigar-lo.
Les dues regles d'or
Regla 1: la
livenessProbeMAI ha de comprovar dependències externes. Ni bases de dades, ni cachés, ni APIs de tercers, ni altres microserveis. Només ha de respondre a "aquest procés, aïllat, continua sent capaç de processar una petició?".
Regla 2: la
readinessProbeSÍ ha de comprovar les dependències que necessita per servir. Siapi-reservesno pot parlar amb PostgreSQL, no pot servir reserves, i el correcte és que surti dels Endpoints fins que pugui.
I què passa si la readiness falla als deu pods alhora? Que el Service es queda sense Endpoints i l'Ingress retorna 503. És dolent, però és honest i reversible: tan bon punt PostgreSQL es recupera, els deu pods tornen a estar llestos en cinc segons, sense arrencades en fred, sense allau de connexions, sense CrashLoopBackOff. La diferència entre una degradació de servei i un desastre.
Per això api-reserves tindrà dos endpoints diferents:
| Endpoint | Qui l'utilitza | Què comprova | Què NO comprova |
|---|---|---|---|
/salut |
livenessProbe |
El bucle d'esdeveniments de Node respon; no hi ha deadlock; hi ha memòria per respondre | PostgreSQL, Redis, passarel·la de pagaments |
/preparat |
readinessProbe |
Tot l'anterior més una connexió lliure al pool de PostgreSQL i PING a redis-cache |
La passarel·la de pagaments externa (vegeu la nota) |
Nota sobre la passarel·la de pagaments: pagos.proveedorexterno.example és un tercer fora del nostre control. Si el fiquem a la readiness, una caiguda del proveïdor deixa tota la plataforma sense Endpoints, inclosa la consulta d'horaris, que no necessita pagaments. La decisió correcta és no incloure'l en cap sonda i gestionar-lo amb un circuit breaker dins de l'aplicació que retorni un error concret només al flux de pagament.
startupProbe: la solució a les arrencades lentes de postgres-reserves
startupProbe: la solució a les arrencades lentes de postgres-reservespostgres-reserves, després del creixement de l'últim any, triga entre 20 i 180 segons a acceptar connexions quan arrenca: si el tancament anterior va ser brut, ha de reproduir el WAL, i aquest temps depèn del volum pendent.
Sense startupProbe, tenim dues males opcions:
- Opció A:
initialDelaySeconds: 200a la liveness. Cobreix el pitjor cas, però durant els 200 primers segons de cada reinici ningú vigila el contenidor. I pitjor: no pots baixar el període de detecció després. - Opció B:
failureThreshold: 40ambperiodSeconds: 5. Cobreix l'arrencada, però també significa que, ja en règim, una penjada trigarà40 * 5 = 200 segonsa detectar-se.
La startupProbe separa els dos pressupostos de temps: un de generós per arrencar, un altre d'estricte per a la vida en règim.
# Fragment del contenidor postgres del StatefulSet postgres-reserves
startupProbe:
exec:
command: ["/bin/sh", "-c", "pg_isready -U rutasnorte -h 127.0.0.1"]
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30 # 30 * 10 = 300 s de pressupost d'arrencada
livenessProbe:
exec:
command: ["/bin/sh", "-c", "pg_isready -U rutasnorte -h 127.0.0.1"]
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3 # ja en règim: 35 s per detectar una penjada
readinessProbe:
exec:
command: ["/bin/sh", "-c", "pg_isready -U rutasnorte -h 127.0.0.1 && psql -U rutasnorte -d reserves -c 'SELECT 1' -tA"]
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 2Com es comporta això en el temps:
sequenceDiagram
participant K as kubelet
participant C as contenidor postgres
K->>C: t=10s startupProbe → error (recuperant WAL)
K->>C: t=20s startupProbe → error
Note over K: liveness i readiness NO s'executen
K->>C: t=90s startupProbe → ÈXIT
Note over K: startupProbe es desactiva per sempre
K->>C: t=100s livenessProbe → èxit
K->>C: t=100s readinessProbe → èxit
Note over K: el pod entra als Endpoints del Service headless
Si als 300 segons la startupProbe continua fallant, el contenidor es mata i es reinicia. És el comportament correcte: alguna cosa va realment malament.
Detall important per a l'exec: pg_isready i psql existeixen a la imatge postgres:16.4, i ens connectem a 127.0.0.1 (dins del mateix pod), no al Service. Comprovar el Service des de la sonda d'un pod seria comprovar una dependència externa, amb tots els problemes de l'apartat 5.
- Sondes i desplegaments sense tall: tancant el deute de 02-04
A 02-04 vam veure RollingUpdate amb maxSurge i maxUnavailable, i vam deixar escrit que allò encara no era un desplegament sense tall. Ara s'entén per què.
El controlador de Deployment considera que un pod nou "està bé" quan el pod és Ready. Sense readinessProbe, un pod es considera Ready tan bon punt els seus contenidors arrenquen. Per a api-reserves, això passa uns 400 ms després de llançar node, molt abans que Express escolti al 8080 i que el pool de connexions estigui obert.
Seqüència del desastre silenciós:
kubectl applyamb la nova imatge.- Es crea el pod nou. Als 400 ms és
Runningi, sense readiness,Ready. - El controlador d'endpoints afegeix la seva IP a l'EndpointSlice: comença a rebre trànsit real.
- El Deployment, en veure un pod nou llest, acaba un pod antic.
- Durant els següents 3-8 segons, el pod nou retorna
ECONNREFUSEDa tot el trànsit que li arriba. - Es repeteix pod a pod. El resultat: uns segons d'errors per cada rèplica substituïda, invisibles a
kubectl get pods, molt visibles a la taxa d'errors.
Amb readinessProbe, el pas 2 canvia: el pod és Running però Ready=False, no entra als Endpoints i el Deployment no avança fins que passi la sonda. Aquest és, literalment, el mecanisme del desplegament sense tall.
minReadySeconds
Hi ha un cas residual: aplicacions que passen la readiness i cauen tres segons després (per exemple, en rebre la primera petició real que toca un codi nou defectuós). minReadySeconds obliga el Deployment a esperar N segons amb el pod contínuament llest abans de donar-lo per bo i continuar substituint pods.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-reserves
namespace: rutas-norte-pro
labels:
app: api-reserves
app.kubernetes.io/part-of: rutas-norte
entorn: pro
spec:
replicas: 6
minReadySeconds: 15 # 15 s llest de manera continuada abans d'avançar
progressDeadlineSeconds: 300 # si en 5 min no progressa, es marca com a fallit
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 0 # mai menys de 6 pods servint
selector:
matchLabels:
app: api-reserves
entorn: pro
template:
metadata:
labels:
app: api-reserves
app.kubernetes.io/part-of: rutas-norte
entorn: pro
spec:
containers:
- name: api
image: registry.rutasnorte.example/api-reserves:2.8.1
ports:
- name: http
containerPort: 8080Amb maxUnavailable: 0 + readinessProbe + minReadySeconds: 15, un desplegament d'api-reserves és genuïnament sense tall. El preu: triga més. Un desplegament de 6 rèpliques passa de 40 segons a uns 3 minuts. És un preu raonable.
Un avís sobre progressDeadlineSeconds: si la readiness no passa mai (per exemple, perquè la nova versió té un error de configuració), el Deployment es queda encallat però els pods vells continuen servint. Als 300 segons el Deployment es marca com a ProgressDeadlineExceeded, cosa que permet detectar-ho automàticament. No fa rollback tot sol: això ho decideixes tu amb kubectl rollout undo.
- El tancament net:
terminationGracePeriodSeconds, preStop i els 502
terminationGracePeriodSeconds, preStop i els 502Hem resolt l'entrada de pods nous. Queda la sortida dels vells, que és on neixen els 502 que Rutas Norte veu a cada desplegament.
La cursa
Quan Kubernetes decideix acabar un pod, passen dues coses en paral·lel, sense coordinació entre elles:
sequenceDiagram
participant API as API Server
participant EPC as Controlador d'Endpoints
participant KP as kube-proxy / Ingress
participant KL as kubelet
participant P as Pod api-reserves
API->>EPC: pod marcat per esborrar
API->>KL: pod marcat per esborrar
par Camí A (xarxa)
EPC->>EPC: treu la IP de l'EndpointSlice
EPC->>KP: propaga la regla actualitzada
KP->>KP: reprograma iptables/IPVS (100-2000 ms)
and Camí B (procés)
KL->>P: SIGTERM immediat
end
Note over P,KP: el procés mor ABANS que deixin d'enviar-li trànsit → 502
El camí B (matar el procés) és gairebé instantani. El camí A (propagar la retirada de l'endpoint a tots els nodes i als controladors d'Ingress) triga des d'uns centenars de mil·lisegons fins a un parell de segons en un clúster amb càrrega. En aquesta finestra, el balancejador continua enviant peticions a un procés que ja s'està tancant. Cadascuna d'aquestes peticions és un 502 per a un client de Rutas Norte.
La solució: preStop
El hook preStop s'executa abans del SIGTERM i el kubelet espera que acabi abans d'enviar-lo. Inserir-hi una pausa dóna temps al camí A.
Sembla un truc brut, i en certa manera ho és, però és la solució recomanada per la mateixa documentació de Kubernetes i la que fa servir pràcticament tothom en producció. Durant aquests 10 segons:
- El contenidor continua atenent peticions amb normalitat (ningú li ha dit que pari).
- El controlador d'endpoints ja l'ha retirat, així que deixa d'arribar-li trànsit nou.
- Les peticions en vol acaben tranquil·lament.
Si la teva aplicació té un endpoint que li fa fallar la readiness voluntàriament, una variant més elegant és cridar-lo i després esperar. Però el sleep funciona i no requereix tocar l'aplicació.
El pressupost complet de terminació
spec:
terminationGracePeriodSeconds: 45
containers:
- name: api
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]Cronologia real:
| Moment | Què passa |
|---|---|
| t=0 s | El pod passa a Terminating. Arrenca el preStop i el rellotge de terminationGracePeriodSeconds |
| t=0 a 2 s | L'endpoint es retira de tots els nodes i de l'Ingress |
| t=0 a 10 s | preStop dormint. El contenidor serveix les peticions en vol |
| t=10 s | Acaba el preStop. El kubelet envia SIGTERM |
| t=10 a 45 s | L'aplicació tanca el servidor HTTP, drena el pool de PostgreSQL, fa commit del que queda pendent i surt |
| t=45 s | Si el procés continua viu, el kubelet envia SIGKILL. Sense pietat |
Punt crític que s'oblida sempre: el temps del preStop es descompta del període de gràcia, no s'hi suma. Amb terminationGracePeriodSeconds: 45 i un preStop de 10 s, l'aplicació només té 35 segons per tancar després del SIGTERM. Ajusta el número tenint-ho en compte.
I com vam veure a 02-01, si el teu procés arrenca via shell (CMD npm start), el SIGTERM pot arribar-li al shell i no a Node. Fes servir la forma exec (CMD ["node", "server.js"]) o shareProcessNamespace/tini per assegurar-te que el senyal arriba a qui ha d'arribar.
Per a worker-notificacions, que processa correus de confirmació, el període de gràcia ha de cobrir l'enviament en curs més llarg: fixem terminationGracePeriodSeconds: 90 i l'aplicació deixa d'agafar missatges nous de la cua en rebre el SIGTERM.
- Disseny de les sondes de cada component de Rutas Norte
Apliquem tot l'anterior component a component. La taula de disseny primer, els manifests després.
| Component | Liveness | Readiness | Startup | Notes |
|---|---|---|---|---|
botiga-web (nginx) |
httpGet / port 80 |
httpGet / port 80 |
No | Estàtic i ràpid; que siguin iguals és acceptable |
api-reserves |
httpGet /salut |
httpGet /preparat |
httpGet /salut, 60 s |
El cas de referència de la lliçó |
postgres-reserves |
exec pg_isready local |
exec pg_isready + SELECT 1 |
exec pg_isready, 300 s |
Arrencada variable pel WAL |
redis-cache |
exec redis-cli PING |
exec redis-cli PING |
No | Arrencada rapidíssima |
worker-notificacions |
httpGet /salut port 8081 |
Cap | No | No té Service: la readiness no aporta res |
informes-ocupacio (CronJob) |
Cap | Cap | No | Job de vida curta: l'èxit és el codi de sortida |
Dues decisions que mereixen explicació:
worker-notificacionssense readiness: la readiness només té sentit si alguna cosa consulta els Endpoints. Aquest worker no està darrere de cap Service; consumeix d'una cua. Posar-hi readiness no faria res. Sí que té liveness, amb un petit servidor HTTP intern que respon200mentre el bucle de consum estigui actiu.- CronJob sense sondes: un pod que viu 4 minuts i l'èxit del qual es mesura pel seu codi de sortida no guanya res amb sondes. En el seu lloc, a 07-04 alertarem sobre
kube_job_status_failed.
Manifest complet d'api-reserves
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-reserves
namespace: rutas-norte-pro
labels:
app: api-reserves
app.kubernetes.io/part-of: rutas-norte
entorn: pro
spec:
replicas: 6
minReadySeconds: 15
progressDeadlineSeconds: 300
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 0
selector:
matchLabels:
app: api-reserves
entorn: pro
template:
metadata:
labels:
app: api-reserves
app.kubernetes.io/part-of: rutas-norte
entorn: pro
spec:
terminationGracePeriodSeconds: 45
containers:
- name: api
image: registry.rutasnorte.example/api-reserves:2.8.1
ports:
- name: http
containerPort: 8080
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
# ---- ARRENCADA: pressupost generós, sense vigilància encara ----
startupProbe:
httpGet:
path: /salut
port: http
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 12 # 12 * 5 = 60 s per arrencar
# ---- VIVACITAT: només el procés, MAI dependències externes ----
livenessProbe:
httpGet:
path: /salut
port: http
httpHeaders:
- name: X-Origen-Sonda
value: kubelet-liveness
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3 # pitjor cas: 33 s fins al reinici
# ---- PREPARACIÓ: sí que comprova PostgreSQL i Redis ----
readinessProbe:
httpGet:
path: /preparat
port: http
httpHeaders:
- name: X-Origen-Sonda
value: kubelet-readiness
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 2 # pitjor cas: 12 s fora d'Endpoints
successThreshold: 1
# ---- TANCAMENT NET: evitar els 502 del desplegament ----
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]Què ha de fer cada endpoint per dins
Pseudocodi del /salut (liveness). Barat, local, sense xarxa sortint:
// GET /salut -> utilitzat per livenessProbe
// Respon 200 si el procés pot processar peticions. RES de dependències.
app.get('/salut', (req, res) => {
const retardBucleMs = mesurarRetardDelBucleDEsdeveniments(); // p. ex. amb perf_hooks
if (retardBucleMs > 5000) {
// El bucle d'esdeveniments porta 5 s bloquejat: el procés no es recupera sol
return res.status(503).json({ estat: 'bloquejat', retardBucleMs });
}
return res.status(200).json({ estat: 'viu', versio: process.env.APP_VERSION });
});Pseudocodi del /preparat (readiness). Comprova dependències, amb timeouts curts:
// GET /preparat -> utilitzat per readinessProbe
// Respon 200 només si podem servir una reserva de debò ARA.
app.get('/preparat', async (req, res) => {
const comprovacions = {};
try {
// 1. Queda alguna connexió lliure al pool? (el cas de l'apartat 1)
comprovacions.poolLliure = poolPg.idleCount > 0 || poolPg.totalCount < poolPg.options.max;
if (!comprovacions.poolLliure) throw new Error('pool de connexions exhaurit');
// 2. PostgreSQL respon en menys d'1 s?
await poolPg.query({ text: 'SELECT 1', timeout: 1000 });
comprovacions.postgres = 'ok';
// 3. Redis respon? Degradació acceptable: sense cau servim igual, més lent
try {
await redis.ping();
comprovacions.redis = 'ok';
} catch (e) {
comprovacions.redis = 'degradat'; // NO impedeix estar preparat
}
// ATENCIÓ: no comprovem pagos.proveedorexterno.example expressament
return res.status(200).json({ estat: 'preparat', comprovacions });
} catch (err) {
return res.status(503).json({ estat: 'no-preparat', motiu: err.message, comprovacions });
}
});postgres-reserves i redis-cache
# Fragment del StatefulSet postgres-reserves (contenidor principal)
containers:
- name: postgres
image: postgres:16.4
ports:
- name: pg
containerPort: 5432
startupProbe:
exec:
command: ["/bin/sh", "-c", "pg_isready -U rutasnorte -h 127.0.0.1"]
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30 # 300 s: cobreix la recuperació del WAL
livenessProbe:
exec:
command: ["/bin/sh", "-c", "pg_isready -U rutasnorte -h 127.0.0.1"]
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U rutasnorte -h 127.0.0.1 && psql -U rutasnorte -d reserves -tAc 'SELECT 1'
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 2# Fragment del StatefulSet redis-cache
containers:
- name: redis
image: redis:7.4-alpine
ports:
- name: redis
containerPort: 6379
livenessProbe:
exec:
command: ["redis-cli", "ping"]
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
exec:
command: ["redis-cli", "ping"]
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 2Verificació al clúster
# Estat de preparació de tots els pods del namespace
kubectl -n rutas-norte-pro get pods -l app.kubernetes.io/part-of=rutas-norte
# Veure els esdeveniments de sondes fallides d'un pod concret
kubectl -n rutas-norte-pro describe pod api-reserves-7d9f8c4b5-x2klm | grep -A 20 Events
# Veure la condició Ready i el seu motiu exacte
kubectl -n rutas-norte-pro get pod api-reserves-7d9f8c4b5-x2klm \
-o jsonpath='{.status.conditions[?(@.type=="Ready")]}' | jq
# Comprovar quines IPs són realment als Endpoints (la veritat del Service)
kubectl -n rutas-norte-pro get endpointslices -l kubernetes.io/service-name=api-reserves -o yamlSortida típica d'un pod la readiness del qual falla:
Para atenció: STATUS és Running i RESTARTS és 0. Tot "sembla" bé. L'única senyal és el 0/1 de la columna READY. I als esdeveniments:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Unhealthy 2m (x24 over 4m) kubelet Readiness probe failed: HTTP probe failed with statuscode: 503Aquesta és la traça que cal buscar. A 07-06 sistematitzarem aquesta manera de llegir esdeveniments.
Errors Comuns i Consells
1. Posar la mateixa sonda a liveness i readiness comprovant dependències. És l'error de l'apartat 5 disfressat. Si copies el mateix httpGet /salut/complet a totes dues, has creat el bucle de reinicis en cascada. Endpoints diferents, sempre.
2. timeoutSeconds: 1 (el valor per defecte). Sota càrrega, un endpoint sa triga més d'un segon amb facilitat. Puja a 2-3 s a readiness i 3-5 s a liveness. Aquest paràmetre per defecte ha causat més falsos positius que cap altre.
3. Oblidar la startupProbe en aplicacions lentes. El símptoma clàssic: CrashLoopBackOff en un contenidor que "en local arrenca bé". El que passa és que la liveness el mata als 30 segons i mai no acaba d'arrencar. Si veus reinicis cíclics sense logs d'error de l'aplicació, sospita d'això el primer.
4. Sonda exec amb un binari que no existeix. curl, wget o nc no són a les imatges distroless ni a moltes alpine minimalistes. Comprova amb kubectl exec -it <pod> -- which curl abans d'escriure la sonda. L'esdeveniment delator és Liveness probe errored: exec: "curl": executable file not found in $PATH.
5. Endpoint de salut que exigeix autenticació. El kubelet no envia credencials. Un /salut protegit per JWT retorna 401 i la sonda falla sempre. Deixa l'endpoint de salut obert, sense dades sensibles a la resposta, i protegeix-lo per xarxa si cal.
6. Confiar en els codis 3xx. Recorda: 200-399 és èxit. Un /salut que redirigeix a / continuarà "passant" encara que l'aplicació estigui completament trencada. Retorna 200 explícit.
7. readinessProbe sense preStop. Tens desplegaments sense tall a l'entrada però continues generant 502 a la sortida. Els dos mecanismes són complementaris i calen tots dos.
8. Sondes massa cares. Un /preparat que fa SELECT count(*) FROM reserves està llançant una consulta pesada cada 5 segons per cada rèplica. Amb 6 rèpliques són 72 consultes per minut només per preguntar com estem. Les comprovacions han de ser trivials: SELECT 1 i poca cosa més.
9. Fer servir sondes per al que serveix un PodDisruptionBudget. Les sondes no protegeixen d'un drenatge de node ni d'una actualització del clúster. Això és 09-05.
10. Un preStop més llarg que el període de gràcia. Si poses preStop: sleep 60 amb terminationGracePeriodSeconds: 30, als 30 segons arriba el SIGKILL i l'aplicació mai no rep el SIGTERM: no tanca res netament. El preStop sempre ha de ser bastant menor que el període de gràcia.
Exercicis
Exercici 1 — Calcular i ajustar el pressupost de detecció
Un company ha configurat així la liveness de worker-notificacions:
livenessProbe:
httpGet:
path: /salut
port: 8081
initialDelaySeconds: 20
periodSeconds: 30
timeoutSeconds: 1
failureThreshold: 5Respon:
- Quant triga, en el pitjor cas, a detectar-se que el worker s'ha penjat?
- El negoci exigeix que un worker penjat es reiniciï en menys de 60 segons. Proposa una configuració que ho compleixi sense fer-la propensa a falsos positius.
- El worker triga 8 segons a arrencar i connectar-se a la cua. És correcte l'
initialDelaySeconds: 20? Quina alternativa millor existeix?
Exercici 2 — Detectar i arreglar una liveness perillosa
Aquest és el manifest real de botiga-web a rutas-norte-pre. Conté tres problemes relacionats amb les sondes i el tancament. Identifica'ls i escriu el manifest corregit.
apiVersion: apps/v1
kind: Deployment
metadata:
name: botiga-web
namespace: rutas-norte-pre
labels:
app: botiga-web
entorn: pre
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: botiga-web
entorn: pre
template:
metadata:
labels:
app: botiga-web
entorn: pre
spec:
containers:
- name: nginx
image: registry.rutasnorte.example/botiga-web:5.2.0
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /api/estat-complet # proxy a api-reserves i a PostgreSQL
port: 80
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 2Exercici 3 — Dissenyar les sondes d'un component nou
Rutas Norte afegeix un component: cercador-rutes, un servei Java (Spring Boot) que manté en memòria un índex de totes les rutes i horaris. Dades:
- En arrencar carrega l'índex complet des de
postgres-reserves: entre 45 i 150 segons segons el volum. - Un cop carregat l'índex, no torna a tocar PostgreSQL: ho serveix tot des de memòria.
- Exposa
/actuator/health/livenessi/actuator/health/readiness(Spring Boot Actuator). - Està darrere d'un Service
cercador-rutesque consultabotiga-web. - Ocasionalment pateix pauses llargues del recol·lector d'escombraries (fins a 4 segons).
Escriu el bloc de sondes complet i justifica cada valor.
Solucions
Solució 1
1. Temps de detecció.
Més de dos minuts i mig amb el worker penjat i sense enviar ni un correu de confirmació. Molt per sobre del requisit de 60 segons.
2. Configuració proposada.
livenessProbe:
httpGet:
path: /salut
port: 8081
periodSeconds: 10
timeoutSeconds: 3 # 1 s era massa ajustat sota càrrega
failureThreshold: 4S'ha baixat el període de 30 a 10 s (detecció més ràpida) i pujat el timeoutSeconds d'1 a 3 s (menys falsos positius). El failureThreshold: 4 dóna marge perquè un pic puntual no dispari un reinici: calen quatre errors consecutius, és a dir 40 segons de problema sostingut.
3. L'initialDelaySeconds.
És una solució grollera. Amb 20 s fixos: si un dia l'arrencada triga 25 s per lentitud del node, la liveness comença a fallar durant l'arrencada i el pod pot entrar en CrashLoopBackOff. I si arrenca en 8 s, hem perdut 12 s de vigilància.
L'alternativa correcta és una startupProbe que separi els pressupostos:
startupProbe:
httpGet:
path: /salut
port: 8081
periodSeconds: 3
timeoutSeconds: 2
failureThreshold: 15 # 45 s de marge d'arrencada, de sobres per a 8 s
livenessProbe:
httpGet:
path: /salut
port: 8081
periodSeconds: 10 # sense initialDelaySeconds: ja no cal
timeoutSeconds: 3
failureThreshold: 4Avantatge afegit: el startupProbe amb periodSeconds: 3 detecta l'arrencada acabada tan bon punt passa, així que un pod que arrenca en 8 s està llest en ~9 s, no en 20.
Solució 2
Els tres problemes:
- La liveness comprova dependències externes.
/api/estat-completés un proxy cap aapi-reservesi d'allà a PostgreSQL. Si l'API o la base de dades van lentes, es reinicien els tres pods debotiga-webalhora, deixant el web públic completament caigut encara que nginx estigués perfectament. És exactament l'escenari de l'apartat 5. - No hi ha
readinessProbe. Combinat ambmaxUnavailable: 1, cada desplegament envia trànsit a pods d'nginx que encara no han acabat d'arrencar → errors durant cada actualització. - No hi ha
preStopni període de gràcia ajustat. Els pods que es retiren moren abans que es propagui la retirada de l'endpoint →502a cada desplegament. Afegit:timeoutSeconds: 1és massa ajustat.
Manifest corregit:
apiVersion: apps/v1
kind: Deployment
metadata:
name: botiga-web
namespace: rutas-norte-pre
labels:
app: botiga-web
app.kubernetes.io/part-of: rutas-norte
entorn: pre
spec:
replicas: 3
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # mai baixar de 3 pods servint
selector:
matchLabels:
app: botiga-web
entorn: pre
template:
metadata:
labels:
app: botiga-web
app.kubernetes.io/part-of: rutas-norte
entorn: pre
spec:
terminationGracePeriodSeconds: 30
containers:
- name: nginx
image: registry.rutasnorte.example/botiga-web:5.2.0
ports:
- name: http
containerPort: 80
# Liveness: NOMÉS nginx, sense sortir del pod
livenessProbe:
httpGet:
path: /nginx-salut # ubicació estàtica que retorna 200 des d'nginx
port: http
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
# Readiness: el web estàtic se serveix sense dependre de l'API
readinessProbe:
httpGet:
path: /nginx-salut
port: http
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 2
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"]Amb la configuració d'nginx corresponent:
L'access_log off; evita que les sondes (una cada 5 s per pod, 3 pods → més de 50.000 línies al dia) inundin els logs que centralitzarem a 07-05.
Solució 3
# ---- ARRENCADA: pressupost molt generós, cobreix el pitjor cas de 150 s ----
startupProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 24 # 24 * 10 = 240 s > 150 s del pitjor cas
# ---- VIVACITAT: la JVM respon; RES de PostgreSQL ----
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 15
timeoutSeconds: 6 # > 4 s de la pausa màxima del GC
failureThreshold: 3 # pitjor cas: 51 s
# ---- PREPARACIÓ: està l'índex carregat i serveix consultes? ----
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 10
timeoutSeconds: 6
failureThreshold: 3
successThreshold: 1Justificació de cada decisió:
startupProbeamb 240 s de pressupost. L'arrencada va de 45 a 150 s. Donar un 60 % de marge sobre el pitjor cas conegut evita reinicis espuris el dia que PostgreSQL vagi lent durant la càrrega. Sense aquesta sonda, la liveness mataria el pod als ~50 s i entraria enCrashLoopBackOffetern: no arribaria mai a carregar l'índex.timeoutSeconds: 6a liveness i readiness. Les pauses del recol·lector d'escombraries arriben a 4 segons. Si el timeout fos de 3 s, cada pausa llarga comptaria com a error. Amb 6 s, una pausa de GC no dispara res. Aquest és un ajust específic de la JVM que cal fer conscientment.periodSeconds: 15a liveness. El component és estable; no necessitem detecció subsegon. Un període alt redueix la pressió de sondes sobre l'aplicació.- Liveness contra
/actuator/health/livenessi no contra l'endpoint general. Spring Boot separa per defecte els grupsliveness(estat de la JVM) ireadiness(dependències llestes). Fer servir/actuator/healtha seques inclouria els indicadors de PostgreSQL a la liveness: l'error de l'apartat 5. - Readiness que sí que comprova l'índex. Encara que
cercador-rutesno toqui PostgreSQL en règim, sí que necessita l'índex carregat. Que la readiness falli mentre l'índex no estigui llest és correcte: el pod no ha de rebre cerques que no pot resoldre. successThreshold: 1. Tornar ràpid al servei tan bon punt es recupera.
Detall pràctic: a Spring Boot cal activar els grups de sondes amb management.endpoint.health.probes.enabled=true (s'activa sol si detecta que corre a Kubernetes).
Conclusió
Les sondes són el primer pas cap a una plataforma observable, i també el primer cap a una plataforma fiable. En aquesta lliçó hem vist que:
- Un procés viu no és una aplicació sana, i el cas del pool de connexions exhaurit d'
api-reservesho demostra sense ambigüitat. livenessProbereinicia,readinessProberetira del Service,startupProberetarda les altres dues. Tres preguntes diferents amb tres conseqüències diferents.- Els quatre manejadors (
httpGet,tcpSocket,exec,grpc) tenen costos i precisions molt diferents;execés el més flexible i el més car. - El temps de detecció es calcula, no s'intueix:
failureThreshold * periodSeconds + timeoutSeconds. - La regla que evita la caiguda més cara del mòdul: la liveness mai no comprova dependències externes; la readiness sí que ho ha de fer.
- Les sondes tanquen el deute de 02-04: juntament amb
maxUnavailable: 0,minReadySecondsi unpreStopque guanya la cursa contra elSIGTERM, tenim per fi desplegaments genuïnament sense tall i sense els502que vèiem a cada actualització.
Rutas Norte ja sap si cada component està sa. El que encara no sap és quant consumeix. A 03-04 vam fixar les requests i els limits de cada component pràcticament a ull, i vam deixar escrit que els calibraríem observant el consum real. Aquest moment ha arribat: a la propera lliçó instal·larem metrics-server, la primera font de dades de consum del clúster, i amb kubectl top compararem el que demanem amb el que de debò gastem.
Curs de Kubernetes
Mòdul 1: Introducció a Kubernetes
- Què és Kubernetes?
- Arquitectura de Kubernetes
- Conceptes i Terminologia Clau
- Configuració d'un Clúster de Kubernetes
- La CLI de Kubernetes: kubectl
- Objectes, Manifests YAML i el Model Declaratiu
- El Projecte del Curs: la Plataforma Rutas Norte
Mòdul 2: Components Principals de Kubernetes
- Pods
- ReplicaSets
- Deployments
- Actualitzacions, Rollbacks i Estratègies de Desplegament
- Serveis
- Namespaces
- Etiquetes, Selectors i Anotacions
Mòdul 3: Gestió de Configuració i Secrets
- ConfigMaps
- Secrets
- Variables d'Entorn
- Quotes i Límits de Recursos
- LimitRanges i Classes de Qualitat de Servei (QoS)
- ServiceAccounts i Accés a l'API des dels Pods
Mòdul 4: Xarxes a Kubernetes
- Xarxes de Clúster
- Tipus de Serveis
- DNS Intern i Descobriment de Serveis
- Controladors d'Ingress
- TLS i Gestió de Certificats amb cert-manager
- Polítiques de Xarxa
Mòdul 5: Emmagatzematge a Kubernetes
- Volums
- Volums Persistents
- Reclamacions de Volums Persistents
- Classes d'Emmagatzematge
- Aprovisionament Dinàmic, Expansió i Snapshots
- Còpies de Seguretat i Restauració de Dades
Mòdul 6: Conceptes Avançats de Kubernetes
- StatefulSets
- DaemonSets
- Treballs i CronJobs
- Init Containers, Sidecars i Patrons Multicontenidor
- Planificació: Afinitat, Taints i Toleracions
- Definicions de Recursos Personalitzats (CRDs)
- Operadors i el Patró Controlador
Mòdul 7: Monitoratge i Registre
- Verificacions de Salut i Sondes
- Servidor de Mètriques i kubectl top
- Monitoratge amb Prometheus
- Visualització i Alertes amb Grafana i Alertmanager
- Registre Centralitzat amb Elasticsearch, Fluentd i Kibana (EFK)
- Depuració d'Aplicacions i Esdeveniments del Clúster
Mòdul 8: Seguretat a Kubernetes
- Control d'Accés Basat en Rols (RBAC)
- Contextos de Seguretat i Enduriment del Contenidor
- Polítiques de Seguretat de Pods i Pod Security Standards
- Seguretat de Xarxa
- Seguretat d'Imatges
- Auditoria, Escaneig i Gestió de Vulnerabilitats
Mòdul 9: Escalat i Rendiment
- Autoescalat Horitzontal de Pods
- Autoescalat Vertical de Pods
- Autoescalat de Clúster
- Escalat per Esdeveniments i Mètriques Personalitzades amb KEDA
- Alta Disponibilitat: PodDisruptionBudgets i Topologia
- Ajust de Rendiment
Mòdul 10: Ecosistema i Eines de Kubernetes
- Minikube i Entorns Locals amb kind
- Kubeadm
- Helm
- Kustomize
- GitOps amb Argo CD i Flux
- Kubernetes Gestionat: EKS, AKS i GKE
Mòdul 11: Estudis de Cas i Aplicacions del Món Real
- Desplegament d'una Aplicació Web
- Execució d'Aplicacions amb Estat
- CI/CD amb Kubernetes
- Estratègies de Desplegament: Blue-Green i Canary
- Gestió Multi-Clúster
- Operació en Producció: Incidències, Runbooks i Costos
