La lliçó anterior va deixar un cap solt deliberat: ExternalName funciona perquè CoreDNS retorna un CNAME, els serveis headless funcionen perquè CoreDNS publica una entrada A per pod, i api-reserves troba postgres-reserves perquè algú resol aquell nom. Portem tres mòduls escrivint postgres-reserves:5432 a la configuració sense haver explicat mai qui tradueix això a una adreça IP. Aquesta lliçó obre la caixa: què és CoreDNS, com es configura, quins noms genera per a cada tipus d'objecte, què hi ha realment al /etc/resolv.conf d'un pod, per què el paràmetre ndots: 5 pot multiplicar per cinc les consultes DNS quan api-reserves truca a la passarel·la de pagaments, i com diagnosticar les tres fallades de DNS que et trobaràs una vegada i una altra.

Contingut

  1. Per què el descobriment es fa per DNS
  2. CoreDNS: què és i on viu
  3. El Corefile: la configuració del DNS del clúster
  4. L'esquema de noms i les formes curtes
  5. Registres de Services, Services headless i pods
  6. Registres SRV i ports amb nom
  7. El /etc/resolv.conf del pod i l'efecte d'ndots: 5
  8. dnsPolicy i dnsConfig
  9. Les variables d'entorn heretades de Docker
  10. Diagnòstic: les tres fallades típiques
  11. Escala: NodeLocal DNSCache i rèpliques de CoreDNS

  1. Per què el descobriment es fa per DNS

L'alternativa seria configurar adreces IP, i no funciona: les IP dels pods són efímeres (02-05). Un rollout, un desallotjament o un reinici del node la canvien. Podríem fixar la IP del Service, que sí que és estable mentre el Service existeixi, però això tampoc no aguanta:

Problema de configurar el ClusterIP Conseqüència
S'assigna en crear el Service El manifest d'api-reserves no es pot escriure fins que existeixi el de postgres-reserves
És diferent a cada entorn El ConfigMap de dev, pre i pro divergeix sense necessitat
Es perd en esborrar i recrear el Service Una reconstrucció del namespace trenca la plataforma
No sobreviu a un clúster nou Recuperació davant de desastres impossible sense editar configuració

Amb DNS, api-reserves porta escrit postgres-reserves al seu ConfigMap des de 03-01, i aquell nom és vàlid als tres entorns, en un clúster nou i després de recrear el Service: és l'única referència estable que existeix a Kubernetes. A més, el DNS és universal: no requereix biblioteca, ni SDK, ni saber que ets a Kubernetes.

  1. CoreDNS: què és i on viu

CoreDNS és un servidor DNS escrit en Go, modular a base de plugins, i és el DNS per defecte de Kubernetes des de la versió 1.13 (va substituir kube-dns). S'executa com un Deployment normal al namespace kube-system, exposat per un Service anomenat kube-dns (el nom es va conservar per compatibilitat).

kubectl get deploy,svc,pods -n kube-system -l k8s-app=kube-dns
NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/coredns   1/1     1            1           21d

NAME               TYPE        CLUSTER-IP   PORT(S)                  AGE
service/kube-dns   ClusterIP   10.96.0.10   53/UDP,53/TCP,9153/TCP   21d

NAME                          READY   STATUS    RESTARTS   AGE
pod/coredns-668d6bf9bc-hn2fk  1/1     Running   0          3d

Tres observacions que resolen molts dubtes:

  • CoreDNS és una càrrega de treball com qualsevol altra. Pot caure, quedar-se sense recursos, ser desallotjada. Quan CoreDNS pateix, tota la plataforma sembla trencada simultàniament, i aquell símptoma global és el que t'ha de fer mirar aquí.
  • La IP 10.96.0.10 és la desena adreça del serviceCIDR per convenció, i és la que el kubelet injecta a cada pod.
  • El port 9153 exposa mètriques Prometheus (07-03): taxa de consultes, latència, errors. És de les primeres coses que convé monitorar.
flowchart LR
    POD["Pod api-reserves"] -->|"consulta a 10.96.0.10:53"| SVC["Service kube-dns"]
    SVC -->|"kube-proxy DNAT"| CD["Pod CoreDNS"]
    CD -->|"plugin kubernetes:<br/>watch de Services i EndpointSlices"| API["kube-apiserver"]
    CD -->|"plugin forward:<br/>noms externs"| UP["Resolutor del node<br/>(/etc/resolv.conf de l'amfitrio)"]
    UP --> INT["Internet:<br/>pagos.proveedorexterno.example"]

Fixa't en la circularitat aparent: els pods arriben a CoreDNS a través d'un ClusterIP, que necessita kube-proxy però no necessita DNS. No hi ha problema de l'ou i la gallina perquè la IP 10.96.0.10 va injectada literalment al resolv.conf.

  1. El Corefile: la configuració del DNS del clúster

CoreDNS es configura amb un fitxer anomenat Corefile, que viu en un ConfigMap i es munta al pod. El pots llegir tal qual:

kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}'
.:53 {
    errors
    health {
       lameduck 5s
    }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
       pods insecure
       fallthrough in-addr.arpa ip6.arpa
       ttl 30
    }
    prometheus :9153
    forward . /etc/resolv.conf {
       max_concurrent 1000
    }
    cache 30
    loop
    reload
    loadbalance
}

Llegeix això com una cadena de plugins que s'avaluen en ordre per a tota consulta que arribi al port 53.

Plugin Què fa
errors Registra els errors al log
health / ready Punts per a les sondes del Deployment; lameduck espera 5 s abans de morir per no perdre consultes
kubernetes cluster.local El cor. Observa Services i EndpointSlices a l'API i respon els noms del domini cluster.local
pods insecure Habilita els registres de pods per IP (apartat 5)
ttl 30 Vida de les respostes del clúster: 30 segons
prometheus :9153 Exposa mètriques
forward . /etc/resolv.conf Tot el que no sigui cluster.local es reenvia al DNS que faci servir el node. Així es resol pagos.proveedorexterno.example
cache 30 Memòria cau de respostes, 30 s
loop Detecta bucles de reenviament i avorta l'arrencada
reload Recarrega el Corefile sense reiniciar el pod (triga ~2 min a detectar el canvi)
loadbalance Barreja l'ordre dels registres A a cada resposta

Un cas pràctic de Rutas Norte: l'oficina té un DNS intern a 10.20.30.5 que resol *.interno.rutasnorte.example, on viu la base de dades de facturació heretada. S'afegeix un bloc específic amb kubectl edit configmap coredns -n kube-system:

interno.rutasnorte.example:53 {
    errors
    cache 30
    forward . 10.20.30.5
}

El plugin reload ho recull sol. Compte: és configuració de tot el clúster; un error de sintaxi deixa CoreDNS en CrashLoopBackOff i amb ell tota la plataforma. Revisa el log després de cada canvi.

  1. L'esquema de noms i les formes curtes

El nom canònic d'un Service té sempre aquesta estructura:

<servei>.<namespace>.svc.<domini-del-cluster>
postgres-reserves.rutas-norte-pro.svc.cluster.local
       |                 |          |        |
       |                 |          |        +-- domini del clúster (configurable)
       |                 |          +----------- tipus d'objecte: svc
       |                 +---------------------- namespace
       +---------------------------------------- nom del Service

Les formes curtes funcionen gràcies a la llista search del resolv.conf (apartat 7). Des d'un pod de rutas-norte-pro:

Escrius Funciona? Què resol
postgres-reserves El del mateix namespace
postgres-reserves.rutas-norte-pro Explícit; serveix entre namespaces
postgres-reserves.rutas-norte-pro.svc Explícit
postgres-reserves.rutas-norte-pro.svc.cluster.local Canònic i sense ambigüitat
postgres-reserves.rutas-norte-dev Un altre namespace: els namespaces no aïllen la xarxa (02-06)

Aquell últim cas mereix èmfasi. Un pod de rutas-norte-dev pot resoldre i connectar a postgres-reserves.rutas-norte-pro. La separació per namespace és organitzativa, no de seguretat. Només les NetworkPolicies de 04-06 ho impedeixen de debò.

Regla pràctica per a Rutas Norte:

  • Dins del mateix entorn, fes servir el nom curt: postgres-reserves, redis-cache. El mateix ConfigMap serveix per a dev, pre i pro sense canvis.
  • Per creuar namespaces, fes servir sempre la forma completa amb .svc.cluster.local. És explícita i, com veurem, més ràpida.

  1. Registres de Services, Services headless i pods

Service normal: una A amb el ClusterIP

kubectl run t --rm -it --restart=Never -n rutas-norte-pro \
  --image=nicolaka/netshoot -- dig +short postgres-reserves.rutas-norte-pro.svc.cluster.local
10.96.140.22

Una sola resposta: la IP virtual. El balanceig entre pods el fa kube-proxy després, com vas veure a 04-01. El DNS no intervé en el repartiment.

Service headless: una A per pod

$ dig +short postgres-reserves-headless.rutas-norte-pro.svc.cluster.local
10.244.0.14
10.244.0.18
10.244.0.23

Tres registres A, un per pod llest (ready: true a l'EndpointSlice). Aquí el DNS sí que participa en el descobriment, i qui tria és el client. El plugin loadbalance barreja l'ordre a cada resposta perquè els clients que només miren el primer registre reparteixin alguna cosa.

Un pod que no està llest desapareix d'aquesta llista, tret que el Service tingui publishNotReadyAddresses: true (necessari perquè les rèpliques d'una base de dades es descobreixin entre si durant l'arrencada, cosa que veurem a 06-01).

Registres de pod

Amb pods insecure al Corefile, tot pod té un nom derivat de la seva IP amb els punts substituïts per guions: 10-244-0-14.rutas-norte-pro.pod.cluster.local resol a 10.244.0.14. Fixa't en pod en lloc de svc. És d'ús molt limitat: ningú no escriu això a mà. La seva importància real arriba amb els StatefulSets, on cada pod obté un nom estable i llegible dins d'un Service headless:

postgres-reserves-0.postgres-reserves-headless.rutas-norte-pro.svc.cluster.local
postgres-reserves-1.postgres-reserves-headless.rutas-norte-pro.svc.cluster.local

Aquesta és la identitat de xarxa que fa possible una base de dades en clúster, i arriba a 06-01.

Objecte Format Retorna
Service normal <svc>.<ns>.svc.cluster.local 1 A: el ClusterIP
Service headless <svc>.<ns>.svc.cluster.local N A: una per pod llest
Pod en headless <pod>.<svc>.<ns>.svc.cluster.local 1 A: la IP del pod
Pod per IP <ip-amb-guions>.<ns>.pod.cluster.local 1 A: aquella mateixa IP
ExternalName <svc>.<ns>.svc.cluster.local CNAME al domini extern

  1. Registres SRV i ports amb nom

Els registres SRV publiquen, a més de l'adreça, el port. Es generen per a tot port amb nom d'un Service, amb el format _<port>._<protocol>.<servei>.<ns>.svc.cluster.local.

Recuperant l'api-reserves multiport de 04-02, amb ports http (3000) i metrics (9090):

dig +short SRV _http._tcp.api-reserves.rutas-norte-pro.svc.cluster.local
0 100 3000 api-reserves.rutas-norte-pro.svc.cluster.local.

El format és prioritat pes port desti. Un client que sàpiga llegir SRV descobreix el port sense tenir-lo configurat. En un Service headless, un SRV retorna una línia per pod, amb el port de cadascun:

0 33 5432 10-244-0-14.postgres-reserves-headless.rutas-norte-pro.svc.cluster.local.
0 33 5432 10-244-0-18.postgres-reserves-headless.rutas-norte-pro.svc.cluster.local.
0 33 5432 10-244-0-23.postgres-reserves-headless.rutas-norte-pro.svc.cluster.local.

Poques aplicacions consulten SRV; la majoria configura el port explícitament. Però és el mecanisme que fan servir diversos operadors i biblioteques de descobriment, i és la raó concreta per la qual insistim a anomenar sempre els ports.

  1. El /etc/resolv.conf del pod i l'efecte d'ndots: 5

El kubelet escriu aquest fitxer dins de cada contenidor:

kubectl exec -n rutas-norte-pro deploy/api-reserves -- cat /etc/resolv.conf
nameserver 10.96.0.10
search rutas-norte-pro.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

Tres línies, tres decisions:

  • nameserver 10.96.0.10: el ClusterIP de kube-dns. És el que fa que les formes curtes es resolguin.
  • search: els sufixos que el resolutor provarà. El primer és el namespace mateix: per això postgres-reserves a seques funciona dins de rutas-norte-pro.
  • options ndots:5: aquí hi ha el parany.

Què significa ndots

ndots:5 diu al resolutor: "si el nom que et demanen té menys de 5 punts, prova'l primer amb cada sufix de search, i només al final tal qual".

Compta els punts de pagos.proveedorexterno.example: dos. Menys de cinc. Així que el resolutor fa això:

1. pagos.proveedorexterno.example.rutas-norte-pro.svc.cluster.local  -> NXDOMAIN
2. pagos.proveedorexterno.example.svc.cluster.local                  -> NXDOMAIN
3. pagos.proveedorexterno.example.cluster.local                      -> NXDOMAIN
4. pagos.proveedorexterno.example                                    -> 198.51.100.44  OK

Quatre consultes, tres d'inútils. I pitjor: cadascuna es fa per a A i per a AAAA, així que a la pràctica són 8 consultes quan n'hi havia prou amb 2. Les tres primeres, a més, viatgen a CoreDNS, que consulta l'API i retorna NXDOMAIN.

Ara trasllada això a Rutas Norte en un pont d'agost: api-reserves truca a la passarel·la de pagaments a cada compra. Amb 2.000 compres per minut i sense memòria cau de resolució a l'aplicació, són 16.000 consultes per minut on n'haurien de ser 4.000. CoreDNS se satura, la latència de resolució puja, i tot el clúster comença a anar a poc a poc per una causa que ningú no relaciona amb el DNS.

La solució: el punt final

Un nom acabat en punt és un FQDN absolut: el resolutor no hi afegeix sufixos.

# ConfigMap d'api-reserves: fixa't en el punt final
PASSARELLA_PAGAMENTS_URL: "https://pagos.proveedorexterno.example./cobraments"
1. pagos.proveedorexterno.example.  -> 198.51.100.44  OK

Una consulta. Fi.

Enfocament Consultes per resolució Quan fer-lo servir
Nom extern sense punt final Fins a 4 (x2 amb AAAA) Mai, si ho pots evitar
Nom extern amb punt final 1 Sempre per a dominis externs
Nom intern curt (redis-cache) 1 (encerta al primer sufix) Dins del mateix namespace
Nom intern complet amb punt final 1 Entre namespaces, i en rutes crítiques
dnsConfig amb ndots: 2 1 per als externs Quan no pots tocar les URL

Avís: si baixes ndots a 1, els noms curts com redis-cache (zero punts) continuen funcionant, però postgres-reserves.rutas-norte-pro (un punt) deixaria de resoldre's per cerca i necessitaria l'FQDN. Baixar a 2 és un compromís habitual i segur.

  1. dnsPolicy i dnsConfig

dnsPolicy és un camp del spec del pod que decideix quin resolv.conf rep.

Valor Què fa Quan fer-lo servir
ClusterFirst Per defecte: apunta a CoreDNS, que reenvia allò extern Pràcticament sempre
ClusterFirstWithHostNet Igual, però per a pods amb hostNetwork: true Pods de xarxa d'amfitrió que necessitin DNS del clúster
Default Hereta el resolv.conf del node. No resol noms del clúster Pods d'infraestructura que no parlen amb Services
None Ignora tot i fa servir exclusivament el dnsConfig que escriguis Configuracions DNS molt específiques

El cas de ClusterFirstWithHostNet és un parany clàssic: un pod amb hostNetwork: true i dnsPolicy: ClusterFirst (el valor per defecte) rep el resolv.conf del node i per tant no pot resoldre cap Service. Si poses xarxa d'amfitrió, has de canviar també la política.

dnsConfig permet ajustar el fitxer resultant:

# k8s/base/api-reserves-deployment.yaml (fragment)
spec:
  template:
    spec:
      dnsPolicy: ClusterFirst
      dnsConfig:
        options:
          - name: ndots
            value: "2"          # redueix les consultes inutils a la passarel-la
          - name: single-request-open-tcp
        searches:
          - interno.rutasnorte.example   # sufix addicional per al DNS d'oficina

Amb dnsPolicy: None és obligatori donar almenys un nameserver a dnsConfig, o el pod no arrenca.

  1. Les variables d'entorn heretades de Docker

Kubernetes injecta a cada contenidor variables d'entorn amb l'adreça i el port de tots els Services que existien al seu namespace en el moment de crear-se el pod. És un vestigi de l'enllaçat de contenidors de Docker.

kubectl exec -n rutas-norte-pro deploy/api-reserves -- env | grep SERVICE | sort
BOTIGA_WEB_SERVICE_HOST=10.96.88.7
BOTIGA_WEB_SERVICE_PORT=80
POSTGRES_RESERVES_PORT_5432_TCP_ADDR=10.96.140.22
POSTGRES_RESERVES_PORT_5432_TCP_PORT=5432
POSTGRES_RESERVES_SERVICE_HOST=10.96.140.22
POSTGRES_RESERVES_SERVICE_PORT=5432
REDIS_CACHE_SERVICE_HOST=10.96.77.31
REDIS_CACHE_SERVICE_PORT=6379

Semblen còmodes i no s'han de fer servir mai. Quatre raons:

  1. Només existeixen els Services creats ABANS que el pod. Si desplegues api-reserves i després redis-cache, la variable no existeix. I l'ordre de creació no és una cosa que controlis de manera fiable.
  2. Es congelen en arrencar el pod. Si el ClusterIP canvia (Service recreat), la variable continua amb el valor vell i el pod apunta a una IP morta fins que es reiniciï.
  3. Contaminen l'entorn. Amb 50 Services al namespace són centenars de variables per contenidor. Hi ha casos documentats de fallades en arrencar per excedir la mida de l'entorn.
  4. Trenquen la portabilitat. Aquell POSTGRES_RESERVES_SERVICE_HOST no significa res fora de Kubernetes.

Fes servir sempre el nom DNS, que no depèn de l'ordre, es resol fresc i funciona igual a qualsevol lloc. El soroll de variables s'elimina amb enableServiceLinks: false al spec del pod, recomanable a tots els Deployments de Rutas Norte.

  1. Diagnòstic: les tres fallades típiques

El pod de treball, com a 04-01:

kubectl run dnsdebug --rm -it --restart=Never -n rutas-norte-pro \
  --image=nicolaka/netshoot -- bash

Comprovacions bàsiques a dins:

cat /etc/resolv.conf                     # nameserver, search, ndots
nslookup postgres-reserves               # forma curta, mateix namespace
dig +search +short postgres-reserves     # +search imita el comportament real
dig @10.96.0.10 postgres-reserves.rutas-norte-pro.svc.cluster.local

Fallada 1: nslookup no resol RES, ni intern ni extern

;; connection timed out; no servers could be reached

Causa: CoreDNS és caigut, saturat o no és accessible. És una fallada global, i la seva signatura és que tots els components fallen alhora.

kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50
kubectl get endpointslices -n kube-system -l kubernetes.io/service-name=kube-dns

Sospitosos habituals: pods de CoreDNS en CrashLoopBackOff per un Corefile mal editat; el Service kube-dns sense endpoints; CoreDNS OOMKilled per manca de memòria sota càrrega; o —i això serà molt rellevant a 04-06una NetworkPolicy d'egress que bloqueja el port 53. Aquest últim cas és tan freqüent que li dedicarem un apartat sencer.

Fallada 2: resol els noms externs però no els del clúster

$ nslookup pagos.proveedorexterno.example
Address: 198.51.100.44          # OK

$ nslookup postgres-reserves
** server can't find postgres-reserves: NXDOMAIN

Causa: el resolv.conf del pod no és el del clúster. Gairebé sempre és dnsPolicy: Default posat sense voler, o hostNetwork: true sense ClusterFirstWithHostNet. Comprova-ho:

kubectl get pod <pod> -n rutas-norte-pro \
  -o jsonpath='{.spec.dnsPolicy}{"  hostNetwork="}{.spec.hostNetwork}{"\n"}'

Segona causa possible: el nom està malament. Un error tipogràfic, un Service en un altre namespace, o el Service simplement no existeix.

Fallada 3: resol però la connexió falla o va lentíssima

$ nslookup postgres-reserves
Address: 10.96.140.22           # resol bé

$ nc -zv postgres-reserves 5432
... es queda penjat

El DNS no és el problema. Ja has demostrat que la resolució funciona; la fallada és al Service (endpoints buits, 02-05), al CNI o en una política de xarxa. Aquest és el cas més mal interpretat: es culpa el DNS perquè "no connecta pel nom", quan nslookup ja ha respost correctament.

La variant de lentitud sí que pot ser DNS: si la resolució triga segons, mira ndots i els dominis externs sense punt final (apartat 7), o busca saturació a les mètriques de CoreDNS.

Símptoma Resolució interna Resolució externa Causa probable
No resol res Falla Falla CoreDNS caigut / port 53 bloquejat
Només falla allò del clúster Falla Funciona dnsPolicy o hostNetwork malament
Resol però no connecta Funciona Funciona No és DNS: Service, CNI o NetworkPolicy
Tot va lent Lent Molt lent ndots:5 sense punt final; CoreDNS saturat

  1. Escala: NodeLocal DNSCache i rèpliques de CoreDNS

En clústers amb càrrega alta el DNS es converteix en coll d'ampolla. Dues mesures estàndard:

Rèpliques de CoreDNS. Per defecte són 2 (1 a minikube). S'escala amb la mida del clúster, vigilant-ne les mètriques abans que faci mal: kubectl scale deployment coredns -n kube-system --replicas=3. Existeix cluster-proportional-autoscaler, que ajusta les rèpliques segons el nombre de nodes i nuclis, i és el que fan servir les distribucions gestionades.

NodeLocal DNSCache. Un DaemonSet que posa una memòria cau DNS a cada node. Els pods consulten aquella memòria cau local (per una IP d'enllaç, típicament 169.254.20.10) en lloc de creuar la xarxa fins a CoreDNS.

Benefici Detall
Menys latència La consulta no surt del node si és a la memòria cau
Menys càrrega a CoreDNS Només hi arriben les fallades de memòria cau
Menys problemes de conntrack Fa servir TCP cap a CoreDNS, evitant la coneguda fallada de curses amb UDP
Aïlla incidents Un CoreDNS que es reinicia no interromp les resolucions a la memòria cau

Per a Rutas Norte, amb els seus pics de ponts, és la mesura que es plantejaria juntament amb l'autoescalat de 09-01.

Errors Comuns i Consells

  • Culpar el DNS quan nslookup respon bé. Si resol, el DNS ha fet la seva feina. Continua pel Service i els seus endpoints.
  • Editar el Corefile sense revisar el log. Un error de sintaxi tomba CoreDNS i amb ell tot el clúster. Després de cada kubectl edit configmap coredns, mira kubectl logs -n kube-system -l k8s-app=kube-dns.
  • Dominis externs sense punt final. Multipliquen per quatre (o vuit) les consultes. A Rutas Norte, l'URL de la passarel·la de pagaments ha de portar el punt.
  • Fer servir hostNetwork: true sense dnsPolicy: ClusterFirstWithHostNet. El pod perd la capacitat de resoldre Services i l'error és desconcertant.
  • Fer servir les variables *_SERVICE_HOST. Depenen de l'ordre de creació i es congelen. Fes servir noms DNS i desactiva-les amb enableServiceLinks: false.
  • Guardar resolucions per sempre a la memòria cau de l'aplicació. Alguns entorns (la JVM amb la configuració per defecte històrica) les guarden sense caducitat i continuen fent servir una IP morta. Ajusta el TTL del resolutor del teu llenguatge.
  • Suposar que el namespace aïlla. Un pod de dev resol i connecta a postgres-reserves.rutas-norte-pro. Això només ho talla 04-06.
  • Consell: davant de qualsevol dubte, executa dig +search i no dig a seques. Sense +search no s'apliquen els sufixos i estaràs provant una cosa diferent del que fa la teva aplicació.
  • Consell: vigila coredns_dns_request_duration_seconds i la taxa d'NXDOMAIN. Un pic d'NXDOMAIN sol significar que algú ha desplegat una aplicació amb noms externs sense punt final.

Exercicis

Exercici 1: Cartografiar els noms de la plataforma

Des d'un pod efímer a rutas-norte-pro, resol postgres-reserves en les seves quatre formes (curta, amb namespace, amb .svc, completa) i comprova que totes donen la mateixa IP. Després resol postgres-reserves.rutas-norte-dev i explica què demostra el resultat.

Exercici 2: Mesurar el cost d'ndots: 5

Des d'un pod efímer, compta quantes consultes provoca resoldre pagos.proveedorexterno.example sense punt final i amb punt final. Calcula l'estalvi per a 2.000 compres per minut i proposa dues maneres d'arreglar-ho al manifest d'api-reserves.

Exercici 3: Diagnosticar tres pods amb DNS trencat

Es despleguen tres pods de prova: A amb hostNetwork: true i dnsPolicy per defecte; B normal però consultant un Service inexistent; C normal, amb el Service correcte però el Deployment del qual està escalat a zero. Prediu el símptoma de cadascun i verifica'l.

Solucions

Exercici 1

kubectl run t --rm -it --restart=Never -n rutas-norte-pro \
  --image=nicolaka/netshoot -- sh -c '
    for n in postgres-reserves \
             postgres-reserves.rutas-norte-pro \
             postgres-reserves.rutas-norte-pro.svc \
             postgres-reserves.rutas-norte-pro.svc.cluster.local \
             postgres-reserves.rutas-norte-dev; do
      printf "%-56s %s\n" "$n" "$(dig +search +short $n | head -1)"
    done'
postgres-reserves                                        10.96.140.22
postgres-reserves.rutas-norte-pro                        10.96.140.22
postgres-reserves.rutas-norte-pro.svc                    10.96.140.22
postgres-reserves.rutas-norte-pro.svc.cluster.local      10.96.140.22
postgres-reserves.rutas-norte-dev                        10.96.19.88

Les quatre primeres són la mateixa IP: les formes curtes es completen amb la llista search. La cinquena demostra que des de producció es resol el Service de desenvolupament, amb una IP diferent: els namespaces no aïllen la xarxa. I si a més proves nc -zv postgres-reserves.rutas-norte-dev 5432, connectarà.

Exercici 2

kubectl run t --rm -it --restart=Never -n rutas-norte-pro --image=nicolaka/netshoot -- sh -c '
    dig +search pagos.proveedorexterno.example  | grep -c "^;.*IN.*A"   # sense punt
    dig         pagos.proveedorexterno.example. | grep -c "^;.*IN.*A"'  # amb punt

Sense punt: 4 intents (3 NXDOMAIN + 1 encert), per 2 en comptar AAAA = 8 consultes. Amb punt: 2. Estalvi del 75 %.

A 2.000 compres per minut: 16.000 consultes/min davant de 4.000. Dotze mil consultes inútils cada minut contra CoreDNS.

Dos arreglaments al manifest:

# Opcio A (preferida): punt final a l'URL del ConfigMap
PASSARELLA_PAGAMENTS_URL: "https://pagos.proveedorexterno.example./cobraments"

# Opcio B: baixar ndots per a tot el pod
spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"

L'A és quirúrgica i no afecta res més; la B serveix quan no pots tocar les URL, però recorda que trenca les formes curtes amb un punt.

Exercici 3

Pod Símptoma Causa
A (hostNetwork) Resol pagos.proveedorexterno.example però dona NXDOMAIN a postgres-reserves Fa servir el resolv.conf del node; necessita dnsPolicy: ClusterFirstWithHostNet
B (Service inexistent) NXDOMAIN només en aquell nom; la resta resol El nom no existeix: error tipogràfic o namespace equivocat
C (Deployment a zero) Resol correctament al ClusterIP, però la connexió esgota el temps d'espera El DNS funciona; el Service no té endpoints. No és un problema de DNS
# Verificacio del C: la clau es als endpoints, no al DNS
kubectl get endpointslices -n rutas-norte-pro \
  -l kubernetes.io/service-name=api-reserves
# ENDPOINTS: <none>

El cas C és la lliçó més valuosa de les tres: resoldre no és connectar.

Conclusió

Ara saps qui tradueix postgres-reserves a una adreça IP. CoreDNS és un Deployment normal de kube-system, exposat pel Service kube-dns a 10.96.0.10, configurat per un Corefile el plugin kubernetes del qual observa Services i EndpointSlices per respondre el domini cluster.local, i el plugin forward del qual envia tota la resta al resolutor del node. Que sigui una càrrega de treball corrent té una conseqüència operativa: pot caure, saturar-se o quedar-se sense memòria, i quan això passa tota la plataforma sembla trencada alhora.

Domines l'esquema de noms <servei>.<namespace>.svc.cluster.local i saps que les formes curtes funcionen gràcies a la llista search del resolv.conf, no per màgia. Coneixes els registres que genera cada objecte: una A amb el ClusterIP per a un Service normal, una A per pod llest per a un de headless, noms de pod derivats de la IP, i registres SRV que publiquen el port dels ports amb nom. I entens el que la resolució no fa: no reparteix càrrega en un Service normal (això és kube-proxy) i no garanteix connectivitat.

L'apartat que més t'estalviarà és ndots: 5. Un domini extern amb menys de cinc punts provoca quatre intents de resolució, vuit consultes comptant AAAA, i als pics de ponts de Rutas Norte això són milers de consultes inútils per minut contra CoreDNS. El punt final a pagos.proveedorexterno.example. ho redueix a una. Saps ajustar dnsPolicy i dnsConfig, has vist per què hostNetwork: true sense ClusterFirstWithHostNet deixa el pod sense accés al DNS del clúster, i per què les variables *_SERVICE_HOST heretades de Docker no s'han de fer servir mai —depenen de l'ordre de creació i es congelen— fins al punt de desactivar-les amb enableServiceLinks: false. Tens també les tres fallades típiques amb la seva signatura inconfusible, i a l'horitzó NodeLocal DNSCache i l'escalat de CoreDNS per quan la càrrega estrenyi.

Amb la xarxa entesa (04-01), els tipus de Service dominats (04-02) i el descobriment resolt, ja no queda cap excusa: és hora d'obrir la plataforma al món. A 04-04 desplegarem un controlador d'Ingress i publicarem per fi www.rutasnorte.example contra botiga-web i api.rutasnorte.example contra api-reserves, amb un únic punt d'entrada, encaminat per domini i per ruta. Els clients de Rutas Norte podran, per primera vegada al curs, comprar un bitllet.

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