Tancàvem el mòdul 2 amb una llista de deutes i el primer de tots era aquest: la URL de l'API va incrustada dins de la imatge de botiga-web. Cada vegada que volem desplegar la botiga a rutas-norte-pre en lloc de a rutas-norte-pro cal construir una imatge diferent, amb el mateix codi i un sol caràcter canviat en un fitxer de configuració. Això trenca la promesa més bàsica del contenidor: construir una vegada, desplegar a qualsevol lloc. L'objecte que ho arregla és el ConfigMap, un magatzem de configuració no confidencial que viu al clúster, es versiona a Git juntament amb la resta de manifests i s'injecta al contenidor en el moment de l'arrencada. En aquesta lliçó construiràs el ConfigMap de botiga-web amb el seu nginx.conf real i el muntaràs com un fitxer dins del contenidor, que és la forma de consum més potent i la que dona menys sorpreses. La injecció com a variables d'entorn la veuràs a fons a Variables d'Entorn; aquí només l'esmentarem per situar-la.
Contingut
- Per què se separa la configuració del codi
- Anatomia de l'objecte ConfigMap
- Les quatre formes de crear un ConfigMap
- Les tres formes de consumir-lo (i quina veurem aquí)
- Muntar un ConfigMap com a volum
- Seleccionar claus concretes amb
itemsidefaultMode subPath: l'ús i els seus dos perills- El cas central: el
nginx.confdebotiga-web - Actualitzar un ConfigMap i la propagació als volums
- ConfigMaps immutables
- El límit d'1 MiB i què fer si se supera
- ConfigMaps per entorn a Rutas Norte
- Per què se separa la configuració del codi
La metodologia dels dotze factors (The Twelve-Factor App), escrita el 2011 per a aplicacions al núvol, dedica el seu tercer factor exactament a aquest problema i el seu enunciat és una sola frase:
Desa la configuració a l'entorn. La configuració és tot allò que pot variar entre desplegaments; el codi és el que no varia.
La prova pràctica que proposa és taxativa i val la pena aplicar-la a Rutas Norte: podries fer públic el repositori de codi ara mateix, sense filtrar cap credencial? Avui la resposta és no, perquè la contrasenya de postgres-reserves està escrita en un manifest del repositori. Aquesta part la salda la lliçó de Secrets. Però hi ha una segona prova, més silenciosa: és exactament la mateixa imatge la que corre a rutas-norte-dev, a rutas-norte-pre i a rutas-norte-pro? Avui tampoc.
Això no és purisme. Té conseqüències molt concretes:
| Amb la configuració dins de la imatge | Amb la configuració fora de la imatge |
|---|---|
Una imatge per entorn: botiga-web:2.5.0-dev, -pre, -pro |
Una sola imatge botiga-web:2.5.0 per als tres entorns |
El que es prova a pre no és el que surt a pro |
El validat a pre és l'artefacte exacte que es promociona |
| Canviar un temps d'espera exigeix reconstruir i tornar a passar el pipeline (minuts o hores) | Canviar un temps d'espera és editar un objecte del clúster (segons) |
| La configuració de producció viu al registre d'imatges, illegible | La configuració viu en YAML, revisable en una pull request |
| Un rollback de configuració obliga a rebobinar la imatge | Un rollback de configuració és independent del codi |
Els dos casos concrets que arrossega Rutas Norte:
- La URL de l'API a
botiga-web. La SPA necessita saber a quina adreça ha de trucar:http://api-reservesdins del clúster,https://api.rutasnorte.exampledes del navegador del client. Avui està incrustada al JavaScript compilat dins de la imatge. - El
nginx.confdebotiga-web. La configuració del servidor —compressió, capçaleres de seguretat, la regla de fallback aindex.htmlque tota SPA necessita, el proxy cap aapi-reserves— està cuita a la imatge. Ajustar la memòria cau dels fitxers estàtics requereix reconstruir-la.
Tots dos es resolen avui, en aquesta lliçó.
- Anatomia de l'objecte ConfigMap
Un ConfigMap és un dels objectes més simples de Kubernetes. No té spec ni status: només metadata i dades.
apiVersion: v1 # ConfigMap viu al grup core, sense prefix
kind: ConfigMap
metadata:
name: botiga-web-config
namespace: rutas-norte-dev
labels:
app: botiga-web
app.kubernetes.io/part-of: rutas-norte
entorn: dev
data: # parells clau -> valor de TEXT (UTF-8)
API_URL: "http://api-reserves:8080"
NIVELL_LOG: "debug"
nginx.conf: | # el valor pot ser un fitxer sencer
server {
listen 80;
}
binaryData: # parells clau -> valor BINARI (base64)
favicon.ico: AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAA...Els punts que defineixen l'objecte:
apiVersion: v1: pertany al grup core, com Pod, Service o Namespace. No porta grup al davant.- No té
spec. Un ConfigMap no descriu un estat desitjat que un controlador hagi de reconciliar; és un contenidor de dades passiu. Per això tampoc téstatus. dataconté text vàlid en UTF-8. Les claus són noms de fitxer potencials, així que s'han de limitar a caràcters alfanumèrics,-,_i..binaryDataconté dades que no són text, codificades en base64 en escriure-les al manifest. Kubernetes les descodifica en muntar-les. Serveix per a un certificat en format DER, un.ico, una font, un petit.tar.gzde plantilles.- Una mateixa clau no pot aparèixer a
datai abinaryData: l'applyfalla. - Té namespace. Un pod només pot muntar ConfigMaps del seu propi namespace. No hi ha cap forma nativa de referenciar-ne un d'un altre namespace, i aquesta restricció és la que fa natural el patró d'"un ConfigMap per entorn" que veurem a l'apartat 12.
Un detall important sobre les claus multilínia. El | de YAML és un bloc literal: conserva els salts de línia tal qual. És el que vols per a un fitxer de configuració. El seu cosí > (bloc plegat) converteix els salts de línia simples en espais, i arruïnaria un nginx.conf. Per a fitxers, fes servir sempre |.
- Les quatre formes de crear un ConfigMap
3.1. Manifest YAML
És la forma canònica i l'única que es versiona bé a Git. Ja l'has vista a dalt. S'aplica com qualsevol altre objecte:
3.2. --from-literal
Per a valors curts, sobretot quan estàs explorant o generant el YAML amb --dry-run.
kubectl create configmap botiga-web-config \
--from-literal=API_URL=http://api-reserves:8080 \
--from-literal=NIVELL_LOG=debug \
-n rutas-norte-devComprovem què ha creat exactament:
apiVersion: v1
data:
API_URL: http://api-reserves:8080
NIVELL_LOG: debug
kind: ConfigMap
metadata:
creationTimestamp: "2026-08-05T09:14:22Z"
name: botiga-web-config
namespace: rutas-norte-dev
resourceVersion: "184213"
uid: 6f2b1c4a-3d9e-4a71-9f0c-2b8e5d417a63Fixa't que no porta cap etiqueta. Els objectes creats amb kubectl create no segueixen l'esquema d'etiquetatge de Rutas Norte, i això els deixa fora de totes les consultes amb -l app.kubernetes.io/part-of=rutas-norte. Per això l'ús recomanat de l'ordre imperativa és com a generador de YAML, no com a forma de crear objectes:
kubectl create configmap botiga-web-config \
--from-literal=API_URL=http://api-reserves:8080 \
--dry-run=client -o yaml > k8s/base/botiga-web/configmap.yamlDesprés s'editen les etiquetes a mà i s'aplica el fitxer. És el mateix flux --dry-run=client -o yaml del mòdul 1, ara aplicat a la configuració.
3.3. --from-file amb un fitxer
Aquí hi ha el cas interessant. Tens el nginx.conf real al teu repositori i el vols ficar dins d'un ConfigMap sense copiar-lo a mà dins d'un YAML (amb el risc d'espatllar la indentació).
kubectl create configmap botiga-web-nginx \
--from-file=k8s/base/botiga-web/nginx.conf \
--dry-run=client -o yaml -n rutas-norte-devapiVersion: v1
data:
nginx.conf: |
server {
listen 80;
server_name www.rutasnorte.example;
...
}
kind: ConfigMap
metadata:
creationTimestamp: null
name: botiga-web-nginx
namespace: rutas-norte-devLa clau és el nom base del fitxer (nginx.conf), no la ruta completa. Si vols que la clau s'anomeni d'una altra manera, s'anteposa amb un =:
kubectl create configmap botiga-web-nginx \
--from-file=default.conf=k8s/base/botiga-web/nginx-produccio.conf \
--dry-run=client -o yaml -n rutas-norte-devLa clau serà default.conf encara que el fitxer del disc s'anomeni nginx-produccio.conf. Aquest truc és imprescindible quan el nom que espera l'aplicació no coincideix amb el que et resulta còmode al repositori.
3.4. --from-file amb un directori
Si apuntes a un directori, cada fitxer de dins es converteix en una clau. No és recursiu: els subdirectoris s'ignoren.
kubectl create configmap botiga-web-nginx \
--from-file=k8s/base/botiga-web/conf.d/ \
--dry-run=client -o yaml -n rutas-norte-devapiVersion: v1
data:
compressio.conf: |
gzip on;
gzip_types text/css application/javascript application/json;
seguretat.conf: |
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
spa.conf: |
location / {
try_files $uri $uri/ /index.html;
}
kind: ConfigMap
metadata:
creationTimestamp: null
name: botiga-web-nginxTres fitxers, tres claus. Quan muntem aquest ConfigMap a /etc/nginx/conf.d apareixeran els tres fitxers amb aquests mateixos noms, que és just el que nginx espera.
3.5. --from-env-file
Aquesta és la que més confon. Pren un fitxer amb format CLAU=valor i crea una clau per línia, no una clau amb el fitxer sencer.
kubectl create configmap botiga-web-config \
--from-env-file=k8s/entorns/dev/botiga-web.env \
--dry-run=client -o yaml -n rutas-norte-devapiVersion: v1
data:
API_URL: http://api-reserves:8080
NIVELL_LOG: debug
TEMPS_ESPERA_MS: "5000"
kind: ConfigMap
metadata:
creationTimestamp: null
name: botiga-web-configCompara-ho amb el que hauria fet --from-file sobre el mateix fitxer: una sola clau anomenada botiga-web.env el valor de la qual seria el text complet. La diferència és enorme i és la font d'un error clàssic.
| Opció | Sobre app.env produeix |
Ús típic |
|---|---|---|
--from-file=app.env |
1 clau: app.env → contingut sencer |
Muntar el fitxer tal qual |
--from-env-file=app.env |
N claus: una per línia | Alimentar variables d'entorn amb envFrom |
Regles del format de --from-env-file: les línies buides i les que comencen per # s'ignoren; no s'admeten cometes al voltant del valor (es desarien com a part del valor) ni continuacions de línia; i les claus han de ser noms vàlids de variable d'entorn.
3.6. Taula resum
| Forma | Ordre | Quan fer-la servir |
|---|---|---|
| Manifest | kubectl apply -f cm.yaml |
Sempre en producció. És el que es versiona |
| Literal | --from-literal=K=V |
Proves ràpides i generació de YAML |
| Fitxer | --from-file=ruta/f.conf |
Fitxers de configuració llargs |
| Directori | --from-file=ruta/dir/ |
Un conjunt de fitxers que van junts |
| Env file | --from-env-file=app.env |
Moltes variables simples de cop |
- Les tres formes de consumir-lo (i quina veurem aquí)
flowchart LR
CM["ConfigMap<br/>botiga-web-config"] --> A["Variables d'entorn<br/>env / envFrom"]
CM --> B["Fitxer en un volum<br/>volumeMounts"]
CM --> C["Arguments de línia d'ordres<br/>args amb $(VAR)"]
A --> P["Contenidor"]
B --> P
C --> P
| Forma | Avantatge | Inconvenient | On s'estudia |
|---|---|---|---|
| Variable d'entorn | Simple, universal | No es refresca sense reiniciar el pod | 03-03 |
| Fitxer en volum | S'actualitza en calent, admet fitxers sencers | L'app ha de llegir del disc | Aquesta lliçó |
Argument (args) |
Explícit al manifest | Fix des de l'arrencada | 03-03 |
Aquesta lliçó se centra en la segona perquè és la que resol el cas del nginx.conf —un fitxer de 40 línies no cap còmodament en una variable d'entorn— i perquè és l'única que permet canviar la configuració sense recrear pods.
- Muntar un ConfigMap com a volum
El mecanisme té sempre dues meitats que han de quadrar: un volum a nivell de pod que declara d'on surten les dades, i un volumeMount a nivell de contenidor que declara on apareixen. El pont entre tots dos és el camp name.
apiVersion: apps/v1
kind: Deployment
metadata:
name: botiga-web
namespace: rutas-norte-dev
spec:
replicas: 3
selector:
matchLabels:
app: botiga-web # només app i entorn al selector
entorn: dev
template:
metadata:
labels:
app: botiga-web
app.kubernetes.io/name: botiga-web
app.kubernetes.io/part-of: rutas-norte
entorn: dev
spec:
containers:
- name: nginx
image: nginx:1.27.1-alpine # etiqueta immutable, mai latest
ports:
- containerPort: 80
volumeMounts:
- name: config-nginx # (2) ha de coincidir amb volumes[].name
mountPath: /etc/nginx/conf.d # on apareix dins del contenidor
readOnly: true # bona pràctica: l'app no ha d'escriure
volumes:
- name: config-nginx # (1) nom del volum dins del pod
configMap:
name: botiga-web-nginx # ConfigMap del MATEIX namespaceQuè passa exactament quan arrenca aquest pod:
- El kubelet llegeix el pod i veu un volum de tipus
configMap. - Demana a l'API el ConfigMap
botiga-web-nginxdel namespacerutas-norte-dev. Si no existeix, el pod es queda enContainerCreatingfins que aparegui, amb un esdevenimentFailedMount. No falla per sempre: tan bon punt creïs el ConfigMap, el pod arrenca. - Crea al node un directori i hi escriu a dins un fitxer per cada clau del ConfigMap.
- Munta aquest directori a
/etc/nginx/conf.ddins del contenidor.
I aquí hi ha el punt que sorprèn tothom la primera vegada: el muntatge substitueix el contingut del directori del contenidor. El que la imatge nginx portava a /etc/nginx/conf.d (el fitxer default.conf per defecte) deixa de veure's. No s'esborra de la imatge; simplement queda ocult sota el punt de muntatge, igual que a Linux en muntar un disc sobre un directori que ja tenia fitxers.
Anem a veure-ho:
total 12
drwxrwxrwt 3 root root 120 Aug 5 09:41 .
drwxr-xr-x 3 root root 4096 Aug 5 09:41 ..
drwxr-xr-x 2 root root 80 Aug 5 09:41 ..2026_08_05_09_41_18.1042318874
lrwxrwxrwx 1 root root 32 Aug 5 09:41 ..data -> ..2026_08_05_09_41_18.1042318874
lrwxrwxrwx 1 root root 17 Aug 5 09:41 compressio.conf -> ..data/compressio.conf
lrwxrwxrwx 1 root root 17 Aug 5 09:41 seguretat.conf -> ..data/seguretat.conf
lrwxrwxrwx 1 root root 11 Aug 5 09:41 spa.conf -> ..data/spa.confAquesta sortida conté tota la màgia del mecanisme, així que convé llegir-la amb calma:
- Els fitxers que veu l'aplicació (
compressio.conf, etc.) són enllaços simbòlics a..data/. ..dataés al seu torn un enllaç simbòlic a un directori amb marca de temps.- El directori real conté els fitxers de veritat.
Per què tanta indirecció? Perquè permet una actualització atòmica. Quan el ConfigMap canvia, el kubelet escriu un directori nou amb marca de temps nova i després mou l'enllaç ..data de cop. L'aplicació no veu mai un estat a mitges amb la meitat dels fitxers vells i la meitat nous. És un detall d'implementació, però explica dos comportaments que veuràs més endavant: per què l'actualització funciona i per què amb subPath no funciona.
- Seleccionar claus concretes amb
items i defaultMode
items i defaultModePer defecte es projecten totes les claus. Sovint no és el que vols: un mateix ConfigMap pot tenir claus destinades a variables d'entorn (API_URL) i claus que són fitxers (nginx.conf), i muntar-les totes juntes ompliria /etc/nginx/conf.d de brossa que nginx intentaria interpretar.
Amb items tries quines claus es projecten i amb quin nom de fitxer:
volumes:
- name: config-nginx
configMap:
name: botiga-web-config
defaultMode: 0444 # permisos per defecte: només lectura
items:
- key: nginx.conf # clau del ConfigMap
path: default.conf # nom del fitxer resultant
mode: 0400 # permisos NOMÉS d'aquest fitxer
- key: mapa-mime.types
path: mime.typesDetalls que importen:
pathés relatiu almountPath. AmbmountPath: /etc/nginx/conf.dipath: default.conf, el fitxer acaba a/etc/nginx/conf.d/default.conf.pathadmet subdirectoris:path: extra/capceleres.confcrea el subdirectori. El que no admet és començar per/ni contenir...- Si fas servir
items, només es projecta el que llistis. Les claus no esmentades desapareixen del volum. - Si una clau llistada a
itemsno existeix al ConfigMap, el pod no arrenca. És un comportament desitjable: falla aviat i amb un missatge clar en lloc d'arrencar nginx sense configuració. defaultModes'escriu en octal i s'ha de posar amb el0al davant (0444). Si escrius444en decimal, YAML ho interpreta com el número 444, que en octal és0674: permisos absurds. És un error real i freqüent.- El
moded'unitemconcret preval sobredefaultMode.
Un advertiment sobre els permisos: si el contenidor corre amb un usuari no root (l'habitual i el recomanable, com veuràs a Contextos de Seguretat), un defaultMode: 0400 significa "només lectura per al propietari", i el propietari del fitxer és root. El procés no el podria llegir. Per a aquest cas, 0444 és l'elecció segura.
subPath: l'ús i els seus dos perills
subPath: l'ús i els seus dos perillsTorna al problema de l'apartat 5: muntar un volum sobre un directori amaga el que ja hi havia. I si només vols afegir o substituir un únic fitxer dins d'un directori que ha de conservar la resta del seu contingut?
El cas canònic a Rutas Norte és /etc/nginx/nginx.conf. Aquest fitxer és a /etc/nginx/, un directori que també conté mime.types, fastcgi_params, el directori conf.d... Si muntem el volum a /etc/nginx/, nginx es queda sense tota la resta i no arrenca.
La solució és subPath:
volumeMounts:
- name: config-nginx
mountPath: /etc/nginx/nginx.conf # ruta a un FITXER, no a un directori
subPath: nginx.conf # quina clau del volum es munta aquí
readOnly: trueAmb això, només el fitxer /etc/nginx/nginx.conf queda substituït; la resta de /etc/nginx/ continua intacta.
Tot al seu lloc. Ara els dos perills, que cal conèixer abans de fer-lo servir:
Perill 1: amb subPath l'actualització NO es propaga. Quan muntes amb subPath, el kubelet copia el fitxer directament en lloc de crear la cadena d'enllaços simbòlics de l'apartat 5. En no haver-hi enllaç ..data per moure, el fitxer queda congelat en el valor que tenia en arrencar el pod. Pots editar el ConfigMap cent vegades: el contenidor continuarà veient l'original fins que el recreïs. És, de bon tros, la causa número u de "he canviat la configuració i no passa res".
Perill 2: és fàcil equivocar-se de sentit. mountPath ha d'apuntar al fitxer de destinació i subPath a la clau d'origen. Si els intercanvies, o si mountPath apunta a un directori existent, obtindràs un directori on esperaves un fitxer i l'aplicació fallarà amb un missatge confús.
Sense subPath |
Amb subPath |
|---|---|
mountPath és un directori |
mountPath és un fitxer |
| Amaga el contingut previ del directori | Conserva la resta del directori |
Fitxers com a enllaços simbòlics a ..data |
Fitxer copiat directament |
| S'actualitza en calent | No s'actualitza mai |
| Preferible sempre que sigui possible | Només si no hi ha alternativa |
La recomanació pràctica: evita subPath si pots. En el cas de nginx tens una alternativa millor, que és la que farem servir: muntar a /etc/nginx/conf.d/, el directori que la mateixa imatge reserva per a configuració afegida, sense tocar /etc/nginx/nginx.conf. Aquest directori està pensat per ser substituït sencer.
- El cas central: el
nginx.conf de botiga-web
nginx.conf de botiga-webAnem a resoldre el deute del mòdul 2 amb tot l'anterior. Creem el fitxer k8s/base/botiga-web/configmap-nginx.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: botiga-web-nginx
namespace: rutas-norte-dev
labels:
app: botiga-web
app.kubernetes.io/name: botiga-web
app.kubernetes.io/component: frontend
app.kubernetes.io/part-of: rutas-norte
entorn: dev
data:
default.conf: |
server {
listen 80;
server_name www.rutasnorte.example;
root /usr/share/nginx/html;
index index.html;
# Capceleres de seguretat per al portal de venda de bitllets
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Compressio dels estatics de la SPA
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
# Els estatics amb hash al nom es guarden a la cache un any
location ~* \.(js|css|woff2|png|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Regla imprescindible en una SPA: qualsevol ruta desconeguda
# retorna index.html perque l'enrutador del navegador la resolgui
location / {
try_files $uri $uri/ /index.html;
}
# Sonda de vida per al modul 7
location = /healthz {
access_log off;
return 200 "ok\n";
}
}I modifiquem el Deployment de botiga-web que ja teníem:
apiVersion: apps/v1
kind: Deployment
metadata:
name: botiga-web
namespace: rutas-norte-dev
labels:
app: botiga-web
app.kubernetes.io/name: botiga-web
app.kubernetes.io/part-of: rutas-norte
entorn: dev
spec:
replicas: 3
selector:
matchLabels:
app: botiga-web
entorn: dev
template:
metadata:
labels:
app: botiga-web
app.kubernetes.io/name: botiga-web
app.kubernetes.io/component: frontend
app.kubernetes.io/part-of: rutas-norte
entorn: dev
spec:
containers:
- name: nginx
image: nginx:1.27.1-alpine
ports:
- name: http
containerPort: 80
volumeMounts:
- name: config-nginx
mountPath: /etc/nginx/conf.d # directori sencer: sense subPath
readOnly: true
resources: # els afinarem a 03-04
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
volumes:
- name: config-nginx
configMap:
name: botiga-web-nginx
defaultMode: 0444Apliquem i comprovem:
kubectl apply -f k8s/base/botiga-web/configmap-nginx.yaml
kubectl apply -f k8s/base/botiga-web/deployment.yaml
kubectl rollout status deploy/botiga-web -n rutas-norte-devconfigmap/botiga-web-nginx created
deployment.apps/botiga-web configured
Waiting for deployment "botiga-web" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "botiga-web" rollout to finish: 2 out of 3 new replicas have been updated...
deployment "botiga-web" successfully rolled outVerifiquem que nginx està llegint la nostra configuració i no la de la imatge:
kubectl exec -n rutas-norte-dev deploy/botiga-web -- cat /etc/nginx/conf.d/default.conf | head -5
kubectl exec -n rutas-norte-dev deploy/botiga-web -- nginx -T 2>/dev/null | grep -c "try_files"server {
listen 80;
server_name www.rutasnorte.example;
root /usr/share/nginx/html;
index index.html;
1nginx -T bolca la configuració efectiva després de resoldre tots els include. Que aparegui el nostre try_files confirma que la regla de la SPA està activa. I la prova definitiva, des d'un pod efímer:
kubectl run prova --rm -it --restart=Never --image=curlimages/curl:8.10.1 \
-n rutas-norte-dev -- curl -si http://botiga-web/healthzHTTP/1.1 200 OK
Server: nginx/1.27.1
Content-Type: text/plain
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
okLes tres capçaleres de seguretat hi són. La configuració de nginx ja no viu a la imatge: viu en un YAML revisable en una pull request.
- Actualitzar un ConfigMap i la propagació als volums
Ara el pagament de l'esforç. Afegim una capçalera més sense reconstruir la imatge i sense reiniciar res.
Esperem i mirem dins del contenidor:
kubectl exec -n rutas-norte-dev deploy/botiga-web -- grep Permissions-Policy /etc/nginx/conf.d/default.confEl fitxer ha canviat dins d'un contenidor que no s'ha reiniciat. Els tres pods continuen tenint RESTARTS 0.
Sobre els temps i les condicions:
- La propagació no és instantània. El kubelet refresca els volums al seu cicle de sincronització, governat per
--sync-frequency(per defecte 1 minut), més el temps de memòria cau del seu client de l'API (configMapAndSecretChangeDetectionStrategy, per defecteWatch). A la pràctica compta amb fins a un parell de minuts, no amb segons. - Es propaga només si el volum no fa servir
subPath(apartat 7). - Es propaga només si el ConfigMap no és immutable (apartat 10).
- I, sobretot: que el fitxer canviï no vol dir que l'aplicació se n'assabenti. nginx va llegir la seva configuració en arrencar i la té a memòria.
Aquest darrer punt és fonamental. Hi ha tres tipus d'aplicació:
| Tipus d'aplicació | Comportament en canviar el fitxer | Què fer |
|---|---|---|
| Rellegeix el fitxer a cada ús | S'aplica sola | Res |
| Recarrega amb un senyal (nginx, HAProxy) | Ignora el canvi | Enviar el senyal o reiniciar |
| Llegeix només en arrencar (la majoria) | Ignora el canvi | Reiniciar els pods |
Per a nginx podem enviar-li el senyal de recàrrega, que no talla cap connexió en curs:
Per al cas general, la solució neta és forçar un desplegament nou:
Això substitueix els pods amb l'estratègia RollingUpdate del mòdul 2, sense tall de servei. Existeix una tècnica més elegant i automàtica —desar un hash del ConfigMap en una anotació del template, de manera que canviar la configuració dispari el desplegament tot sol— que veuràs a Variables d'Entorn, on encara és més necessària.
- ConfigMaps immutables
Des de Kubernetes 1.21, un ConfigMap es pot declarar immutable:
apiVersion: v1
kind: ConfigMap
metadata:
name: botiga-web-nginx-v3
namespace: rutas-norte-pro
data:
default.conf: |
server { listen 80; }
immutable: true # a partir d'aqui, ni data ni binaryData es poden tocarQuè hi guanya:
- Rendiment i escala. Aquest és el motiu principal. Cada kubelet manté un watch obert contra l'apiserver per cada ConfigMap que munta. En un clúster gran això són milers de connexions vigilant canvis. Si l'objecte és immutable, el kubelet tanca el watch: no hi ha res a vigilar. La càrrega sobre l'apiserver baixa de manera notable.
- Protecció contra el canvi accidental. Ningú no pot modificar per error la configuració de producció d'una tirada.
- Determinisme. Un pod que arrenqui avui i un altre que arrenqui d'aquí a un mes amb el mateix ConfigMap tindran exactament la mateixa configuració. Amb un ConfigMap mutable, dues rèpliques del mateix Deployment podrien tenir continguts diferents si el ConfigMap va canviar entre les seves arrencades.
Què s'hi perd: l'actualització en calent de l'apartat 9. I hi ha dues regles dures:
- La immutabilitat no es pot revertir. Un cop
immutable: true, no es pot tornar afalse. Només queda esborrar l'objecte i recrear-lo. metadatasí que es pot canviar (etiquetes, anotacions). El que queda congelat ésdataibinaryData.
Intentar modificar-lo produeix un error molt clar:
The ConfigMap "botiga-web-nginx-v3" is invalid: data: Forbidden: field is immutable when `immutable` is setEl patró que acompanya la immutabilitat és versionar el nom: botiga-web-nginx-v3, -v4, etc. El Deployment apunta a la versió concreta, i canviar de configuració significa crear un ConfigMap nou i actualitzar la referència al Deployment, cosa que dispara un desplegament amb historial i rollback. És exactament el que fan els generadors de Kustomize afegint un sufix amb el hash del contingut.
Recomanació per a Rutas Norte: mutable a dev (iterar ràpid amb nginx -s reload), immutable amb nom versionat a pro (determinisme i rollback).
- El límit d'1 MiB i què fer si se supera
Un ConfigMap no pot superar 1 MiB, comptant la suma de data i binaryData més les metadades. El límit no és capriciós: els ConfigMaps es desen a etcd, que imposa per defecte una mida màxima d'1,5 MiB per valor, i etcd és el cor del clúster.
En passar-te'n, l'error és immediat:
O bé, quan la validació la fa el mateix objecte:
Tingues present a més que base64 infla un 33 %: un .ico de 800 KiB ocupa uns 1,07 MiB a binaryData i no hi cap.
Què fer si t'acostes al límit:
| Situació | Solució |
|---|---|
| Molts fitxers que van junts | Dividir en diversos ConfigMaps i muntar diversos volums |
| Un sol fitxer enorme (dataset, catàleg) | No és configuració. Va en un volum persistent (mòdul 5) o es descarrega en arrencar |
| Binaris grans (fonts, imatges) | Han d'anar a la imatge del contenidor |
| Plantilles comprimides | .tar.gz a binaryData i descompressió amb un init container (06-04) |
La regla d'olfacte: si t'acostes al megabyte, gairebé segur que estàs ficant en un ConfigMap alguna cosa que no és configuració. A Rutas Norte això aplicaria al catàleg complet de rutes i parades, que són dades de negoci i viuen a postgres-reserves, no en un ConfigMap.
- ConfigMaps per entorn a Rutas Norte
Recorda la restricció de l'apartat 2: un ConfigMap té namespace i un pod només pot muntar els del seu propi namespace. Com que els entorns de Rutas Norte són namespaces, això encaixa de manera natural: un ConfigMap per entorn, amb el mateix nom a cadascun.
flowchart TB
subgraph dev["rutas-norte-dev"]
CMD["ConfigMap botiga-web-config<br/>API_URL: http://api-reserves:8080<br/>NIVELL_LOG: debug"]
DD["Deployment botiga-web<br/>configMap: botiga-web-config"]
CMD --- DD
end
subgraph pre["rutas-norte-pre"]
CMP["ConfigMap botiga-web-config<br/>API_URL: https://api-pre.rutasnorte.example<br/>NIVELL_LOG: info"]
DP["Deployment botiga-web<br/>configMap: botiga-web-config"]
CMP --- DP
end
subgraph pro["rutas-norte-pro"]
CMR["ConfigMap botiga-web-config<br/>API_URL: https://api.rutasnorte.example<br/>NIVELL_LOG: warn"]
DR["Deployment botiga-web<br/>configMap: botiga-web-config"]
CMR --- DR
end
IMG["Imatge unica<br/>botiga-web:2.5.0"] --> DD
IMG --> DP
IMG --> DR
El nom del ConfigMap és idèntic als tres namespaces. Això permet que el Deployment sigui literalment el mateix fitxer a k8s/base/, i que l'única cosa que canviï entre entorns sigui el ConfigMap de k8s/entorns/<entorn>/. L'estructura del repositori queda així:
k8s/
├── base/
│ ├── botiga-web/
│ │ ├── deployment.yaml # identic per als tres entorns
│ │ ├── service.yaml
│ │ └── configmap-nginx.yaml # nginx.conf comu
│ └── api-reserves/
│ ├── deployment.yaml
│ └── service.yaml
└── entorns/
├── dev/
│ ├── namespace.yaml
│ ├── botiga-web-config.yaml # API_URL de dev
│ └── api-reserves-config.yaml
├── pre/
│ └── ...
└── pro/
└── ...I aquest és el contingut concret de la configuració de Rutas Norte per entorn. Pren aquesta taula com a referència; a 03-03 l'ampliarem al catàleg complet de tots els components.
| Clau | dev |
pre |
pro |
|---|---|---|---|
API_URL |
http://api-reserves:8080 |
https://api-pre.rutasnorte.example |
https://api.rutasnorte.example |
NIVELL_LOG |
debug |
info |
warn |
TEMPS_ESPERA_MS |
5000 |
3000 |
2000 |
REDIS_HOST |
redis-cache |
redis-cache |
redis-cache |
REDIS_TTL_SEGONS |
30 |
120 |
300 |
MAX_PLACES_PER_RESERVA |
9 |
9 |
9 |
MODE_MANTENIMENT |
false |
false |
false |
Observa dues decisions de disseny:
REDIS_TTL_SEGONSpuja en acostar-se a producció. En desenvolupament interessa una memòria cau curta per veure els canvis; en producció, amb els pics de trànsit de ponts i vacances, interessa una memòria cau llarga que descarreguipostgres-reserves.MODE_MANTENIMENTés un interruptor operatiu. Posar-lo atruei recarregar nginx mostra la pàgina d'"estem actualitzant" sense desplegar res. És exactament el tipus de palanca que justifica separar configuració de codi.
I una cosa que no hi ha en aquesta taula i mai no hi ha de ser: la contrasenya de postgres-reserves. Un ConfigMap no ofereix cap protecció: qualsevol que pugui llistar ConfigMaps al namespace en llegeix el contingut en clar. Aquest deute el salda la lliçó següent.
Errors Comuns i Consells
| Error | Símptoma | Solució |
|---|---|---|
| ConfigMap en un altre namespace | Pod en ContainerCreating, esdeveniment FailedMount |
Un pod només munta ConfigMaps del seu namespace. Crea'l al correcte |
| ConfigMap inexistent | configmap "x" not found als esdeveniments |
kubectl get cm -n <ns> i revisa el nom |
| Muntar sobre un directori amb contingut | L'aplicació no troba els seus fitxers | Munta en un subdirectori previst (conf.d) o fes servir subPath |
Esperar actualització amb subPath |
"He canviat el ConfigMap i no passa res" | subPath no propaga. Treu-lo o fes rollout restart |
| Esperar que l'app recarregui sola | El fitxer canvia però el comportament no | Envia el senyal de recàrrega o reinicia els pods |
defaultMode: 644 sense el zero |
Permisos estranys (--w----r-T) |
És octal: escriu 0644 |
0400 amb usuari no root |
Permission denied en llegir |
Fes servir 0444 o ajusta fsGroup |
Fer servir > en lloc de ` |
` a YAML | El fitxer arriba amb les línies enganxades |
Confondre --from-file i --from-env-file |
Una clau amb tot el .env a dins |
Revisa la taula de l'apartat 3.5 |
| Ficar secrets en un ConfigMap | Credencial llegible per qualsevol | Fes servir Secrets i RBAC |
Objectes creats amb kubectl create |
No apareixen a les consultes amb -l |
Genera el YAML amb --dry-run=client -o yaml i afegeix etiquetes |
| Superar 1 MiB | Too long: must have at most 1048576 bytes |
Divideix, o admet que no és configuració |
Cinc consells que estalvien hores:
- Etiqueta els ConfigMaps amb el mateix esquema que la resta.
kubectl get cm -l app=botiga-web -Aha de funcionar. - Comprova sempre des de dins del contenidor, no des del YAML:
kubectl exec ... -- cat <ruta>. És l'única veritat. - Documenta cada clau amb un comentari al YAML. El manifest és la documentació de la configuració.
- Un ConfigMap per propòsit, no un de gegant per aplicació. Separa el que es munta com a fitxer del que alimenta variables d'entorn.
- En producció, immutable i versionat. El determinisme compensa la comoditat.
Exercicis
Exercici 1: De la imatge al ConfigMap
api-reserves necessita un fitxer /app/config/regles-tarifes.json amb les regles de descompte, que avui és dins de la seva imatge.
- Crea a la teva màquina el fitxer
regles-tarifes.jsonamb aquest contingut:
{
"descompte_anada_tornada": 0.12,
"descompte_jove": 0.20,
"edat_maxima_jove": 26,
"recarrec_pont": 0.15
}- Genera amb
kubectl(sense escriure YAML a mà) el manifest d'un ConfigMap anomenatapi-reserves-tarifesarutas-norte-deva partir d'aquest fitxer, i desa'l ak8s/entorns/dev/. - Afegeix-li les etiquetes de l'esquema de Rutas Norte i aplica'l.
- Escriu el fragment de
volumesivolumeMountsque el munta a/app/configde manera que només es pugui llegir. - Verifica des de dins del pod que el fitxer existeix i té el contingut correcte.
Exercici 2: subPath i el parany de l'actualització
- Desplega un pod de
botiga-webque munti la seva configuració ambsubPathsobre/etc/nginx/nginx.conf. - Comprova que
/etc/nginx/conservamime.typesi el directoriconf.d. - Modifica el ConfigMap i espera tres minuts. Comprova si el fitxer ha canviat dins del contenidor.
- Repeteix l'experiment muntant el mateix ConfigMap sense
subPatha/etc/nginx/conf.d/. Es propaga ara? - Explica en dues línies la diferència i què es veu amb
ls -laen cada cas.
Exercici 3: Configuració dels tres entorns
- Crea el ConfigMap
botiga-web-configals tres namespaces amb els valors de la taula de l'apartat 12. - Demostra amb una sola ordre que existeixen tres objectes amb aquest nom i que els seus valors de
NIVELL_LOGsón diferents. - Fes immutable únicament el de
rutas-norte-proi intenta modificar-lo. Copia l'error. - Actualitza el
REDIS_TTL_SEGONSde producció sabent que el ConfigMap és immutable. Descriu la seqüència completa de passos. - Respon: si demà cal activar el mode manteniment a producció a les 3 de la matinada, quina ordre exacta executaries?
Solucions
Solució 1
mkdir -p k8s/entorns/dev
cat > /tmp/regles-tarifes.json <<'EOF'
{
"descompte_anada_tornada": 0.12,
"descompte_jove": 0.20,
"edat_maxima_jove": 26,
"recarrec_pont": 0.15
}
EOF
kubectl create configmap api-reserves-tarifes \
--from-file=/tmp/regles-tarifes.json \
-n rutas-norte-dev \
--dry-run=client -o yaml > k8s/entorns/dev/api-reserves-tarifes.yamlEl YAML generat, ja amb les etiquetes afegides a mà:
apiVersion: v1
kind: ConfigMap
metadata:
name: api-reserves-tarifes
namespace: rutas-norte-dev
labels:
app: api-reserves
app.kubernetes.io/name: api-reserves
app.kubernetes.io/component: backend
app.kubernetes.io/part-of: rutas-norte
entorn: dev
data:
regles-tarifes.json: |
{
"descompte_anada_tornada": 0.12,
"descompte_jove": 0.20,
"edat_maxima_jove": 26,
"recarrec_pont": 0.15
}El fragment del Deployment:
volumeMounts:
- name: tarifes
mountPath: /app/config
readOnly: true
volumes:
- name: tarifes
configMap:
name: api-reserves-tarifes
defaultMode: 0444I la verificació:
{
"descompte_anada_tornada": 0.12,
"descompte_jove": 0.20,
"edat_maxima_jove": 26,
"recarrec_pont": 0.15
}Un apunt: la clau del ConfigMap és regles-tarifes.json perquè --from-file pren el nom base del fitxer, i el mountPath és el directori /app/config. El resultat és /app/config/regles-tarifes.json, la ruta demanada.
Solució 2
Amb subPath:
volumeMounts:
- name: config-nginx
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
readOnly: trueEl directori ho conserva tot. Després de modificar el ConfigMap i esperar:
kubectl apply -f configmap-nginx.yaml
sleep 180
kubectl exec -n rutas-norte-dev deploy/botiga-web -- grep -c "worker_connections 2048" /etc/nginx/nginx.confNo ha canviat. El fitxer continua amb el valor de l'arrencada.
Sense subPath, muntant a /etc/nginx/conf.d/:
kubectl apply -f configmap-nginx.yaml
sleep 90
kubectl exec -n rutas-norte-dev deploy/botiga-web -- grep -c "Permissions-Policy" /etc/nginx/conf.d/default.confAra sí. La diferència es veu amb ls -la:
# SENSE subPath: enllacos simbolics, l'actualitzacio mou ..data de forma atomica
lrwxrwxrwx 1 root root 15 Aug 5 10:41 default.conf -> ..data/default.conf
lrwxrwxrwx 1 root root 32 Aug 5 10:41 ..data -> ..2026_08_05_10_41_02.418822910
# AMB subPath: fitxer real copiat en arrencar, sense enllac que actualitzar
-rw-r--r-- 1 root root 648 Aug 5 09:12 nginx.confSense subPath, el kubelet actualitza escrivint un directori nou i movent l'enllaç ..data. Amb subPath no hi ha enllaç: el fitxer es va copiar una vegada i allà es queda fins que es recreï el pod.
Solució 3
for E in dev pre pro; do
kubectl create namespace rutas-norte-$E --dry-run=client -o yaml | kubectl apply -f -
done
kubectl create configmap botiga-web-config -n rutas-norte-dev \
--from-literal=API_URL=http://api-reserves:8080 \
--from-literal=NIVELL_LOG=debug \
--from-literal=TEMPS_ESPERA_MS=5000 \
--from-literal=REDIS_TTL_SEGONS=30
kubectl create configmap botiga-web-config -n rutas-norte-pre \
--from-literal=API_URL=https://api-pre.rutasnorte.example \
--from-literal=NIVELL_LOG=info \
--from-literal=TEMPS_ESPERA_MS=3000 \
--from-literal=REDIS_TTL_SEGONS=120
kubectl create configmap botiga-web-config -n rutas-norte-pro \
--from-literal=API_URL=https://api.rutasnorte.example \
--from-literal=NIVELL_LOG=warn \
--from-literal=TEMPS_ESPERA_MS=2000 \
--from-literal=REDIS_TTL_SEGONS=300La comprovació en una ordre:
kubectl get cm botiga-web-config -A -o custom-columns=\
NS:.metadata.namespace,LOG:.data.NIVELL_LOG,TTL:.data.REDIS_TTL_SEGONS,API:.data.API_URLNS LOG TTL API
rutas-norte-dev debug 30 http://api-reserves:8080
rutas-norte-pre info 120 https://api-pre.rutasnorte.example
rutas-norte-pro warn 300 https://api.rutasnorte.exampleTres objectes amb el mateix nom i continguts diferents, exactament el que permet que el Deployment sigui idèntic.
Ara la immutabilitat de producció:
kubectl patch configmap botiga-web-config -n rutas-norte-pro \
--type merge -p '{"immutable": true}'
kubectl patch configmap botiga-web-config -n rutas-norte-pro \
--type merge -p '{"data": {"NIVELL_LOG": "info"}}'configmap/botiga-web-config patched
Error from server: ConfigMap "botiga-web-config" is invalid: data: Forbidden: field is immutable when `immutable` is setPer actualitzar REDIS_TTL_SEGONS a producció essent immutable, la seqüència correcta és:
- Crear un ConfigMap nou amb nom versionat,
botiga-web-config-v2, amb el valor nou iimmutable: true. - Aplicar-lo.
- Editar el Deployment de
rutas-norte-properquè apunti abotiga-web-config-v2, ambkubernetes.io/change-causedocumentant el motiu. kubectl rollout statusper seguir el desplegament progressiu del mòdul 2.- Verificar i conservar el
-v1uns dies: si cal ferkubectl rollout undo, el Deployment el tornarà a referenciar i ha d'existir. - Esborrar el
-v1passada la finestra de rollback.
Fixa't en l'avantatge: la configuració de producció ara té historial i rollback, igual que el codi.
Per al mode manteniment a les 3 de la matinada, amb el ConfigMap mutable d'nginx:
kubectl patch configmap botiga-web-nginx -n rutas-norte-pro \
--type merge -p '{"data": {"manteniment.conf": "return 503;"}}'
kubectl exec -n rutas-norte-pro deploy/botiga-web -- nginx -s reloadSense reconstruir imatge, sense desplegar, sense tallar connexions. Aquest és exactament el valor de tenir la configuració fora del codi.
Conclusió
Has saldat el primer dels deutes del mòdul 2. Saps per què el tercer factor dels dotze factors exigeix que la configuració visqui fora del codi, i has vist el cost concret de no fer-ho: una imatge per entorn, el provat a pre diferent del desplegat a pro, i un canvi de temps d'espera convertit en un pipeline complet. Coneixes l'anatomia del ConfigMap —sense spec ni status, amb data per a text i binaryData per a la resta, amb namespace i amb les claus com a noms de fitxer potencials— i les seves quatre formes de creació, sabent distingir el parany entre --from-file i --from-env-file.
Domines el consum com a volum, que és el que dona més joc: la parella volumes/volumeMounts, el fet que el muntatge amaga el contingut previ del directori, la cadena d'enllaços simbòlics cap a ..data que fa atòmica l'actualització, la selecció de claus amb items, els permisos amb defaultMode en octal i les seves interaccions amb els usuaris no root, i els dos perills de subPath, començant pel més car: amb subPath la configuració no es propaga mai. Saps que la propagació triga fins a un parell de minuts i que canviar el fitxer no equival que l'aplicació se n'assabenti, amb les tres estratègies segons el tipus d'aplicació. I coneixes els ConfigMaps immutables, el que guanyen en càrrega sobre l'apiserver i determinisme, el patró de nom versionat que els acompanya, i el límit d'1 MiB amb el senyal que emet: si t'hi acostes, no és configuració.
Sobre el terreny, botiga-web ja serveix el seu nginx.conf des d'un ConfigMap muntat a /etc/nginx/conf.d, amb capçaleres de seguretat, compressió, memòria cau d'estàtics, la regla try_files de la SPA i un endpoint /healthz que la lliçó de sondes aprofitarà. Els tres entorns tenen el seu propi ConfigMap amb el mateix nom, cosa que ha permès que el Deployment sigui un únic fitxer a k8s/base/.
Queda el deute gros. Tot el que hem desat avui és públic: qualsevol amb permís de lectura al namespace veu el contingut íntegre d'un ConfigMap. La contrasenya de postgres-reserves —la base de dades que desa el nom, el DNI, el telèfon i el correu de cada client de Rutas Norte— continua en clar en un manifest de Git. La lliçó següent, Secrets, la treu d'allà: veuràs en què es diferencia realment un Secret d'un ConfigMap, per què base64 no és xifratge i ho demostraràs descodificant-ne un en tres segons, els tipus de Secret i els seus usos, el xifratge en repòs a etcd que per defecte no està activat, com es descarreguen imatges privades de registry.rutasnorte.example i quines solucions reals fa servir l'ecosistema quan el Secret natiu no n'hi ha prou.
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
