A la lliçó anterior vam desmuntar la ficció del ClusterIP: una adreça que no existeix en cap interfície i que només viu com a regles d'iptables programades per kube-proxy. Però aquell descobriment deixa intacte el problema que arrosseguem des del final del mòdul 3: un ClusterIP només és accessible des de dins del clúster. Un client que vol comprar un bitllet a www.rutasnorte.example no té cap manera d'arribar a botiga-web. Aquesta lliçó recorre els quatre tipus de Service —ClusterIP, NodePort, LoadBalancer i ExternalName— més dues variants que no són tipus però es comporten com si ho fossin: els serveis headless i els serveis sense selector. En acabar sabràs exactament quin fer servir en cada situació, per què LoadBalancer no és la solució per publicar la plataforma, i com donar un nom intern del clúster a la passarel·la de pagaments externa.

Contingut

  1. Panorama: sis maneres de declarar un Service
  2. Com cada tipus es construeix sobre l'anterior
  3. NodePort en detall
  4. LoadBalancer i el seu cost
  5. externalTrafficPolicy: Cluster davant de Local
  6. ExternalName: la passarel·la de pagaments
  7. Serveis sense selector: endpoints a mà
  8. Serveis headless
  9. sessionAffinity, ports amb nom i multiport
  10. Què fa servir Rutas Norte i per què

  1. Panorama: sis maneres de declarar un Service

Abans d'entrar en detall, la taula que convé tenir al davant durant tota la lliçó.

Forma spec clau Abast Qui l'implementa Quan fer-lo servir
ClusterIP type: ClusterIP (per defecte) Només dins del clúster kube-proxy Comunicació entre components. El 90 % dels casos
NodePort type: NodePort IP de qualsevol node, port 30000-32767 kube-proxy Proves, laboratori, o com a fonament d'un balancejador extern
LoadBalancer type: LoadBalancer IP pública/externa Un controlador del proveïdor de núvol Exposar un servei TCP/UDP a l'exterior al núvol
ExternalName type: ExternalName + externalName Redirecció de nom DNS CoreDNS (registre CNAME) Donar un nom intern a un servei de fora
Headless clusterIP: None Només DNS, sense IP virtual CoreDNS (una A per pod) StatefulSets, clients que balancegen sols
Sense selector Sense selector, amb EndpointSlice manual Igual que el seu tipus Tu, a mà Servei extern amb IP fixes, migracions

Les dues últimes files no són valors de type: són modificacions que s'hi combinen. Un Service pot ser ClusterIP i headless, o ClusterIP i sense selector.

  1. Com cada tipus es construeix sobre l'anterior

Els tres primers tipus no són alternatives: són capes acumulatives. Un NodePort continua tenint el seu ClusterIP i continua funcionant des de dins. Un LoadBalancer continua tenint el seu NodePort.

flowchart TB
    subgraph LB["type: LoadBalancer"]
      subgraph NP["type: NodePort"]
        subgraph CIP["type: ClusterIP"]
          A["IP virtual interna<br/>10.96.x.x:port<br/>regles de kube-proxy"]
        end
        B["A mes: port 30000-32767<br/>obert a TOTS els nodes"]
      end
      C["A mes: IP externa aprovisionada<br/>pel proveidor, que apunta<br/>als NodePorts dels nodes"]
    end

Comprova-ho amb la sortida de kubectl:

kubectl get svc -n rutas-norte-dev
NAME           TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
api-reserves   ClusterIP      10.96.201.14    <none>        3000/TCP       9d
botiga-web     NodePort       10.96.88.7      <none>        80:31080/TCP   4m
demo-lb        LoadBalancer   10.96.44.190    10.0.0.51     80:32410/TCP   2m

Fixa't en la columna PORT(S): 80:31080/TCP significa "port 80 del Service, publicat també al 31080 de cada node". I tots tres tenen CLUSTER-IP, inclòs el LoadBalancer. Aquesta acumulació és la raó que el diagnòstic es faci sempre de dins cap a fora: si el ClusterIP no funciona, el NodePort tampoc no ho farà.

  1. NodePort en detall

Un NodePort obre el mateix port a tots els nodes del clúster, independentment d'on siguin els pods. Si truques a aquell port en un node que no allotja cap rèplica, kube-proxy reenvia el trànsit al node que sí que la té.

# k8s/entorns/dev/botiga-web-service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: botiga-web
  namespace: rutas-norte-dev
  labels:
    app: botiga-web
    app.kubernetes.io/part-of: rutas-norte
    entorn: dev
spec:
  type: NodePort
  selector:            # recorda: nomes app i entorn als selectors
    app: botiga-web
    entorn: dev
  ports:
    - name: http
      port: 80         # port del ClusterIP
      targetPort: 8080 # port del contenidor nginx
      nodePort: 31080  # OPCIONAL: si s'omet, l'assigna el cluster

Punts que cal tenir clars:

  • El rang per defecte és 30000-32767 (uns 2.700 ports). Es pot canviar amb --service-node-port-range a l'apiserver, però és una decisió del clúster, no de l'equip d'aplicació.
  • Si omets nodePort, Kubernetes en tria un de lliure. És el recomanable: fixar-lo introdueix un conflicte potencial entre equips i un Invalid value: provided port is already allocated quan algú s'avança.
  • El port s'obre a tots els nodes, encara que només un tingui pods. Amb externalTrafficPolicy: Local això canvia (apartat 5).
  • Prova a minikube:
minikube -p rutas-norte service botiga-web -n rutas-norte-dev --url
http://192.168.49.2:31080

Per què no es fa servir en producció

Problema Conseqüència
Ports lletjos i arbitraris Ningú no escriurà http://rutasnorte.example:31080 al navegador
Sense nom de domini ni TLS Caldria resoldre l'HTTPS per fora
Sense alta disponibilitat Apuntes a la IP d'un node; si aquell node cau, el servei deixa de respondre encara que el pod visqui
Requereix obrir el tallafocs 2.700 ports potencialment accessibles a tots els nodes
No agrupa serveis Un port per Service, sense encaminament per host ni per ruta

L'ús legítim de NodePort és com a fonament: els balancejadors dels núvols i molts controladors d'Ingress s'hi recolzen precisament. Per a desenvolupament i demostracions ràpides, és perfecte.

  1. LoadBalancer i el seu cost

type: LoadBalancer diu al clúster: "demana al proveïdor d'infraestructura un balancejador extern que apunti a aquest servei". Kubernetes no el materialitza: ho fa un controlador que forma part del cloud-controller-manager del núvol, o un complement com MetalLB en instal·lacions pròpies.

apiVersion: v1
kind: Service
metadata:
  name: botiga-web
  namespace: rutas-norte-pro
spec:
  type: LoadBalancer
  selector:
    app: botiga-web
    entorn: pro
  ports:
    - name: http
      port: 80
      targetPort: 8080

En un clúster sense proveïdor de núvol, el resultat és aquest:

NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
botiga-web   LoadBalancer   10.96.44.190   <pending>     80:32410/TCP   5m

Aquest <pending> etern és una de les preguntes més repetides per qui comença. No és una fallada: és que ningú no ha recollit la petició. L'objecte està ben creat, però no existeix cap controlador capaç d'aprovisionar una IP externa. A minikube es resol amb:

# En un altre terminal; demana contrasenya d'administrador i cal deixar-lo obert
minikube -p rutas-norte tunnel
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)
botiga-web   LoadBalancer   10.96.44.190   10.96.44.190  80:32410/TCP

minikube tunnel crea rutes a la teva màquina perquè les IP assignades siguin accessibles. És una emulació local, no un balancejador real.

L'argument econòmic que porta a Ingress

Cada Service de tipus LoadBalancer provoca l'aprovisionament d'un balancejador independent al núvol, amb la seva IP pública i la seva factura. Per a Rutas Norte:

Servei a publicar Balancejador propi? Cost mensual aproximat
botiga-web 1 balancejador
api-reserves 1 balancejador
Panell d'administració intern 1 balancejador
Mètriques de Grafana (07-04) 1 balancejador

Quatre balancejadors, quatre IP públiques, quatre certificats TLS per gestionar per separat i cap possibilitat d'encaminar per ruta o per capçalera. Multiplicat per tres entorns, dotze. L'alternativa és un únic LoadBalancer davant d'un controlador d'Ingress, que reparteix internament per domini i per ruta. D'aquí ve 04-04.

A més, LoadBalancer opera en L4: no entén HTTP, no pot encaminar per Host, no pot reescriure rutes. Per a serveis que no són HTTP (un broker MQTT, un postgres exposat a una altra xarxa) continua sent la resposta correcta.

  1. externalTrafficPolicy: Cluster davant de Local

S'aplica a NodePort i LoadBalancer, i decideix què fa un node quan li arriba trànsit extern per a un Service.

flowchart TB
    subgraph CL["externalTrafficPolicy: Cluster (per defecte)"]
      direction LR
      C1["Client 203.0.113.9"] --> N1["Node A<br/>(sense pods)"]
      N1 -->|"SNAT: origen passa a IP del node A"| N2["Node B<br/>pod botiga-web"]
      N2 --> R1["El pod veu origen = 192.168.49.2"]
    end
    subgraph LO["externalTrafficPolicy: Local"]
      direction LR
      C2["Client 203.0.113.9"] --> M1["Node A<br/>(sense pods)"]
      M1 -->|"DESCARTA"| X1["Sense resposta"]
      C2 --> M2["Node B<br/>pod botiga-web"]
      M2 --> R2["El pod veu origen = 203.0.113.9"]
    end
Cluster (per defecte) Local
Salt extra entre nodes Sí, si el node no té pods No, mai
IP d'origen del client Es perd (SNAT al node) Es conserva
Repartiment de càrrega Uniforme entre tots els pods Proporcional als pods d'aquell node
Comprovació de salut del balancejador Tots els nodes responen Només els que tenen pods (per healthCheckNodePort)
Si un node no té pods Reenvia Descarta el paquet

El desequilibri de Local mereix un exemple. Si botiga-web té 3 rèpliques i el node A n'allotja 2 i el node B 1, un balancejador que reparteixi 50/50 entre nodes farà que cada pod del node A rebi un 25 % del trànsit i el del node B un 50 %. Es corregeix amb antiafinitat de pods (06-05).

Per a Rutas Norte, la IP d'origen importa: els registres d'accés, la limitació de peticions als pics dels ponts i el bloqueig d'adreces abusives en depenen. Amb Cluster totes les peticions semblarien venir dels nodes.

spec:
  type: LoadBalancer
  externalTrafficPolicy: Local   # conserva la IP real del client

Quan el trànsit arriba per un Ingress HTTP hi ha una alternativa: el controlador afegeix la capçalera X-Forwarded-For amb la IP real. Però això només serveix per a HTTP i només si l'aplicació la llegeix. Existeix també internalTrafficPolicy, amb la mateixa idea aplicada al trànsit intern del clúster: Local obliga que un pod es connecti només a rèpliques del seu propi node.

  1. ExternalName: la passarel·la de pagaments

api-reserves ha de cobrar els bitllets contra pagos.proveedorexterno.example, que és fora del clúster. Es podria posar aquest domini en un ConfigMap i llestos, però ExternalName dona una capa d'indirecció molt útil.

# k8s/entorns/pro/passarella-pagaments-externalname.yaml
apiVersion: v1
kind: Service
metadata:
  name: passarella-pagaments
  namespace: rutas-norte-pro
  labels:
    app.kubernetes.io/part-of: rutas-norte
    entorn: pro
spec:
  type: ExternalName
  externalName: pagos.proveedorexterno.example

Què passa per sota: CoreDNS crea un registre CNAME. Quan api-reserves resol passarella-pagaments.rutas-norte-pro.svc.cluster.local, la resposta és un àlies cap a pagos.proveedorexterno.example, que es resol pel DNS extern (04-03).

kubectl run t --rm -it --restart=Never -n rutas-norte-pro \
  --image=nicolaka/netshoot -- nslookup passarella-pagaments
Server:    10.96.0.10
Address:   10.96.0.10#53

passarella-pagaments.rutas-norte-pro.svc.cluster.local
    canonical name = pagos.proveedorexterno.example.
Name:      pagos.proveedorexterno.example
Address:   198.51.100.44

Avantatges per al nostre cas:

  • L'aplicació sempre apunta a passarella-pagaments, als tres entorns. El que canvia és l'externalName de cada namespace: a rutas-norte-dev apuntaria a pagos-sandbox.proveedorexterno.example, a pro al de producció. Canviar de proveïdor no toca ni una línia de codi.
  • No hi ha proxy: el trànsit va directe del pod a la destinació externa, sense passar per kube-proxy.

I les seves tres limitacions, que causen sorpreses:

  1. No tradueix ports. El ports d'un ExternalName és decoratiu. Si el proveïdor escolta al 8443, el teu client ha de demanar el 8443.
  2. No fa TLS. El certificat del proveïdor serà el de pagos.proveedorexterno.example, no el de passarella-pagaments, així que la validació SNI/hostname farà servir el nom real. Amb HTTP i Host reescrit pot fallar la comprovació a l'altre extrem.
  3. Depèn del DNS extern. Si CoreDNS no pot reenviar al resolutor del node, això no funciona.

Nota important per a 04-06: un ExternalName no compta com a excepció en una política de xarxa. Per permetre la sortida cap al proveïdor caldrà autoritzar el seu rang d'IP amb ipBlock.

  1. Serveis sense selector: endpoints a mà

Un Service normal esbrina les seves destinacions mitjançant el selector. Si omets el selector, el controlador d'endpoints no crea res i pots escriure tu mateix l'EndpointSlice. El resultat és un ClusterIP normal, amb el seu nom DNS i el seu balanceig, que apunta a adreces que no són pods.

Cas realista de Rutas Norte: durant la migració, la base de dades de facturació continua en una màquina virtual de l'oficina, a 10.20.30.40:5432.

# k8s/entorns/pro/facturacio-externa.yaml
apiVersion: v1
kind: Service
metadata:
  name: facturacio-llegat
  namespace: rutas-norte-pro
spec:
  ports:
    - name: postgres
      port: 5432
      targetPort: 5432
      protocol: TCP
  # SENSE selector: ningu no omple els endpoints automaticament
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: facturacio-llegat-manual
  namespace: rutas-norte-pro
  labels:
    # OBLIGATORIA: enllaca el slice amb el Service
    kubernetes.io/service-name: facturacio-llegat
addressType: IPv4
ports:
  - name: postgres      # ha de coincidir amb el name del Service
    port: 5432
    protocol: TCP
endpoints:
  - addresses:
      - "10.20.30.40"
    conditions:
      ready: true

Detalls crítics:

  • L'etiqueta kubernetes.io/service-name és el que uneix el slice amb el Service. Sense ella, el Service es queda sense endpoints.
  • El camp ports[].name de l'EndpointSlice ha de coincidir amb el ports[].name del Service.
  • Ningú no comprova la salut d'aquella adreça. Si la màquina cau, el Service continua enviant-hi trànsit. La responsabilitat de mantenir la llista és teva.
  • Les adreces no poden ser IP de Services (10.96.x.x) ni de bucle local.

Avantatge principal: l'aplicació connecta a facturacio-llegat:5432 des del primer dia. El dia que aquella base de dades es migri a un pod del clúster, s'afegeix el selector, s'esborra el slice manual i l'aplicació no se n'assabenta. És el patró d'indirecció que fa les migracions tolerables.

  1. Serveis headless

Un Service amb clusterIP: None és headless: no té IP virtual, no crea regles a kube-proxy i no balanceja res. L'única cosa que fa és publicar al DNS una entrada A per cada pod que casa amb el selector.

apiVersion: v1
kind: Service
metadata:
  name: postgres-reserves-headless
  namespace: rutas-norte-pro
spec:
  clusterIP: None          # aixo el converteix en headless
  selector:
    app: postgres-reserves
    entorn: pro
  ports:
    - name: postgres
      port: 5432
Service normal Service headless
CLUSTER-IP 10.96.x.x None
Regles de kube-proxy No
Resposta DNS Una A: la IP virtual Una A per pod llest
Balanceig El fa el kernel El fa el client
Ús típic Tota la resta Bases de dades en clúster, StatefulSets
$ nslookup postgres-reserves-headless.rutas-norte-pro.svc.cluster.local
Name:  postgres-reserves-headless...  Address: 10.244.0.14
Name:  postgres-reserves-headless...  Address: 10.244.0.18
Name:  postgres-reserves-headless...  Address: 10.244.0.23

Per a què serveix això? Per als casos en què el client necessita distingir els pods entre si:

  • Una rèplica de PostgreSQL amb un primari i dos secundaris: el client escriu al primari i llegeix dels secundaris. Un ClusterIP que repartís a l'atzar ho trencaria.
  • Bases de dades distribuïdes (Cassandra, Elasticsearch, Kafka) on cada node ha de conèixer els altres pel seu nom.
  • Clients amb balanceig propi (gRPC), que volen la llista de destinacions per repartir per petició i no per connexió.

El detall complet dels registres DNS que genera és a 04-03, i el seu ús real —amb identitats estables tipus postgres-reserves-0.postgres-reserves-headless— arriba amb els StatefulSets de 06-01, on per fi postgres-reserves deixarà de ser un Deployment.

Un matís: un Service headless i sense selector no genera registres A; en el seu lloc CoreDNS retorna els noms de l'externalName si n'hi hagués, o els endpoints manuals que defineixis.

  1. sessionAffinity, ports amb nom i multiport

sessionAffinity: ClientIP

Per defecte kube-proxy reparteix les connexions a l'atzar. Amb afinitat, totes les connexions de la mateixa IP d'origen van al mateix pod durant un temps.

spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800   # 3 hores; per defecte tambe 10800

És un martell tosc i cal dir-ho clar:

  • Funciona per IP, no per usuari. Cent clients darrere del NAT d'una empresa són una sola IP i cauen tots al mateix pod.
  • Trenca el repartiment de càrrega i complica els desplegaments progressius.
  • Amb externalTrafficPolicy: Cluster, la IP d'origen ja ve emmascarada, així que l'afinitat s'aplica sobre la IP del node: inútil.

Per a Rutas Norte no el fem servir: api-reserves és sense estat, i la sessió del comprador va en un JWT i a redis-cache. Aquesta és la solució correcta; l'afinitat de sessió és un pedaç per a aplicacions que guarden estat en memòria.

Ports amb nom i multiport

Quan un Service exposa més d'un port, name és obligatori a cadascun.

apiVersion: v1
kind: Service
metadata:
  name: api-reserves
  namespace: rutas-norte-pro
spec:
  selector:
    app: api-reserves
    entorn: pro
  ports:
    - name: http          # obligatori en haver-hi diversos
      port: 3000
      targetPort: api     # apunta a un port AMB NOM del contenidor
      protocol: TCP
    - name: metrics       # el consumira Prometheus al 07-03
      port: 9090
      targetPort: metrics
      protocol: TCP

Amb el contenidor declarant:

    ports:
      - name: api
        containerPort: 3000
      - name: metrics
        containerPort: 9090

Avantatges de fer servir targetPort amb nom: si demà l'aplicació canvia de port, es toca només el Deployment. I els noms de port són els que habiliten els registres SRV del DNS (04-03). Restricció: el nom ha de tenir 15 caràcters o menys i ser minúscules, dígits i guions.

  1. Què fa servir Rutas Norte i per què

Component Tipus triat Motiu
botiga-web ClusterIP Es publicarà per Ingress (04-04), no directament
api-reserves ClusterIP multiport (http + metrics) Igual; les mètriques no s'exposen mai fora
postgres-reserves ClusterIP (i headless a 06-01) No ha de sortir mai del clúster
redis-cache ClusterIP Ús intern exclusiu
worker-notificacions Sense Service No rep connexions entrants; només consumeix de la cua
passarella-pagaments ExternalName Indirecció cap al proveïdor extern
facturacio-llegat Sense selector + EndpointSlice manual Base de dades encara fora del clúster
Controlador d'Ingress LoadBalancer (un de sol) Un balancejador per a tota la plataforma

Aquella última fila és la conclusió pràctica de la lliçó: la plataforma sencera acaba tenint un sol punt d'entrada de pagament, i tota la resta viu en ClusterIP.

Un detall sobre worker-notificacions que convé interioritzar: no tot Deployment necessita un Service. El Service existeix per rebre connexions. Un procés que només fa connexions sortints no en necessita cap. (Sí que en necessitarà un si més endavant exposa mètriques per a Prometheus.)

Errors Comuns i Consells

  • Posar type: LoadBalancer en un clúster local i esperar una IP. El <pending> és l'esperat sense proveïdor. A minikube, minikube tunnel; en un servidor propi, MetalLB.
  • Fixar nodePort a mà "perquè sigui fàcil de recordar". Genera col·lisions entre equips i desplegaments que fallen amb provided port is already allocated. Deixa que l'assigni el clúster.
  • Canviar de ClusterIP a NodePort i creure que es perd el ClusterIP. No es perd: els tipus són acumulatius. El trànsit intern continua igual.
  • Fer servir ExternalName amb ports diferents. No tradueix ports. Si el proveïdor escolta al 8443, el teu client ha de demanar 8443 explícitament.
  • Un Service sense selector sense EndpointSlice. Es crea sense cap error i no respon mai. Comprova sempre amb kubectl get endpointslices -l kubernetes.io/service-name=<svc>.
  • Oblidar name en un Service multiport. L'error és clar (must specify name for multiple ports), però arriba tard: acostuma't a anomenar sempre els ports, fins i tot amb un de sol.
  • Fer servir sessionAffinity per arreglar una aplicació amb estat en memòria. Estàs tapant el problema. Treu l'estat a redis-cache.
  • Esperar que un Service headless balancegi. No ho fa. El client rep totes les IP i decideix ell. Moltes biblioteques HTTP agafen només la primera.
  • Consell: kubectl get svc -A -o custom-columns=NS:.metadata.namespace,NOM:.metadata.name,TIPUS:.spec.type és una auditoria d'un cop d'ull. Qualsevol LoadBalancer inesperat és diners sortint.
  • Consell: en depurar, ves sempre de dins cap a fora. ClusterIP → NodePort → LoadBalancer. Una fallada a la capa interna es manifesta a totes les externes.

Exercicis

Exercici 1: Publicar botiga-web amb NodePort i comprovar les capes

A rutas-norte-dev, canvia el Service de botiga-web a NodePort sense fixar el port. Comprova que continua funcionant des de dins pel seu ClusterIP i des de fora per la IP del node. Explica quina columna de kubectl get svc et diu tots dos ports.

Exercici 2: La passarel·la de pagaments amb ExternalName

Crea el Service passarella-pagaments a rutas-norte-dev apuntant a un sandbox (pagos-sandbox.proveedorexterno.example) i a rutas-norte-pro al domini real. Verifica des d'un pod efímer que la resolució retorna un CNAME diferent a cada namespace. Explica per què això permet que el codi d'api-reserves sigui idèntic als dos entorns.

Exercici 3: Base de dades de facturació fora del clúster

Crea facturacio-llegat com a Service sense selector amb un EndpointSlice manual apuntant a 10.20.30.40:5432. Comprova que el Service té endpoints i que el nom resol. Després, provoca la fallada típica: esborra l'etiqueta kubernetes.io/service-name del slice i observa què passa.

Solucions

Exercici 1

kubectl patch svc botiga-web -n rutas-norte-dev -p '{"spec":{"type":"NodePort"}}'
kubectl get svc botiga-web -n rutas-norte-dev
NAME         TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
botiga-web   NodePort   10.96.88.7    <none>        80:30417/TCP   9d
# Des de dins: el ClusterIP segueix viu
kubectl run t --rm -it --restart=Never -n rutas-norte-dev \
  --image=nicolaka/netshoot -- curl -s -o /dev/null -w '%{http_code}\n' http://botiga-web

# Des de fora
curl -s -o /dev/null -w '%{http_code}\n' http://$(minikube -p rutas-norte ip):30417

La columna PORT(S) amb format 80:30417/TCP dona tots dos: a l'esquerra el port del ClusterIP, a la dreta el NodePort. Demostra que NodePort afegeix una capa sobre ClusterIP.

Exercici 2

for NS in dev pro; do
  DEST=$( [ $NS = pro ] && echo pagos.proveedorexterno.example \
                        || echo pagos-sandbox.proveedorexterno.example )
  kubectl create service externalname passarella-pagaments \
    --external-name=$DEST -n rutas-norte-$NS
done

kubectl run t --rm -it --restart=Never -n rutas-norte-dev \
  --image=nicolaka/netshoot -- nslookup passarella-pagaments
passarella-pagaments.rutas-norte-dev.svc.cluster.local
    canonical name = pagos-sandbox.proveedorexterno.example.

El codi d'api-reserves fa servir sempre http://passarella-pagaments/cobraments. El namespace determina a quin proveïdor real es tradueix, sense variables d'entorn diferents ni branques al codi. Canviar de proveïdor és editar un manifest.

Exercici 3

kubectl apply -f k8s/entorns/pro/facturacio-externa.yaml

kubectl get endpointslices -n rutas-norte-pro \
  -l kubernetes.io/service-name=facturacio-llegat
NAME                       ADDRESSTYPE   PORTS   ENDPOINTS     AGE
facturacio-llegat-manual   IPv4          5432    10.20.30.40   8s
# Provocar la fallada: treure l'etiqueta que uneix slice i Service
kubectl label endpointslice facturacio-llegat-manual \
  -n rutas-norte-pro kubernetes.io/service-name-

kubectl get endpointslices -n rutas-norte-pro \
  -l kubernetes.io/service-name=facturacio-llegat
# No resources found

El Service continua existint, el seu ClusterIP continua assignat, kubectl get svc no mostra res estrany, i tota connexió es queda penjada fins a esgotar el temps d'espera. És el mateix quadre clínic que un selector que no casa, vist a 02-05: el Service sembla sa i no té destinacions. Per això la primera comprovació davant de "el Service no respon" és sempre mirar-ne els endpoints.

Conclusió

Ja coneixes el catàleg complet. ClusterIP és el tipus per defecte i el que fa servir gairebé tota la plataforma: només accessible des de dins. NodePort hi afegeix a sobre un port del rang 30000-32767 obert a tots els nodes, útil per provar i com a fonament d'altres mecanismes, però inacceptable en producció pels seus ports arbitraris, la seva manca de TLS i la seva dependència de la IP d'un node concret. LoadBalancer hi afegeix a sobre una IP externa aprovisionada pel proveïdor —d'aquí el <pending> etern quan no n'hi ha cap, i minikube tunnel per emular-lo—, i el seu cost d'un balancejador per servei és exactament l'argument que justifica l'Ingress. ExternalName no encamina trànsit: fa que CoreDNS retorni un CNAME, i amb ell hem donat a la passarel·la de pagaments un nom intern estable, diferent per entorn, sense tocar el codi.

Saps que els tres primers tipus són capes acumulatives, cosa que imposa diagnosticar sempre de dins cap a fora. Entens externalTrafficPolicy: Cluster reenvia entre nodes però destrueix la IP d'origen del client, mentre que Local la conserva a canvi d'un repartiment de càrrega desigual. I domines les dues variants que no són tipus: els serveis sense selector, on escrius tu l'EndpointSlice per apuntar a una base de dades externa i on l'etiqueta kubernetes.io/service-name és la baula que tothom oblida; i els serveis headless, que renuncien a la IP virtual per publicar una entrada A per pod, imprescindibles quan el client necessita distingir rèpliques.

Hi ha un fil que ha travessat tota la lliçó sense desenvolupar-se: el DNS. ExternalName funciona perquè CoreDNS crea un CNAME. Headless funciona perquè CoreDNS crea una A per pod. api-reserves troba postgres-reserves perquè hi ha un nom que es resol. La lliçó següent, 04-03, obre aquella caixa: què és CoreDNS, quines formes curtes de nom funcionen i quan, per què ndots: 5 pot multiplicar per cinc les consultes a la passarel·la de pagaments, i com diagnosticar les tres fallades de DNS que veuràs una vegada i una altra a la teva carrera.

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