Tancàvem la lliçó anterior assenyalant un fil solt que ha recorregut tot el mòdul. El selector d'un ReplicaSet que adopta pods, el selector d'un Service que troba els seus destins, l'etiqueta entorn amb què les polítiques de xarxa seleccionaran namespaces, l'anotació kubernetes.io/change-cause que documenta cada revisió, el pod-template-hash que el Deployment afegeix pel seu compte, i totes les consultes amb -l que fem servir des de la primera lliçó: tot això són etiquetes i anotacions. Les hem anat fent servir per necessitat, sense sistematitzar. Aquesta lliçó hi posa ordre, i no és un afer menor: en un clúster amb sis components per tres entorns, l'esquema d'etiquetatge és el que separa una plataforma navegable d'un caos d'objectes anònims. Veuràs la diferència exacta entre etiqueta i anotació, la sintaxi i les restriccions de claus i valors, les etiquetes recomanades per Kubernetes i com es concreten a l'esquema oficial de Rutas Norte, els selectors d'igualtat i de conjunt en totes les seves formes, qui els consumeix, per què el selector d'un Deployment és immutable, l'ús real de les anotacions i les consultes del dia a dia que et faran productiu.

Contingut

  1. Etiquetes enfront d'anotacions
  2. Sintaxi i restriccions de claus i valors
  3. Les etiquetes recomanades per Kubernetes
  4. L'esquema d'etiquetatge de Rutas Norte
  5. Selectors d'igualtat
  6. Selectors de conjunt
  7. Selectors en manifests: matchLabels i matchExpressions
  8. Qui consumeix selectors
  9. La regla d'or: el selector d'un Deployment és immutable
  10. Anotacions a la pràctica
  11. kubectl label i kubectl annotate
  12. Consultes útils del dia a dia

  1. Etiquetes enfront d'anotacions

Totes dues viuen a metadata i totes dues són parells clau-valor. Aquí s'acaba la semblança.

metadata:
  name: api-reserves
  labels:                                    # per IDENTIFICAR i SELECCIONAR
    app.kubernetes.io/name: api-reserves
    entorn: pro
  annotations:                               # per ADJUNTAR INFORMACIÓ
    kubernetes.io/change-cause: "v2.5.0 - cache de disponibilitat (RN-482)"
    rutasnorte.example/responsable: [email protected]
Aspecte Etiquetes (labels) Anotacions (annotations)
Propòsit Identificar i seleccionar objectes Adjuntar metadades no identificatives
Es poden consultar? : kubectl get -l, selectors No: no existeix --annotation-selector
Estan indexades? , etcd les indexa No
Mida del valor Màxim 63 caràcters Fins a 256 KiB en total
Caràcters del valor Alfanumèrics, -, _, . Qualsevol: JSON, text llarg, URLs, salts de línia
Qui les fa servir Controladors, Services, el planificador, tu Eines, kubectl, controladors d'Ingress, humans
Cost de tenir-ne moltes Impacte en el rendiment de les consultes Pràcticament nul
Exemple app: api-reserves kubernetes.io/change-cause: "..."

La regla de decisió, que resol el 100 % dels dubtes:

Voldràs buscar objectes per aquesta dada, o servirà a un selector? És una etiqueta. És informació que es llegeix però no es busca? És una anotació.

Exemples aplicats a Rutas Norte:

Dada Tipus Per què
Component (api-reserves) Etiqueta Els Services i els ReplicaSets hi seleccionen
Entorn (pro) Etiqueta Es consulta constantment i les NetworkPolicies la faran servir
Versió (2.5.0) Etiqueta Útil per consultar quina versió corre on
Correu de l'equip responsable Anotació Es llegeix en un incident, no s'hi busca
Motiu de l'últim desplegament Anotació Text lliure i llarg
URL del quadre de comandament Anotació Conté : i /, prohibits en valors d'etiqueta
Hash del commit desplegat Anotació (o etiqueta curta) Sol ser informatiu
Contrasenya de la base de dades Cap de les dues Mai. Va en un Secret (03-02)

  1. Sintaxi i restriccions de claus i valors

Kubernetes és estricte amb el format, i conèixer les regles evita rebutjos incomprensibles.

Estructura d'una clau

Una clau té un nom i, opcionalment, un prefix separat per /:

app.kubernetes.io/component
└──────┬─────────┘ └───┬───┘
    prefix          nom

Regles del nom (la part obligatòria):

  • Màxim 63 caràcters.
  • Ha de començar i acabar per caràcter alfanumèric.
  • Pot contenir -, _, . i alfanumèrics enmig.

Regles del prefix (opcional):

  • Ha de ser un subdomini DNS vàlid: rutasnorte.example, app.kubernetes.io.
  • Màxim 253 caràcters.
  • Se separa del nom amb /.
  • Reservats: els prefixos kubernetes.io/ i k8s.io/ estan reservats per al mateix Kubernetes i els seus components principals. No els facis servir per a les teves etiquetes.

Regles del valor d'una etiqueta

  • Màxim 63 caràcters (o buit, que és vàlid).
  • Si no és buit, ha de començar i acabar per alfanumèric.
  • Només alfanumèrics, -, _ i . enmig.
  • Prohibits: espais, /, :, @, accents, ç, emojis.

Exemples vàlids i invàlids:

Etiqueta Vàlida? Motiu
app: api-reserves Format canònic
app.kubernetes.io/version: 2.5.0 Prefix estàndard, valor amb punts
rutasnorte.example/linia: BIL-SAN Prefix propi de l'empresa
entorn: pro Sense prefix, perfectament vàlid
equip: backend_reserves El guió baix està permès
responsable: [email protected] No L'@ està prohibida en valors
panell: https://grafana.example/d/abc No : i / prohibits. Fes servir una anotació
descripcio: API de reserves No Els espais estan prohibits
regió: nord No Els accents estan prohibits
kubernetes.io/la-meva-etiqueta: x No (desaconsellat) Prefix reservat

Comprova el rebuig tu mateix:

kubectl label pod --all [email protected] -n rutas-norte-dev
error: '[email protected]' is not a valid label value:
a valid label must be an empty string or consist of alphanumeric characters,
'-', '_' or '.', and must start and end with an alphanumeric character

La versió correcta fa servir una anotació:

kubectl annotate deployment api-reserves \
  rutasnorte.example/[email protected] -n rutas-norte-dev
deployment.apps/api-reserves annotated

Regles de les anotacions

Les claus segueixen les mateixes regles que les de les etiquetes. Els valors, en canvi, són gairebé lliures: qualsevol cadena, inclosos JSON, salts de línia i caràcters especials, amb un límit conjunt de 256 KiB per a totes les anotacions d'un objecte.

Aquell límit generós és el que permet que kubectl apply desi el manifest complet a kubectl.kubernetes.io/last-applied-configuration, com vam veure a Objectes i Manifests.

  1. Les etiquetes recomanades per Kubernetes

Kubernetes defineix un conjunt d'etiquetes comunes sota el prefix app.kubernetes.io/. No són obligatòries ni les imposa res, però són el vocabulari compartit que entenen Helm, Kustomize, Argo CD, Prometheus i pràcticament qualsevol eina de l'ecosistema.

Etiqueta Significat Exemple a Rutas Norte
app.kubernetes.io/name Nom de l'aplicació api-reserves
app.kubernetes.io/instance Identifica una instal·lació concreta d'aquella aplicació api-reserves-pro
app.kubernetes.io/version Versió actual 2.5.0
app.kubernetes.io/component Paper que juga dins de l'arquitectura api, frontend, cache, base-dades, worker
app.kubernetes.io/part-of Aplicació de nivell superior a la qual pertany rutas-norte
app.kubernetes.io/managed-by Eina que gestiona l'objecte kubectl, helm, argocd

La diferència entre name i instance és la que més costa:

  • name és què és: postgres. La mateixa en totes les instal·lacions.
  • instance és quin és: postgres-reserves-pro. Distingeix dues instal·lacions del mateix.

Si Rutas Norte tingués dues bases de dades PostgreSQL —una de reserves i una altra de facturació—, totes dues portarien name: postgres però instance: postgres-reserves i instance: postgres-facturacio.

Un exemple complet amb les sis:

metadata:
  name: api-reserves
  labels:
    app.kubernetes.io/name: api-reserves
    app.kubernetes.io/instance: api-reserves-pro
    app.kubernetes.io/version: "2.5.0"
    app.kubernetes.io/component: api
    app.kubernetes.io/part-of: rutas-norte
    app.kubernetes.io/managed-by: kubectl

Nota pràctica: version: "2.5.0" va entre cometes. Sense elles, YAML podria interpretar valors com 1.0 com a número i provocar un error de tipus, ja que els valors d'etiqueta han de ser cadenes.

Un advertiment important que enllaça amb l'apartat 9: app.kubernetes.io/version no ha d'anar al selector. Si hi fos, en canviar de versió el selector deixaria d'encaixar amb els seus propis pods, i com que el selector és immutable, hauries de recrear el Deployment a cada desplegament. La versió és informativa; la identitat la donen name i instance.

  1. L'esquema d'etiquetatge de Rutas Norte

Ha arribat el moment de fixar l'esquema definitiu del projecte. Fins ara hem fet servir tres etiquetes mínimes (app, app.kubernetes.io/part-of i entorn); les consolidem i les ampliem.

Les etiquetes del projecte

Etiqueta Obligatòria Valors possibles Per a què
app botiga-web, api-reserves, postgres-reserves, redis-cache, worker-notificacions, informes-ocupacio Identificador curt del component. Va als selectors
entorn dev, pre, pro Entorn. Va als selectors i a les NetworkPolicies
app.kubernetes.io/name Igual que app Compatibilitat amb l'ecosistema
app.kubernetes.io/part-of rutas-norte Agrupa tota la plataforma. Mai en un selector
app.kubernetes.io/component frontend, api, base-dades, cache, worker, informes Paper arquitectònic
app.kubernetes.io/version 1.8.0, 2.5.0... Versió desplegada. Mai en un selector
app.kubernetes.io/managed-by Recomanada kubectl, argocd Qui gestiona l'objecte
rutasnorte.example/criticitat Recomanada critica, alta, mitjana, baixa Prioritza la resposta davant d'incidents

La regla d'or de l'esquema, que es deriva de tot el que hem après al mòdul:

Només app i entorn entren als selectors. Són les dues úniques etiquetes que identifiquen el component de forma inequívoca i que no canvien mai.

Tota la resta —versió, component, criticitat, gestor— és informativa i pot canviar sense recrear res.

La taula mestra: sis components

Component app component version criticitat Porta Service?
botiga-web botiga-web frontend 1.8.0 alta
api-reserves api-reserves api 2.4.0 critica
postgres-reserves postgres-reserves base-dades 16.2 critica Sí (headless al mòdul 6)
redis-cache redis-cache cache 7.2 mitjana
worker-notificacions worker-notificacions worker 1.2.0 alta No
informes-ocupacio informes-ocupacio informes 1.0.3 baixa No

El manifest resultant

Així queda api-reserves amb l'esquema complet aplicat:

# k8s/base/api-reserves-deployment.yaml (metadades completes)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-reserves
  namespace: rutas-norte-dev
  labels:
    app: api-reserves
    entorn: dev
    app.kubernetes.io/name: api-reserves
    app.kubernetes.io/instance: api-reserves-dev
    app.kubernetes.io/version: "2.4.0"
    app.kubernetes.io/component: api
    app.kubernetes.io/part-of: rutas-norte
    app.kubernetes.io/managed-by: kubectl
    rutasnorte.example/criticitat: critica
  annotations:
    kubernetes.io/change-cause: "v2.4.0 - desplegament inicial"
    rutasnorte.example/responsable: [email protected]
    rutasnorte.example/panell: "https://grafana.rutasnorte.example/d/api-reserves"
    rutasnorte.example/runbook: "https://wiki.rutasnorte.example/runbooks/api-reserves"
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api-reserves        # NOMES aquestes dues.
      entorn: dev              # Immutables.
  template:
    metadata:
      labels:
        app: api-reserves
        entorn: dev
        app.kubernetes.io/name: api-reserves
        app.kubernetes.io/instance: api-reserves-dev
        app.kubernetes.io/version: "2.4.0"
        app.kubernetes.io/component: api
        app.kubernetes.io/part-of: rutas-norte
        app.kubernetes.io/managed-by: kubectl
        rutasnorte.example/criticitat: critica
    spec:
      containers:
        - name: api
          image: node:20-alpine
          # ...

Fixa't en l'asimetria, que és exactament la que vam aprendre amb els ReplicaSets: el template porta nou etiquetes i el selector només n'exigeix dues. Està permès i és el correcte: el selector ha de ser un subconjunt mínim i estable; la resta d'etiquetes viatgen amb els pods per poder-los consultar.

Aplica'l i observa la potència immediata:

kubectl apply -f k8s/base/api-reserves-deployment.yaml
kubectl get pods -l app.kubernetes.io/component=api --show-labels
NAME                           READY   STATUS    LABELS
api-reserves-9c4d7e831-j2wsk   1/1     Running   app=api-reserves,app.kubernetes.io/component=api,app.kubernetes.io/instance=api-reserves-dev,app.kubernetes.io/managed-by=kubectl,app.kubernetes.io/name=api-reserves,app.kubernetes.io/part-of=rutas-norte,app.kubernetes.io/version=2.4.0,entorn=dev,pod-template-hash=9c4d7e831,rutasnorte.example/criticitat=critica

  1. Selectors d'igualtat

Un selector és una consulta sobre etiquetes. El tipus més simple compara valors.

Operador Significat Exemple
= Igual app=api-reserves
== Igual (sinònim exacte de =) app==api-reserves
!= Diferent entorn!=pro

Es fan servir amb -l (o --selector), i diverses condicions separades per comes es combinen amb I lògic:

# Els pods d'api-reserves
kubectl get pods -l app=api-reserves

# Els pods d'api-reserves A desenvolupament (I logic)
kubectl get pods -l app=api-reserves,entorn=dev

# Tots els pods de la plataforma que NO son a produccio
kubectl get pods -A -l app.kubernetes.io/part-of=rutas-norte,entorn!=pro
NAMESPACE         NAME                           READY   STATUS    AGE
rutas-norte-dev   api-reserves-9c4d7e831-j2wsk   1/1     Running   5m
rutas-norte-dev   botiga-web-6f9c4b8d7-42kxr     1/1     Running   1h
rutas-norte-dev   redis-cache-6c8f9d745-w2mtb    1/1     Running   1h
rutas-norte-pre   api-reserves-7f1d3e942-c9plk   1/1     Running   45m
rutas-norte-pre   botiga-web-6f9c4b8d7-d3jbx     1/1     Running   45m

Un detall sobre != que convé conèixer: també selecciona els objectes que no tenen aquella etiqueta en absolut. entorn!=pro retorna tant els de dev i pre com els que no tenen l'etiqueta entorn.

No existeix l'O lògic als selectors d'igualtat. Per a això hi ha els de conjunt.

  1. Selectors de conjunt

Més expressius. Operen sobre conjunts de valors i sobre l'existència de la clau.

Operador Significat Exemple
in El valor és a la llista entorn in (pre,pro)
notin El valor no és a la llista entorn notin (dev)
<clau> L'etiqueta existeix, amb qualsevol valor rutasnorte.example/criticitat
!<clau> L'etiqueta no existeix !rutasnorte.example/criticitat

Exemples aplicats:

# Els components dels entorns "seriosos" (O logic)
kubectl get pods -A -l 'entorn in (pre,pro)'

# Tot el que NO es de desenvolupament
kubectl get deploy -A -l 'entorn notin (dev)'

# Objectes als quals s'ha oblidat la criticitat: auditoria de l'esquema
kubectl get deploy -A -l 'app.kubernetes.io/part-of=rutas-norte,!rutasnorte.example/criticitat'

# Components critics o d'alta criticitat, en qualsevol entorn
kubectl get pods -A -l 'rutasnorte.example/criticitat in (critica,alta)'
NAMESPACE         NAME                                  READY   STATUS    AGE
rutas-norte-dev   api-reserves-9c4d7e831-j2wsk          1/1     Running   12m
rutas-norte-dev   api-reserves-9c4d7e831-p7xtr          1/1     Running   12m
rutas-norte-dev   botiga-web-6f9c4b8d7-42kxr            1/1     Running   1h
rutas-norte-dev   worker-notificacions-8c7b5d94f-k2xrt  1/1     Running   1h

Nota sobre les cometes: els selectors de conjunt porten parèntesis i espais, que la shell interpretaria. Tanca'ls sempre entre cometes simples.

I es poden combinar amb els d'igualtat, separant per comes:

kubectl get pods -A -l 'app.kubernetes.io/part-of=rutas-norte,entorn in (pre,pro),app.kubernetes.io/component!=cache'

  1. Selectors en manifests: matchLabels i matchExpressions

Dins d'un YAML, els selectors tenen dues formes equivalents a les dues famílies que acabes de veure.

matchLabels: igualtat

selector:
  matchLabels:
    app: api-reserves
    entorn: dev

És un mapa: totes les claus han de coincidir (I lògic). És el que hem fet servir a tot el mòdul.

matchExpressions: conjunts

selector:
  matchExpressions:
    - key: app
      operator: In
      values: [api-reserves]
    - key: entorn
      operator: In
      values: [pre, pro]
    - key: rutasnorte.example/obsolet
      operator: DoesNotExist
operator Equival a Requereix values?
In in (…)
NotIn notin (…)
Exists <clau> No
DoesNotExist !<clau> No

Combinar-los

Es poden fer servir tots dos alhora; totes les condicions es combinen amb I lògic:

selector:
  matchLabels:
    app: api-reserves
  matchExpressions:
    - key: entorn
      operator: In
      values: [pre, pro]

Traducció: "pods amb app=api-reserves i amb entorn igual a pre o a pro".

Quin fer servir

Cas Recomanació
Selector d'un Deployment, ReplicaSet o StatefulSet matchLabels: simple, llegible, i el selector ha de ser mínim
Selector d'un Service Obligatòriament mapa pla: no admet cap de les dues formes, només selector: directe
NetworkPolicy que abasta diversos entorns o n'exclou alguna cosa matchExpressions
Afinitat de pods matchExpressions

Atenció a l'excepció del Service, que ja vam veure a la seva lliçó i convé recordar aquí perquè és una asimetria real de l'API:

# Service: mapa pla, SENSE matchLabels
kind: Service
spec:
  selector:
    app: api-reserves
    entorn: dev
# Deployment: AMB matchLabels
kind: Deployment
spec:
  selector:
    matchLabels:
      app: api-reserves
      entorn: dev

El motiu és històric: els Services són del grup core v1, anterior al selector enriquit, i només admeten igualtat.

  1. Qui consumeix selectors

Els selectors no són només per a consultes manuals. Són el mecanisme d'acoblament de tot Kubernetes: la manera com uns objectes en troben uns altres sense conèixer-ne els noms.

Qui Què selecciona Lliçó
ReplicaSet Els pods que ha de mantenir i comptar 02-02
Deployment Els pods dels seus ReplicaSets (amb pod-template-hash afegit) 02-03
Service Els pods als quals enruta el trànsit 02-05
StatefulSet i DaemonSet Els seus pods 06-01, 06-02
Job i CronJob Els pods de les seves execucions 06-03
NetworkPolicy Quins pods protegeix i de qui accepta trànsit 04-06
Afinitat i antiafinitat de pods Al costat de quins pods col·locar-se o no 06-05
PodDisruptionBudget Quins pods protegeix dels desallotjaments voluntaris 09-05
HorizontalPodAutoscaler Indirectament, via l'objecte que escala 09-01
Prometheus (ServiceMonitor) De quins pods recollir mètriques 07-03
kubectl -l El que tu vulguis 01-05
flowchart TB
    POD["Pods amb etiquetes<br/>app=api-reserves<br/>entorn=dev"]
    RS["ReplicaSet<br/>els mante"]
    SVC["Service<br/>els enruta transit"]
    NP["NetworkPolicy<br/>els protegeix"]
    PDB["PodDisruptionBudget<br/>evita desallotjar-los tots"]
    MON["ServiceMonitor<br/>recull les seves metriques"]
    RS -->|selector| POD
    SVC -->|selector| POD
    NP -->|podSelector| POD
    PDB -->|selector| POD
    MON -->|selector| POD

La lliçó de fons: les etiquetes d'un pod no són decoració, són la seva interfície amb la resta del sistema. Canviar una etiqueta d'un pod el pot treure d'un Service, deixar-lo fora d'una política de xarxa o fer-lo invisible per al seu ReplicaSet. Ho veurem a l'exercici 3.

  1. La regla d'or: el selector d'un Deployment és immutable

Comprova-ho:

kubectl patch deployment api-reserves --type=merge \
  -p '{"spec":{"selector":{"matchLabels":{"app":"api-reserves","entorn":"dev","nou":"si"}}}}'
The Deployment "api-reserves" is invalid: spec.selector: Invalid value:
field is immutable

El mateix passa amb ReplicaSets, StatefulSets i DaemonSets. Els Services són l'excepció: el seu selector sí que es pot modificar, i això els converteix en l'eina de les estratègies blue-green del mòdul 11.

Per què és immutable

La raó és la seguretat de les dades... dels pods. Imagina que es permetés canviar-lo:

  1. El Deployment té selector: app=api-reserves i governa 4 pods amb aquella etiqueta.
  2. Canvies el selector a app=api-reserves-v2.
  3. Instantàniament, els 4 pods deixen d'encaixar. Es converteixen en orfes, exactament com els de l'experiment --cascade=orphan de la lliçó de ReplicaSets.
  4. El Deployment compta 0 pods propis i en crea 4 de nous.
  5. Resultat: 8 pods corrent, 4 d'ells sense controlador, invisibles per al Deployment, consumint recursos i sense ningú que els actualitzi ni els recuperi si cauen.

Pitjor encara: si el Service continuava apuntant a app=api-reserves, aquells 4 orfes continuarien rebent trànsit de producció sense que cap controlador els vigili.

Kubernetes prefereix fallar en aplicar abans que deixar-te arribar a aquell estat.

Com canviar un selector quan de debò cal

Hi ha dos camins, i el primer és el bo:

Camí A: substituir el controlador sense tallar el servei.

# 1. Esborrar el Deployment deixant vius els pods
kubectl delete deployment api-reserves --cascade=orphan

# 2. Aplicar el manifest nou, amb el selector corregit
kubectl apply -f k8s/base/api-reserves-deployment.yaml

# 3. Comprovar. Si el selector nou encaixa amb els pods vells, els adopta;
#    si no, crea els seus i despres cal netejar els orfes
kubectl get pods -l app.kubernetes.io/part-of=rutas-norte -n rutas-norte-dev

Camí B: nom nou i transició pel Service. Es crea un Deployment amb un altre nom i el selector correcte, s'espera que estigui llest i es canvia el selector del Service perquè apunti als pods nous. És, en essència, un desplegament blue-green (11-04).

La conseqüència pràctica

Pensa bé el selector abans del primer apply. És la decisió menys reversible d'un manifest.

I d'aquí la regla del projecte que ja coneixes: al selector, només app i entorn. Mai la versió, mai el gestor, mai la criticitat, mai part-of. Tot el que pugui canviar amb el temps es queda fora.

  1. Anotacions a la pràctica

Les anotacions semblen menys importants que les etiquetes fins que descobreixes quantes coses funcionen gràcies a elles.

Anotacions que ja has fet servir

kubectl get deployment api-reserves -o jsonpath='{.metadata.annotations}' | python3 -m json.tool
{
    "deployment.kubernetes.io/revision": "3",
    "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",...}",
    "kubernetes.io/change-cause": "v2.4.0 - desplegament inicial",
    "rutasnorte.example/responsable": "[email protected]"
}
Anotació Qui la posa Per a què
kubernetes.io/change-cause Tu o el teu CI/CD Apareix a kubectl rollout history (02-04)
deployment.kubernetes.io/revision El controlador de Deployment Número de revisió de cada ReplicaSet
kubectl.kubernetes.io/last-applied-configuration kubectl apply Base de la fusió de tres vies (01-06)
kubectl.kubernetes.io/restartedAt kubectl rollout restart Força un canvi al template per recrear els pods

Anotacions que configuren comportament

Aquest és l'ús més potent: molts controladors es configuren mitjançant anotacions, no mitjançant camps del spec. El cas paradigmàtic és el controlador d'Ingress:

# Avancament del modul 4: NO ho apliquis encara
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rutas-norte
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "8m"
    nginx.ingress.kubernetes.io/rate-limit-rps: "50"
    cert-manager.io/cluster-issuer: "letsencrypt-produccio"

Cap d'aquelles línies és a l'esquema de l'Ingress: són instruccions que el controlador d'NGINX i cert-manager llegeixen i apliquen. El motiu d'aquest disseny és que cada implementació d'Ingress té capacitats diferents, i l'API estàndard no les pot cobrir totes. S'estudien a Controladors d'Ingress i TLS i Certificats.

Anotacions per a humans

I l'ús més simple i més rendible: informació que salva una guàrdia nocturna.

metadata:
  annotations:
    rutasnorte.example/responsable: "[email protected]"
    rutasnorte.example/guardia: "+34 600 000 000"
    rutasnorte.example/panell: "https://grafana.rutasnorte.example/d/api-reserves"
    rutasnorte.example/runbook: "https://wiki.rutasnorte.example/runbooks/api-reserves"
    rutasnorte.example/repositori: "https://git.rutasnorte.example/rutas-norte/api-reserves"
    rutasnorte.example/commit: "a3f9c1b4e7d2"
    rutasnorte.example/descripcio: |
      API REST de reserves. Consulta disponibilitat a redis-cache i
      persisteix a postgres-reserves. Dependencies critiques: totes dues.
      Davant de 5xx sostinguts, revisar primer la connexio a postgres-reserves.

Fixa't en dues coses impossibles amb etiquetes: valors amb @, : i /, i un valor multilínia. Aquesta és la raó que existeixin les anotacions.

A les tres de la matinada, això val el seu pes en or:

kubectl get deploy api-reserves -o jsonpath='{.metadata.annotations.rutasnorte\.example/runbook}{"\n"}'

La regla innegociable: mai secrets a les anotacions

Mai no guardis contrasenyes, tokens, claus d'API ni certificats privats a les anotacions.

Els motius:

  • Es llegeixen en clar amb kubectl get -o yaml o kubectl describe, que solen ser permisos àmpliament concedits.
  • Apareixen als logs: qualsevol bolcat d'un objecte les inclou.
  • Es copien a last-applied-configuration, i queden duplicades.
  • Se'n van als backups i a les eines de GitOps, propagant-se arreu.
  • No es xifren en repòs dins d'etcd tret que el clúster ho tingui configurat, i tot i així el mecanisme està pensat per a Secrets.

El mateix val per a les etiquetes, amb l'agreujant que a més són indexades i consultables.

Per a això hi ha els Secrets, a la lliçó 03-02, i la seva gestió segura al mòdul 8. Aquell POSTGRES_PASSWORD en clar que vam deixar al Deployment provisional de postgres-reserves és exactament el deute que pagarem al mòdul següent.

  1. kubectl label i kubectl annotate

Totes dues ordres comparteixen sintaxi.

Afegir

kubectl label deployment api-reserves rutasnorte.example/criticitat=critica
kubectl annotate deployment api-reserves rutasnorte.example/guardia="+34 600 000 000"
deployment.apps/api-reserves labeled
deployment.apps/api-reserves annotated

Sobreescriure: --overwrite

Si la clau ja existeix, l'ordre falla tret que ho indiquis:

kubectl label deployment api-reserves rutasnorte.example/criticitat=alta
error: 'rutasnorte.example/criticitat' already has a value (critica),
and --overwrite is false
kubectl label deployment api-reserves rutasnorte.example/criticitat=alta --overwrite
deployment.apps/api-reserves labeled

És una protecció deliberada contra trepitjar sense voler una etiqueta que algun selector estigui fent servir.

Esborrar: el sufix -

kubectl label deployment api-reserves rutasnorte.example/criticitat-
kubectl annotate deployment api-reserves rutasnorte.example/guardia-
deployment.apps/api-reserves unlabeled
deployment.apps/api-reserves annotated

Aquell guió final és fàcil de passar per alt en llegir un script. Recorda-ho: clau amb guió al final significa esborrar.

Operacions massives

# A diversos objectes pel nom
kubectl label pods pod-a pod-b prova=si

# A TOTS els objectes d'un tipus al namespace
kubectl label pods --all revisat=2026-08-05

# Filtrant per un altre selector
kubectl label deploy -l app.kubernetes.io/part-of=rutas-norte \
  app.kubernetes.io/managed-by=kubectl --overwrite

# A tots els namespaces
kubectl label ns -l app.kubernetes.io/part-of=rutas-norte auditat=si

Advertiment de seguretat: --all combinat amb --overwrite sobre una etiqueta que apareix en algun selector pot treure pods del seu Service o del seu ReplicaSet a l'acte. Prova sempre primer amb --dry-run=server:

kubectl label pods --all entorn=pre --dry-run=server

Un advertiment sobre les etiquetes posades a mà

Canviar una etiqueta amb kubectl label sobre un pod gestionat per un Deployment no sobreviu: al pròxim desplegament, els pods es recreen a partir del template i el canvi desapareix. Perquè perduri, edita spec.template.metadata.labels al manifest.

I hi ha un efecte secundari molt més greu, que explorarem a l'exercici 3: si canvies una etiqueta que és al selector del ReplicaSet, el pod queda orfe a l'instant i el ReplicaSet en crea un altre per substituir-lo.

  1. Consultes útils del dia a dia

Aquí és on l'esquema d'etiquetatge deixa de ser burocràcia i es converteix en productivitat. Les consultes més rendibles combinen -l amb els formats de sortida que vam veure a La CLI de Kubernetes.

Inventari de la plataforma

kubectl get pods -A -l app.kubernetes.io/part-of=rutas-norte \
  -o custom-columns='ENTORN:.metadata.labels.entorn,COMPONENT:.metadata.labels.app,VERSIO:.metadata\.labels.app\.kubernetes\.io/version,POD:.metadata.name,ESTAT:.status.phase'
ENTORN   COMPONENT               VERSIO    POD                                    ESTAT
dev      api-reserves            2.4.0     api-reserves-9c4d7e831-j2wsk           Running
dev      api-reserves            2.4.0     api-reserves-9c4d7e831-p7xtr           Running
dev      botiga-web              1.8.0     botiga-web-6f9c4b8d7-42kxr             Running
dev      redis-cache             7.2       redis-cache-6c8f9d745-w2mtb            Running
pre      api-reserves            2.4.0     api-reserves-7f1d3e942-c9plk           Running
pro      botiga-web              1.8.0     botiga-web-6f9c4b8d7-x4nbc             Running

Truc de sintaxi important: a custom-columns, els punts que formen part de la clau de l'etiqueta cal escapar-los amb \.. Si no, kubectl els interpreta com a separadors de ruta JSONPath i no troba res.

Quina versió corre a cada entorn

La consulta que més vegades et demanaran:

kubectl get deploy -A -l app=api-reserves \
  -o custom-columns='NS:.metadata.namespace,DEPLOY:.metadata.name,IMATGE:.spec.template.spec.containers[0].image,REPLIQUES:.spec.replicas'
NS                DEPLOY         IMATGE           REPLIQUES
rutas-norte-dev   api-reserves   node:20-alpine   2
rutas-norte-pre   api-reserves   node:20-alpine   2

Auditoria de l'esquema d'etiquetatge

Trobar el que incompleix la convenció del projecte:

# Deployments sense l'etiqueta part-of: s'han escapat de l'esquema
kubectl get deploy -A -l '!app.kubernetes.io/part-of'

# Objectes de la plataforma sense criticitat assignada
kubectl get deploy,sts,ds -A -l 'app.kubernetes.io/part-of=rutas-norte,!rutasnorte.example/criticitat'

# Pods sense l'etiqueta d'entorn: no els abastaria cap NetworkPolicy
kubectl get pods -A -l 'app.kubernetes.io/part-of=rutas-norte,!entorn'

Consultes d'incident

# Tot el que es critic i NO esta corrent
kubectl get pods -A -l 'rutasnorte.example/criticitat=critica' \
  --field-selector status.phase!=Running

# Logs agregats de totes les repliques d'un component, amb prefix de pod
kubectl logs -l app=api-reserves -n rutas-norte-pro --tail=50 --prefix

# Ordenar per reinicis: qui esta patint
kubectl get pods -A -l app.kubernetes.io/part-of=rutas-norte \
  --sort-by=.status.containerStatuses[0].restartCount

# Els esdeveniments recents del namespace, el primer que cal mirar
kubectl get events -n rutas-norte-pro --sort-by=.lastTimestamp | tail -20

Operacions en bloc

# Reiniciar tots els components sense estat d'un entorn
kubectl rollout restart deploy -l 'app.kubernetes.io/part-of=rutas-norte' -n rutas-norte-dev

# Esborrar TOT un entorn de proves (amb compte)
kubectl delete all -l 'app.kubernetes.io/part-of=rutas-norte' -n rutas-norte-dev --dry-run=client

Aquell --dry-run=client abans d'un delete massiu és un hàbit que convé adquirir.

Combinacions amb -o wide i --show-labels

# Veure on es cada cosa
kubectl get pods -A -l entorn=pro -o wide

# Veure totes les etiquetes d'un objecte
kubectl get deploy api-reserves --show-labels

# Agrupar visualment per una etiqueta
kubectl get pods -A -L entorn,app.kubernetes.io/component

El flag -L (ela majúscula) és un petit tresor: afegeix columnes amb el valor de les etiquetes indicades, sense filtrar res.

NAMESPACE         NAME                           READY   STATUS    AGE   ENTORN   COMPONENT
rutas-norte-dev   api-reserves-9c4d7e831-j2wsk   1/1     Running   30m   dev      api
rutas-norte-dev   botiga-web-6f9c4b8d7-42kxr     1/1     Running   2h    dev      frontend
rutas-norte-dev   redis-cache-6c8f9d745-w2mtb    1/1     Running   2h    dev      cache
rutas-norte-pre   api-reserves-7f1d3e942-c9plk   1/1     Running   1h    pre      api

Errors Comuns i Consells

  • Ficar la versió al selector. En desplegar una versió nova el selector deixa d'encaixar, i com que és immutable, t'obliga a recrear el Deployment. El selector només porta app i entorn.
  • Selectors massa amplis. Un app.kubernetes.io/part-of: rutas-norte com a selector adopta i pot esborrar pods d'altres components, com vam veure a ReplicaSets.
  • Fer servir caràcters prohibits en valors d'etiqueta. Res d'@, :, /, espais ni accents. Si la dada els necessita, és una anotació.
  • Fer servir el prefix kubernetes.io/ per a etiquetes pròpies. Està reservat. Fes servir el domini de la teva organització: rutasnorte.example/.
  • Guardar secrets a les anotacions o les etiquetes. Es llegeixen en clar amb describe, viatgen als backups i acaben a Git. Van en Secrets.
  • Posar les etiquetes només a metadata i no a spec.template.metadata. Els pods hereten les del template, no les del Deployment. És l'error que trenca els Services.
  • Oblidar --overwrite i pensar que l'ordre ha funcionat. kubectl label falla si la clau existeix. Llegeix sempre la sortida.
  • Canviar amb kubectl label una etiqueta que és en un selector. El pod queda orfe i el controlador en crea un altre. Pot duplicar capacitat sense que te n'adonis.
  • Esperar que un canvi amb kubectl label sobre un pod perduri. Es perd al pròxim desplegament. Edita el template.
  • Oblidar les cometes simples als selectors de conjunt. Els parèntesis i els espais els interpreta la shell.
  • No escapar els punts de les claus a custom-columns. app.kubernetes.io/version s'ha d'escriure app\.kubernetes\.io/version.
  • Consell: defineix l'esquema d'etiquetatge abans de desplegar res. Afegir-lo després implica editar tots els manifests i, si toca selectors, recrear objectes.
  • Consell: kubectl get pods -A -L entorn,app et dona una vista tabulada de tota la plataforma sense filtrar. És la consulta amb què començar qualsevol revisió.
  • Consell: documenta l'esquema a k8s/README.md del repositori. Un esquema que només coneix qui el va dissenyar no és un esquema.

Exercicis

Exercici 1: Aplicar l'esquema complet a botiga-web

  1. Actualitza k8s/base/botiga-web-deployment.yaml perquè el Deployment i el seu template portin les vuit etiquetes de l'esquema de Rutas Norte (app, entorn, les cinc d'app.kubernetes.io/ i criticitat), i quatre anotacions: responsable, panell, runbook i change-cause.
  2. Assegura't que el selector continua tenint només app i entorn, i explica en dues línies per què.
  3. Aplica'l i demostra amb una ordre que els pods porten les vuit etiquetes.
  4. Recupera l'URL del runbook amb una sola ordre.

Exercici 2: Consultes sobre la plataforma

Amb els tres entorns desplegats, escriu l'ordre exacta per a cada pregunta:

  1. Tots els pods de la plataforma a pre o pro, en qualsevol namespace.
  2. Tots els Deployments de la plataforma que no són de desenvolupament.
  3. Els pods de components amb criticitat critica o alta que no estan en estat Running.
  4. Una taula amb entorn, component, versió i node de tots els pods de la plataforma.
  5. Els Deployments als quals els falta l'etiqueta rutasnorte.example/criticitat (auditoria de l'esquema).
  6. Tots els pods, sense filtrar, mostrant entorn i app.kubernetes.io/component com a columnes addicionals.

Exercici 3: L'experiment del pod orfe

Aquest exercici demostra per què les etiquetes són la interfície d'un pod amb la resta del sistema. Treballa a rutas-norte-dev amb el Deployment de botiga-web (3 rèpliques) i el seu Service.

  1. Apunta quants pods hi ha i quants endpoints té el Service.
  2. Tria un pod i canvia-li l'etiqueta app a botiga-web-aillat amb kubectl label --overwrite.
  3. Observa immediatament: quants pods hi ha ara? Què ha fet el ReplicaSet? Continua viu el pod modificat? Qui n'és el propietari?
  4. Comprova quants endpoints té ara el Service i explica per què.
  5. Explica per a què serveix aquesta tècnica al món real i quin perill té.
  6. Deixa el clúster com estava.

Solucions

Solució 1

# k8s/base/botiga-web-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: botiga-web
  namespace: rutas-norte-dev
  labels:
    app: botiga-web
    entorn: dev
    app.kubernetes.io/name: botiga-web
    app.kubernetes.io/instance: botiga-web-dev
    app.kubernetes.io/version: "1.8.0"
    app.kubernetes.io/component: frontend
    app.kubernetes.io/part-of: rutas-norte
    app.kubernetes.io/managed-by: kubectl
    rutasnorte.example/criticitat: alta
  annotations:
    kubernetes.io/change-cause: "v1.8.0 - esquema d'etiquetatge complet (RN-495)"
    rutasnorte.example/responsable: "[email protected]"
    rutasnorte.example/panell: "https://grafana.rutasnorte.example/d/botiga-web"
    rutasnorte.example/runbook: "https://wiki.rutasnorte.example/runbooks/botiga-web"
spec:
  replicas: 3
  revisionHistoryLimit: 5
  minReadySeconds: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: botiga-web
      entorn: dev
  template:
    metadata:
      labels:
        app: botiga-web
        entorn: dev
        app.kubernetes.io/name: botiga-web
        app.kubernetes.io/instance: botiga-web-dev
        app.kubernetes.io/version: "1.8.0"
        app.kubernetes.io/component: frontend
        app.kubernetes.io/part-of: rutas-norte
        app.kubernetes.io/managed-by: kubectl
        rutasnorte.example/criticitat: alta
      annotations:
        rutasnorte.example/responsable: "[email protected]"
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
            limits:
              cpu: "200m"
              memory: "128Mi"
  1. El selector porta només app i entorn perquè és immutable i ha d'identificar el component de forma estable. Si inclogués app.kubernetes.io/version, en desplegar la 1.9.0 el selector deixaria d'encaixar amb els pods nous, i com que no es pot modificar, caldria esborrar i recrear el Deployment a cada desplegament. A més, un selector mínim evita el risc d'adopcions indegudes si un altre objecte comparteix etiquetes informatives.
kubectl apply -f k8s/base/botiga-web-deployment.yaml
kubectl get pods -l app=botiga-web -o jsonpath='{.items[0].metadata.labels}' | python3 -m json.tool
{
    "app": "botiga-web",
    "app.kubernetes.io/component": "frontend",
    "app.kubernetes.io/instance": "botiga-web-dev",
    "app.kubernetes.io/managed-by": "kubectl",
    "app.kubernetes.io/name": "botiga-web",
    "app.kubernetes.io/part-of": "rutas-norte",
    "app.kubernetes.io/version": "1.8.0",
    "entorn": "dev",
    "pod-template-hash": "5b7c9f4d8",
    "rutasnorte.example/criticitat": "alta"
}

Les vuit de l'esquema, més el pod-template-hash que hi afegeix el Deployment.

kubectl get deploy botiga-web -o jsonpath='{.metadata.annotations.rutasnorte\.example/runbook}{"\n"}'
https://wiki.rutasnorte.example/runbooks/botiga-web

Solució 2

# 1. Pods de la plataforma a pre o pro
kubectl get pods -A -l 'app.kubernetes.io/part-of=rutas-norte,entorn in (pre,pro)'

# 2. Deployments que no son de desenvolupament
kubectl get deploy -A -l 'app.kubernetes.io/part-of=rutas-norte,entorn notin (dev)'

# 3. Critics o alts que no estan Running
kubectl get pods -A -l 'rutasnorte.example/criticitat in (critica,alta)' \
  --field-selector status.phase!=Running

# 4. Taula d'inventari
kubectl get pods -A -l app.kubernetes.io/part-of=rutas-norte \
  -o custom-columns='ENTORN:.metadata.labels.entorn,COMPONENT:.metadata.labels.app,VERSIO:.metadata.labels.app\.kubernetes\.io/version,NODE:.spec.nodeName'

# 5. Auditoria: sense criticitat
kubectl get deploy -A -l 'app.kubernetes.io/part-of=rutas-norte,!rutasnorte.example/criticitat'

# 6. Columnes addicionals sense filtrar
kubectl get pods -A -L entorn,app.kubernetes.io/component
# Sortida de la 4
ENTORN   COMPONENT      VERSIO   NODE
dev      api-reserves   2.4.0    rutas-norte
dev      api-reserves   2.4.0    rutas-norte
dev      botiga-web     1.8.0    rutas-norte
dev      redis-cache    7.2      rutas-norte
pre      api-reserves   2.4.0    rutas-norte
pro      botiga-web     1.8.0    rutas-norte

Nota sobre la pregunta 3: -l filtra per etiquetes i --field-selector per camps de l'objecte. Són mecanismes diferents i complementaris; els camps disponibles per a --field-selector són limitats (status.phase, spec.nodeName, metadata.name, metadata.namespace i poca cosa més).

Solució 3

# 1. Situacio de partida
kubectl get pods -l app=botiga-web -n rutas-norte-dev
kubectl get endpoints botiga-web -n rutas-norte-dev
NAME                         READY   STATUS    RESTARTS   AGE
botiga-web-5b7c9f4d8-6kdnp   1/1     Running   0          10m
botiga-web-5b7c9f4d8-q3xwt   1/1     Running   0          10m
botiga-web-5b7c9f4d8-z8mrv   1/1     Running   0          10m

NAME         ENDPOINTS                                      AGE
botiga-web   10.244.0.81:80,10.244.0.82:80,10.244.0.83:80   2h

3 pods, 3 endpoints.

# 2. Canviar l'etiqueta del selector en un pod
kubectl label pod botiga-web-5b7c9f4d8-6kdnp app=botiga-web-aillat --overwrite -n rutas-norte-dev

# 3. Observar
kubectl get pods -n rutas-norte-dev -l app.kubernetes.io/part-of=rutas-norte,app.kubernetes.io/component=frontend
pod/botiga-web-5b7c9f4d8-6kdnp labeled

NAME                         READY   STATUS    RESTARTS   AGE
botiga-web-5b7c9f4d8-6kdnp   1/1     Running   0          11m
botiga-web-5b7c9f4d8-h7pnk   1/1     Running   0          4s
botiga-web-5b7c9f4d8-q3xwt   1/1     Running   0          11m
botiga-web-5b7c9f4d8-z8mrv   1/1     Running   0          11m

Ara hi ha 4 pods. El que ha passat, pas a pas:

  • En canviar app de botiga-web a botiga-web-aillat, el pod va deixar d'encaixar amb el selector del ReplicaSet.
  • A la volta següent del seu bucle de reconciliació, el ReplicaSet va comptar 2 pods propis on n'hi havia d'haver 3 i en va crear un de nou (h7pnk, amb 4 segons d'edat).
  • El pod modificat continua viu, però ha quedat orfe: cap controlador no el vigila.
kubectl get pod botiga-web-5b7c9f4d8-6kdnp -n rutas-norte-dev \
  -o jsonpath='{.metadata.ownerReferences}{"\n"}'

Sortida buida: no té propietari. Kubernetes li va retirar les ownerReferences en deixar d'encaixar amb el selector.

# 4. Endpoints del Service
kubectl get endpoints botiga-web -n rutas-norte-dev
NAME         ENDPOINTS                                      AGE
botiga-web   10.244.0.82:80,10.244.0.83:80,10.244.0.87:80   2h

Continuen sent 3, però un ha canviat. El pod aïllat (10.244.0.81) ha sortit del Service perquè el seu selector també exigeix app=botiga-web; al seu lloc hi ha entrat el pod nou (10.244.0.87). El pod orfe ja no rep trànsit encara que continuï perfectament viu i sa.

  1. Per a què serveix al món real: és la tècnica clàssica d'aïllament per depurar en producció. Si un de cinc pods presenta un comportament anòmal —una fuita de memòria, respostes erràtiques, un log sospitós—, canviar-li una etiqueta del selector el treu del Service i del ReplicaSet en un instant. Deixa de rebre trànsit de clients reals, el ReplicaSet crea un substitut per no perdre capacitat, i tu conserves el pod intacte, amb la seva memòria i el seu estat, per inspeccionar-lo amb calma: kubectl exec, kubectl logs, bolcats, perfilat. No cal reproduir el problema en un entorn de proves: tens el pacient viu sobre la taula.

    El perill és doble. Primer, aquell pod ja no el gestiona ningú: si el node cau o algú el desallotja, desapareix sense substitut, i sobretot és fàcil oblidar-se'n, consumint CPU i memòria indefinidament. Segon, si el canvi d'etiqueta es fa per error o en sentit contrari —donar a un pod solt les etiquetes d'un component governat—, provoques l'adopció i el possible esborrat immediat que vam veure a la lliçó de ReplicaSets. La disciplina obligatòria és etiquetar el pod aïllat amb la data i el motiu, i esborrar-lo en acabar:

kubectl label pod botiga-web-5b7c9f4d8-6kdnp \
  rutasnorte.example/aillat-el=2026-08-05 \
  rutasnorte.example/incident=RN-501 -n rutas-norte-dev
# 6. Neteja
kubectl delete pod botiga-web-5b7c9f4d8-6kdnp -n rutas-norte-dev
kubectl get pods -l app=botiga-web -n rutas-norte-dev
kubectl get endpoints botiga-web -n rutas-norte-dev
pod "botiga-web-5b7c9f4d8-6kdnp" deleted

NAME                         READY   STATUS    RESTARTS   AGE
botiga-web-5b7c9f4d8-h7pnk   1/1     Running   0          6m
botiga-web-5b7c9f4d8-q3xwt   1/1     Running   0          17m
botiga-web-5b7c9f4d8-z8mrv   1/1     Running   0          17m

NAME         ENDPOINTS                                      AGE
botiga-web   10.244.0.82:80,10.244.0.83:80,10.244.0.87:80   2h

Tres pods, tres endpoints, tot en ordre. I adona't que en esborrar l'orfe no va aparèixer cap substitut: el ReplicaSet ja tenia els seus tres pods i aquell no era seu.

Conclusió

Tanques el mòdul 2 amb el fil que el recorria sencer convertit en sistema. Saps distingir sense dubtar una etiqueta d'una anotació: les primeres identifiquen i es consulten, estan indexades, tenen 63 caràcters i un joc de caràcters restringit; les segones adjunten informació que es llegeix però no es busca, admeten fins a 256 KiB i qualsevol contingut, inclosos JSON i text multilínia. Coneixes la sintaxi completa de claus i valors, saps que kubernetes.io/ és un prefix reservat i que el domini de la teva organització és el lloc correcte per al que és propi, i domines les sis etiquetes recomanades per Kubernetes, inclosa la distinció entre name (què és) i instance (quin és).

Has fixat l'esquema d'etiquetatge definitiu de Rutas Norte per als seus sis components i els seus tres entorns, amb la seva regla d'or: només app i entorn entren als selectors, perquè el selector d'un Deployment és immutable i ficar-hi la versió et condemnaria a recrear l'objecte a cada desplegament. Fas servir les dues famílies de selectors —igualtat amb =, == i !=; conjunts amb in, notin, exists i la seva negació— tant a la línia d'ordres amb -l com als manifests amb matchLabels i matchExpressions, sense oblidar l'asimetria dels Services, que només admeten mapa pla. Saps qui consumeix selectors, que són gairebé tots: ReplicaSets, Deployments, Services, NetworkPolicies, PodDisruptionBudgets, afinitat i les eines de monitoratge. Fas servir les anotacions per al que serveixen —documentar el canvi, configurar controladors d'Ingress, deixar el runbook i el telèfon de guàrdia a mà— sense cometre mai l'error de guardar-hi un secret. I tens al cinturó kubectl label i kubectl annotate amb --overwrite i l'esborrat amb guió final, més el repertori de consultes que converteix un clúster gran en una cosa navegable.

Amb això tanques el mòdul 2 i la plataforma Rutas Norte ja és viva. botiga-web va passar de ser aquell pod fràgil del mòdul 1 a un Deployment de tres rèpliques que es recupera sol; api-reserves, redis-cache i worker-notificacions tenen els seus; els quatre components que reben connexions tenen una adreça estable amb balanceig entre rèpliques; saps desplegar sense tall de servei i desfer un desplegament trencat en menys d'un minut; tens tres entorns separats en namespaces amb el mateix manifest desplegat en diversos d'ells; i tot està etiquetat amb un esquema coherent que les lliçons següents explotaran.

Però hi ha deutes pendents molt visibles. La contrasenya de PostgreSQL continua escrita en clar dins d'un manifest que és a Git, i aquella base de dades guarda el nom, el DNI, el telèfon i el correu de cada client de Rutas Norte. L'URL de l'API va incrustada a la imatge en lloc d'injectar-se segons l'entorn. Cap namespace no té quota, així que un error a desenvolupament pot deixar sense recursos la producció. I tots els nostres pods declaren requests i limits a ull, sense criteri. El mòdul 3, Gestió de Configuració i Secrets, ataca exactament això: separarem la configuració del codi amb ConfigMaps, traurem les credencials dels manifests amb Secrets, injectarem tots dos com a variables d'entorn, posarem quotes i límits a cada entorn, entendrem les classes de qualitat de servei que decideixen quin pod mor primer quan falta memòria, i donarem a cada component la seva pròpia identitat davant de l'API amb ServiceAccounts.

Curs de Kubernetes

Mòdul 1: Introducció a Kubernetes

Mòdul 2: Components Principals de Kubernetes

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

Mòdul 4: Xarxes a Kubernetes

Mòdul 5: Emmagatzematge a Kubernetes

Mòdul 6: Conceptes Avançats de Kubernetes

Mòdul 7: Monitoratge i Registre

Mòdul 8: Seguretat a Kubernetes

Mòdul 9: Escalat i Rendiment

Mòdul 10: Ecosistema i Eines de Kubernetes

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

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

© Copyright 2026. Tots els drets reservats