A 10-02 vam muntar un clúster amb kubeadm i vam descobrir tot el que implica: preparar màquines, configurar containerd, generar certificats que caduquen a l'any, mantenir el quòrum d'etcd, assajar restauracions i actualitzar node a node. Vam acabar amb un advertiment clar: operar això en producció exigeix un equip dedicat amb guàrdia.

A 10-05 vam muntar GitOps, i ara tot l'estat de la plataforma Rutas Norte viu a Git. Això planteja una pregunta natural: si el clúster és reemplaçable i es reconstrueix sol, per què l'hauríem de mantenir nosaltres?

Aquesta lliçó respon a aquesta pregunta. Veurem què gestiona el proveïdor i què continua sent teu, compararem EKS, AKS i GKE en els criteris que de debò decideixen, tancarem la federació d'identitat que vam deixar pendent a 03-06, aprofitarem les instàncies interrompibles per a les càrregues tolerants de Rutas Norte, i acabarem amb una recomanació raonada.

Contingut

  1. El model de responsabilitat compartida
  2. Comparació d'EKS, AKS i GKE
  3. Identitat: IRSA i Workload Identity
  4. Nodes: grups gestionats, plantilles i instàncies interrompibles
  5. Què canvia en el que ja saps
  6. Actualització de versió d'un clúster gestionat
  7. Cost real i palanques d'estalvi
  8. Portabilitat i dependència del proveïdor
  9. La decisió per a Rutas Norte
  10. Errors comuns i consells
  11. Exercicis
  12. Conclusió

  1. El model de responsabilitat compartida

Un clúster gestionat no és "Kubernetes sense feina". És una frontera diferent entre el que fa el proveïdor i el que fas tu.

flowchart TB
    subgraph prov["Responsabilitat del proveïdor"]
        P1[apiserver, scheduler,<br/>controller-manager]
        P2[etcd: rèpliques,<br/>còpies, xifratge]
        P3[Certificats<br/>del pla de control]
        P4[Alta disponibilitat<br/>entre zones]
        P5[Pedaços del pla<br/>de control]
    end
    subgraph teva["Responsabilitat teva"]
        T1[Càrregues de treball<br/>i els seus recursos]
        T2[Nodes: tipus, mida,<br/>pedaços del SO]
        T3[Xarxa: VPC, subxarxes,<br/>NetworkPolicies]
        T4[RBAC, PSS,<br/>secrets]
        T5[Cost]
        T6[Còpies de les DADES]
    end
    style prov fill:#e8ffe8
    style teva fill:#fff4e8

La taula completa, contrastada amb kubeadm

Responsabilitat kubeadm (10-02) Gestionat
Màquines del pla de control Teva Del proveïdor
apiserver, scheduler, controller-manager Teva Del proveïdor
etcd: desplegament, quòrum, còpies Teva (etcdctl snapshot) Del proveïdor
Certificats del pla de control Teva (kubeadm certs renew, caduquen a l'any) Del proveïdor
Alta disponibilitat del pla de control Teva (HAProxy, 3 nodes) Del proveïdor
Actualització del pla de control Teva (kubeadm upgrade apply) Del proveïdor (tu tries quan)
Escalat del pla de control amb la càrrega Teva Del proveïdor
Registre d'auditoria de l'apiserver Teva (configurar) Del proveïdor (tu l'actives)
Sistema operatiu dels nodes Teva Teva (imatges del proveïdor, tu apliques)
Actualització del kubelet als nodes Teva Teva (assistida)
CNI Teva (instal·lar Calico) Del proveïdor per defecte, canviable per tu
Càrregues de treball, sondes, recursos Teva Teva
RBAC, Pod Security Standards, polítiques Teva Teva
NetworkPolicies Teva Teva
Secrets i la seva gestió Teva Teva
Còpies de les dades de les aplicacions Teva (Velero, 05-06) Teva (Velero)
Monitoratge de les aplicacions Teva (Prometheus, 07-03) Teva
Cost Teva Teva
Compliment normatiu i dades personals Teva Teva

El que cal entendre

El pla de control desapareix de les teves preocupacions. Els certificats que caduquen a l'any, el quòrum d'etcd, els snapshots, l'HAProxy davant dels tres nodes de control: tot això se'n va. És aproximadament la meitat de la lliçó 10-02.

Tota la resta continua igual. Els nodes continuen sent teus, amb el seu sistema operatiu que cal pedaçar. El RBAC continua sent teu. Les NetworkPolicies continuen sent teves. I si postgres-reserves perd dades, és el teu problema, no el del proveïdor.

Un error molt freqüent: "està gestionat, així que les còpies estan fetes". Fals. El proveïdor fa còpia d'etcd (la definició dels objectes), no dels volums de les teves aplicacions. De les dades de clients de postgres-reserves en fas la còpia tu, amb Velero o amb els snapshots del proveïdor. Ho vam veure a 05-06 i continua sent així.

La regla d'or: un clúster gestionat et treu la meitat de la feina d'operació de Kubernetes. L'altra meitat, la que has estudiat als mòduls 2 a 9, continua sent íntegrament teva.

  1. Comparació d'EKS, AKS i GKE

Cost del pla de control

EKS AKS GKE
Cost mensual del pla de control ~73 $ per clúster 0 $ al nivell gratuït; ~73 $ amb SLA ~73 $ per clúster (un de gratuït per compte)
SLA inclòs 99,95 % Només amb el nivell estàndard 99,95 % (regional) / 99,5 % (zonal)
S'aplica a cada clúster? Sí, tret del primer

Amb tres entorns separats en clústers diferents, això són uns 220 $/mes només per existir. És una de les raons per les quals moltes empreses separen entorns per namespace dins d'un clúster (com fa Rutas Norte) en comptes de per clúster.

Avís: el nivell gratuït d'AKS no té SLA financer. Per a rutas-norte-pro això pot ser inacceptable, i llavors el cost s'iguala.

Cicle de versions i finestra de suport

EKS AKS GKE
Versions menors suportades ~4 simultànies ~3 Depèn del canal
Suport estàndard 14 mesos 12 mesos 14 mesos (canal normal)
Suport estès (de pagament) Sí, fins a 26 mesos
Actualització automàtica Només del pedaç Configurable Sí, per canals
Canals de versió No No Ràpid, normal, estable

El sistema de canals de GKE és una diferència real d'operació: tries "estable" i Google manté el clúster en una versió provada, actualitzant-lo a la finestra de manteniment que li indiquis. És el més a prop que s'està de no pensar en versions.

A EKS i AKS, el cicle és més manual. I si deixes caducar la versió, tots dos acaben forçant l'actualització, cosa que és pitjor que planificar-la.

Model de xarxa i assignació d'IP als pods

Aquesta és la diferència tècnica més important i menys coneguda, i la que provoca sorpreses serioses en producció.

EKS (VPC CNI) AKS (Azure CNI) AKS (kubenet) GKE (natiu de VPC)
IP del pod Real de la VPC Real de la xarxa virtual Superposada amb NAT Real de la VPC (àlies)
Consumeix IP de la teva xarxa? Sí, una per pod Sí, una per pod No Sí, d'un rang secundari
Pods per node Limitat pel tipus d'instància Configurable, reserva per endavant Alt Configurable
Rendiment Natiu, sense superposició Natiu NAT, més latència Natiu
Recursos externs arriben al pod directament No

El problema que això causa a la pràctica: a EKS, cada pod consumeix una IP real de la teva VPC. Una instància t3.medium admet 3 interfícies de xarxa amb 6 IP cadascuna: 17 pods màxim, independentment que tingui CPU i memòria lliures. Si la teva subxarxa és un /24 (254 adreces), et quedes sense IP per a pods molt abans de quedar-te sense CPU.

# A EKS: comprovar el límit real de pods d'un node
kubectl get node ip-10-0-1-42.eu-west-1.compute.internal \
  -o jsonpath='{.status.allocatable.pods}{"\n"}'
17

I el símptoma quan s'esgota:

Warning  FailedCreatePodSandBox  2m  kubelet
  Failed to create pod sandbox: plugin type="aws-cni" failed:
  add cmd: failed to assign an IP address to container

Un pod Pending sense que falti CPU ni memòria. Desconcertant si no coneixes el model.

Mitigacions a EKS: activar el mode de prefixos delegats (ENABLE_PREFIX_DELEGATION), que multiplica per 16 les IP disponibles per interfície; fer servir instàncies més grans; o planificar rangs de VPC generosos des del principi (un /16, no un /24).

A AKS amb Azure CNI clàssic, cal reservar per endavant maxPods × nombre de nodes adreces. Si planifiques malament, no pots escalar. La variant "superposició" (Azure CNI Overlay) resol això i és la recomanada avui.

A GKE amb natiu de VPC, es fan servir rangs secundaris d'IP àlies, cosa que separa l'adreçament de pods del de nodes. És el model més net dels tres.

Integració d'identitat

EKS AKS GKE
Mecanisme IRSA (IAM Roles for Service Accounts) o Pod Identity Workload Identity (basat en Entra ID) Workload Identity Federation
Base Token OIDC projectat al pod Token OIDC Token OIDC
Maduresa Molt alta (des de 2019) Alta Molt alta (el més antic)
Complexitat de configuració Mitjana Mitjana Baixa

Els tres han convergit en el mateix mecanisme: OIDC. El detallem a l'apartat 3.

Grups de nodes i modes automàtics

EKS AKS GKE
Grups gestionats Sí (agrupacions de nodes) Sí (agrupacions)
Mode sense gestió de nodes Fargate (per pod) Nodes virtuals (ACI) Autopilot
Autoescalador de nodes Cluster Autoscaler o Karpenter Cluster Autoscaler o NAP Integrat o aprovisionament automàtic
Aprovisionament automàtic de tipus Amb Karpenter Sí (NAP)

GKE Autopilot mereix menció a part: és un mode en què no hi ha nodes en absolut des del teu punt de vista. Declares pods amb les seves requests i pagues exactament per això. No hi ha nodes per pedaçar ni per dimensionar, no hi ha DaemonSets propis, no hi ha hostPath ni privilegis. És Kubernetes reduït a la seva interfície de càrregues de treball.

  • A favor: zero operació de nodes, facturació exacta pel sol·licitat, seguretat reforçada per defecte.
  • En contra: restriccions fortes (no pots executar Falco de 08-06 tal com és, ni certs DaemonSets), cost per unitat més gran si els teus pods estan mal dimensionats, menys control.

AWS Fargate és similar però més limitat: no admet DaemonSets en absolut, no admet emmagatzematge persistent en bloc, ni privileged. Per a worker-notificacions pot encaixar; per a postgres-reserves, no.

Autoescalat

EKS AKS GKE
Cluster Autoscaler L'instal·les tu Integrat, s'activa Integrat
Alternativa avançada Karpenter (recomanada) NAP Aprovisionament automàtic
Velocitat d'arrencada de node 40-90 s (Karpenter: ~40 s) 60-120 s 40-80 s
Elecció automàtica del tipus d'instància Karpenter sí NAP sí

Recordant 09-03: Karpenter no treballa amb grups de nodes predefinits, sinó que tria el tipus d'instància òptim per als pods pendents. Si informes-ocupacio necessita 8 CPU un cop per nit, Karpenter aixeca la instància justa, la fa servir i la retira. És una diferència notable de cost i de velocitat.

Complements gestionats i ecosistema

Complement EKS AKS GKE
CNI Gestionat (VPC CNI) Gestionat Gestionat
CoreDNS, kube-proxy Gestionats Gestionats Gestionats
Controlador CSI Gestionat Gestionat Gestionat
Controlador d'Ingress AWS Load Balancer Controller (l'instal·les) Application Gateway (gestionat) GKE Ingress (natiu)
Mètriques i registres CloudWatch Container Insights Azure Monitor Cloud Operations (el més integrat)
Malla de serveis App Mesh (en retirada) / Istio Istio gestionat Anthos Service Mesh
Escaneig d'imatges ECR scanning Defender for Containers Artifact Analysis
Política Cap de nativa Azure Policy Policy Controller

Taula resum

Criteri EKS AKS GKE
Cost del pla de control Mitjà Baix (gratis sense SLA) Mitjà (1 de gratuït)
Finestra de suport Llarga (14 m) Mitjana (12 m) Llarga (14 m)
Model de xarxa IP real, límit per instància Flexible, requereix planificació El més net
Identitat IRSA, molt madur Workload Identity El més senzill
Autoescalat Karpenter, el millor Bo Molt bo
Mode sense nodes Fargate (limitat) Nodes virtuals Autopilot, el millor
Facilitat d'operació Mitjana Mitjana-alta Alta
Ecosistema i eines El més gran Bo Molt bo
Integració amb la resta del núvol Molt bona Excel·lent si ja fas servir Microsoft Molt bona
Corba d'aprenentatge Alta (IAM d'AWS) Mitjana Baixa

Conclusió honesta: la millor elecció gairebé sempre és el núvol on ja hi ha la resta de la teva infraestructura. La diferència tècnica entre els tres és menor que el cost d'operar en dos núvols alhora. Si comences de zero i només mires Kubernetes, GKE és el més polit; si la teva empresa ja viu a AWS, EKS amb Karpenter és excel·lent; si la teva empresa és un entorn Microsoft, AKS s'integra sense fricció.

  1. Identitat: IRSA i Workload Identity

Tanquem aquí el que va quedar anunciat a 03-06.

El problema

api-reserves necessita llegir un fitxer de tarifes d'un bucket d'emmagatzematge i publicar missatges en una cua. La forma tradicional:

# LA FORMA DOLENTA. No ho facis.
apiVersion: v1
kind: Secret
metadata:
  name: credencials-nuvol
stringData:
  ACCES_ID: "AKIAIOSFODNN7EXAMPLE"
  ACCES_SECRET: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

Problemes: la credencial és permanent, cal rotar-la a mà, està en un Secret (base64, no xifrat, 03-02), i si es filtra serveix des de qualsevol lloc del món.

La solució: federació OIDC

Els tres proveïdors convergeixen en el mateix mecanisme. El clúster exposa un emissor OIDC; el kubelet projecta al pod un token signat de curta durada amb la identitat de la ServiceAccount; el proveïdor de núvol confia en aquest emissor i bescanvia el token per credencials temporals.

sequenceDiagram
    participant P as Pod api-reserves
    participant K as kubelet
    participant O as Emissor OIDC<br/>del clúster
    participant I as IAM del proveïdor
    participant S as Bucket / Cua

    K->>O: sol·licita token per a la SA
    O-->>K: token JWT signat (1 h)
    K->>P: el projecta a<br/>/var/run/secrets/.../token
    P->>I: "bescanvia aquest token per credencials"
    I->>O: verifica la signatura contra les claus públiques
    I-->>P: credencials temporals (15 min - 1 h)
    P->>S: hi accedeix amb aquestes credencials

Cap credencial permanent. Res a rotar. El token es renova sol i només serveix per a aquella ServiceAccount en aquell clúster.

IRSA a EKS

# 1. Crear el proveïdor d'identitat OIDC del clúster (una sola vegada)
eksctl utils associate-iam-oidc-provider --cluster rutas-norte-pro --approve

# 2. Crear el rol i associar-lo a la ServiceAccount
eksctl create iamserviceaccount --name api-reserves --namespace rutas-norte-pro \
  --cluster rutas-norte-pro \
  --attach-policy-arn arn:aws:iam::123456789012:policy/RutasNorteTarifesLectura --approve

La màgia és a la política de confiança del rol, la condició de la qual limita qui el pot assumir:

"Condition": { "StringEquals": {
  "oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLE...:sub":
    "system:serviceaccount:rutas-norte-pro:api-reserves",
  "oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLE...:aud": "sts.amazonaws.com"
}}

Fixa't en sub: només la ServiceAccount api-reserves del namespace rutas-norte-pro pot assumir aquest rol. Ni una altra SA, ni la mateixa SA en un altre namespace.

# El manifest que va a Git: una anotació, cap secret
apiVersion: v1
kind: ServiceAccount
metadata:
  name: api-reserves
  namespace: rutas-norte-pro
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/RutasNorteApiReserves

Al Deployment n'hi ha prou amb serviceAccountName: api-reserves: sense variables de credencials, perquè l'SDK del proveïdor detecta el token projectat automàticament. Pots verificar-ho amb kubectl exec ... -- ls /var/run/secrets/eks.amazonaws.com/serviceaccount/.

Nota: AWS ha introduït EKS Pod Identity, més senzill (una associació a l'API d'EKS, sense proveïdor OIDC ni anotació). És l'opció recomanada per a clústers nous; IRSA continua sent necessària si la càrrega corre fora d'EKS.

Workload Identity a GKE i a AKS

El mecanisme és el mateix; canvien les ordres i l'anotació.

# --- GKE ---
gcloud container clusters update rutas-norte-pro \
  --workload-pool=rutas-norte-projecte.svc.id.goog --region=europe-west1
gcloud iam service-accounts create rn-api-reserves
gcloud iam service-accounts add-iam-policy-binding \
  [email protected] \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:rutas-norte-projecte.svc.id.goog[rutas-norte-pro/api-reserves]"

# --- AKS ---
az aks update -g rutas-norte -n rutas-norte-pro --enable-oidc-issuer --enable-workload-identity
EMISSOR=$(az aks show -g rutas-norte -n rutas-norte-pro --query "oidcIssuerProfile.issuerUrl" -otsv)
az identity create -g rutas-norte -n rn-api-reserves
az identity federated-credential create --name rn-api-reserves-fed \
  --identity-name rn-api-reserves --resource-group rutas-norte --issuer "${EMISSOR}" \
  --subject "system:serviceaccount:rutas-norte-pro:api-reserves"
EKS (IRSA) AKS GKE
Anotació a la SA eks.amazonaws.com/role-arn azure.workload.identity/client-id iam.gke.io/gcp-service-account
Requisit extra al pod Cap Etiqueta azure.workload.identity/use: "true" Cap
Passos de configuració 3 4 3
Durada de les credencials 15 min - 12 h 1 h 1 h
Alternativa més simple EKS Pod Identity

L'important per a tu: en els tres casos, el manifest que va a Git conté una anotació amb un identificador, mai un secret. És exactament el model que buscàvem a 03-06 i que encaixa perfectament amb GitOps (10-05).

  1. Nodes: grups gestionats, plantilles i instàncies interrompibles

Grups de nodes gestionats

Un grup de nodes gestionat és un conjunt de màquines homogènies que el proveïdor manté: les crea, les uneix al clúster, les actualitza de forma gradual i les substitueix si fallen.

# Exemple amb eksctl per a Rutas Norte
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata: { name: rutas-norte-pro, region: eu-west-1, version: "1.30" }

managedNodeGroups:
  # Grup general: càrregues que serveixen trànsit de clients
  - name: general
    instanceType: m6i.large
    minSize: 3
    maxSize: 12
    desiredCapacity: 4
    availabilityZones: [eu-west-1a, eu-west-1b, eu-west-1c]  # repartiment real (09-05)
    volumeSize: 60
    volumeType: gp3
    updateConfig: { maxUnavailablePercentage: 25 }

  # Grup interrompible: càrregues tolerants a interrupció
  - name: interrompible
    instanceTypes: [m6i.large, m5.large, m5a.large, m6a.large]
    spot: true                        # <-- instàncies interrompibles
    minSize: 0
    maxSize: 20
    labels: { rutasnorte.example/interrompible: "true" }
    taints:
      # Res no es planifica aquí tret que ho toleri explícitament
      - { key: rutasnorte.example/interrompible, value: "true", effect: NoSchedule }

Fixa't en instanceTypes amb quatre tipus al grup interrompible: com més tipus acceptes, menor és la probabilitat que el proveïdor no tingui capacitat i t'ho interrompi tot alhora.

Instàncies interrompibles (spot)

Són capacitat sobrant del proveïdor a un 60-90 % de descompte, amb una contrapartida: el proveïdor la pot reclamar amb un avís de 2 minuts (AWS), 30 segons (Azure) o 30 segons (GCP).

Càrrega de Rutas Norte Apta per a spot? Per què
botiga-web No De cara al client; una interrupció és una venda perduda
api-reserves No Igual, i a més manté sessions de reserva
postgres-reserves Rotundament no Estat crític; una interrupció durant una escriptura és un risc
redis-cache Amb compte És memòria cau, però perdre-la de cop provoca una allau sobre la base de dades
worker-notificacions Processa una cua; si un pod mor, el missatge torna a la cua
informes-ocupacio CronJob nocturn; si falla, reintenta

Els manifestos, amb el que hem après a 06-05 i 09-05 (el detall complet és a la solució de l'exercici 1):

    spec:
      # 1. TOLERAR el taint del grup interrompible
      tolerations:
        - { key: rutasnorte.example/interrompible, operator: Equal,
            value: "true", effect: NoSchedule }

      # 2. PREFERIR nodes interrompibles, però acceptar els normals si no
      #    hi ha capacitat (preferredDuringScheduling, NO required)
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - { key: rutasnorte.example/interrompible, operator: In, values: ["true"] }

      # 3. REPARTIR entre zones: una zona sencera es pot quedar sense
      #    capacitat interrompible de cop
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: ScheduleAnyway
          labelSelector:
            matchLabels: { app: worker-notificacions }

      # 4. Marge per acabar el missatge en curs dins de l'avís
      terminationGracePeriodSeconds: 90
      containers:
        - name: worker
          lifecycle:
            preStop:
              exec: { command: ["/app/drenar", "--espera", "60s"] }

I un PDB amb minAvailable: 1, perquè encara que siguin interrompibles, no volem que caiguin tots alhora (09-05).

Matís important sobre el PDB: un PodDisruptionBudget protegeix davant d'interrupcions voluntàries (drenatge d'un node per actualitzar-lo). Una interrupció d'una instància spot és involuntària: el proveïdor s'emporta la màquina, amb PDB o sense. El PDB ajuda perquè els gestors de terminació (AWS Node Termination Handler, o el maneig natiu de Karpenter) cordonen i drenen el node en rebre l'avís, i aquell drenatge que respecta el PDB.

informes-ocupacio, en ser un CronJob nocturn (06-03), és un cas encara més clar: a més de la toleration pot portar un nodeSelector dur sobre rutasnorte.example/interrompible: "true" —si no hi ha capacitat, esperar és acceptable— i un backoffLimit: 6 generós, perquè la interrupció és esperable.

Karpenter: l'alternativa moderna a EKS

En lloc de definir grups amb tipus fixos, declares restriccions i Karpenter tria:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata: { name: rutas-norte-lots }
spec:
  template:
    spec:
      taints:
        - { key: rutasnorte.example/interrompible, value: "true", effect: NoSchedule }
      requirements:
        - { key: karpenter.sh/capacity-type, operator: In, values: ["spot"] }
        - { key: kubernetes.io/arch, operator: In, values: ["amd64", "arm64"] }  # ARM és més barat
        - { key: karpenter.k8s.aws/instance-category, operator: In, values: ["c", "m", "r"] }
        - { key: topology.kubernetes.io/zone, operator: In,
            values: ["eu-west-1a", "eu-west-1b", "eu-west-1c"] }
      nodeClassRef: { group: karpenter.k8s.aws, kind: EC2NodeClass, name: predeterminat }
  limits: { cpu: "200", memory: 400Gi }
  disruption:
    # Consolidar: si els pods caben en menys nodes, migrar-los i
    # retirar els sobrants. Estalvi directe.
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 60s
    expireAfter: 720h                    # renovar nodes cada 30 dies

Diferències amb el Cluster Autoscaler (09-03):

Cluster Autoscaler Karpenter
Com decideix Escala grups predefinits Tria la instància òptima per als pods pendents
Tipus d'instància Els del grup Centenars, segons les restriccions
Velocitat 60-120 s ~40 s
Consolidació Limitada Activa i agressiva
Interrupció de spot Amb un manejador a part Integrada
Disponibilitat Els tres proveïdors Només AWS (i Azure en desenvolupament)

  1. Què canvia en el que ja saps

Aquest apartat repassa les lliçons anteriors del curs i assenyala què és diferent al núvol.

La StorageClass (05-04)

A minikube fèiem servir standard, un directori del node. Al núvol:

# EKS amb EBS gp3
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata: { name: rutas-norte-ssd }
provisioner: ebs.csi.aws.com
parameters: { type: gp3, iops: "6000", throughput: "250", encrypted: "true" }
allowVolumeExpansion: true
# CRÍTIC: el volum no es crea fins que el pod es planifica,
# perquè es creï a la MATEIXA zona que el node.
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
Aspecte Local Núvol
Modes d'accés Tot "funciona" amb un node ReadWriteOnce de debò: un disc de bloc només es munta en una màquina
ReadWriteMany Amb hostPath, sí Requereix sistema de fitxers compartit (EFS, Azure Files, Filestore), més car i més lent
Zones No existeixen Un disc pertany a una zona. Si el pod es planifica en una altra, no arrenca
Expansió De vegades no Sí, en calent
Xifratge en repòs No Sí, amb claus gestionades
Cost 0 Per GB/mes i per IOPS

L'error clàssic: postgres-reserves amb volumeBindingMode: Immediate crea el disc a eu-west-1a; el planificador col·loca el pod a eu-west-1c; el pod es queda Pending per sempre amb volume node affinity conflict. Solució: WaitForFirstConsumer, que és el valor que porten per defecte les StorageClass dels tres proveïdors.

El Service de tipus LoadBalancer (04-02)

En local es quedava a <pending> i calia minikube tunnel. Al núvol funciona de debò:

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
  annotations:
    # Balancejador de xarxa (capa 4), més ràpid i barat que el de capa 7
    service.beta.kubernetes.io/aws-load-balancer-type: "external"
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
  type: LoadBalancer
  # Conservar la IP real del client. Sense això, els registres i el
  # limitador de taxa veuen la IP del node, no la de l'usuari.
  externalTrafficPolicy: Local

Coses per aprendre:

  • Cada Service de tipus LoadBalancer costa diners (uns 20-25 $/mes més el trànsit). Per això se'n fa servir un de sol per al controlador d'Ingress i tota la resta va per regles d'Ingress (04-04).
  • Les anotacions són específiques de cada proveïdor. Són la part menys portable dels teus manifestos (apartat 8).
  • Esborrar el Service esborra el balancejador. I si el balancejador té un registre DNS apuntant-hi, es trenca.
  • externalTrafficPolicy: Local conserva la IP del client però requereix que hi hagi un pod a cada node amb endpoints; si no, el trànsit a aquell node es descarta.

Els tres proveïdors ofereixen a més un controlador d'Ingress gestionat (AWS Load Balancer Controller, Application Gateway Ingress Controller, GKE Ingress) que crea un balancejador de capa 7 directament des de l'objecte Ingress. És còmode, però et lliga més al proveïdor que ingress-nginx.

Registres d'imatges (08-05)

Proveïdor Registre Escaneig Autenticació des del clúster
AWS ECR Bàsic o millorat (Inspector) Per rol del node o IRSA, sense imagePullSecrets
Azure ACR Defender for Containers Vinculació directa amb AKS
GCP Artifact Registry Artifact Analysis Per compte de servei del node

L'avantatge pràctic: desapareix l'imagePullSecrets. El node s'autentica amb la seva identitat de núvol. Un Secret menys per gestionar.

I s'integra amb el de 08-05: la signatura amb Cosign i la política d'admissió funcionen igual, amb l'avantatge que els tres registres ofereixen escaneig automàtic que alimenta el que hem après a 08-06.

Mètriques i registres (07-03, 07-05)

Prometheus propi Servei del proveïdor
Cost El de l'emmagatzematge i els nodes Per mètrica o per GB ingerit
Retenció llarga Requereix Thanos o Mimir Inclosa
Consultes PromQL AWS i GCP sí (compatibles); Azure amb KQL
Portabilitat Total Nul·la
Operació Teva Del proveïdor
Correlació amb la resta del núvol Manual Nativa

Consell pragmàtic: mantenir Prometheus (07-03) per a les mètriques de les aplicacions, perquè les teves alertes i panells són portables, i fer servir el servei del proveïdor per als registres (07-05), perquè operar EFK a escala és car i dolorós, i el cost del servei gestionat sol sortir a compte. És la combinació que tria la majoria d'equips.

Compte amb el cost dels registres: api-reserves amb LOG_LEVEL=debug en producció pot generar centenars de GB al mes. Els tres proveïdors cobren per ingesta.

  1. Actualització de versió d'un clúster gestionat

Comparat amb el procediment de kubeadm (10-02), això és un passeig. Però continua exigint planificació, i qui ho tracta com un botó s'emporta un disgust.

# EKS: pla de control i grups de nodes, per separat
aws eks update-cluster-version --name rutas-norte-pro --kubernetes-version 1.31
aws eks update-nodegroup-version --cluster-name rutas-norte-pro --nodegroup-name general

# AKS
az aks upgrade -g rutas-norte -n rutas-norte-pro --kubernetes-version 1.31.1

# GKE
gcloud container clusters upgrade rutas-norte-pro --master --cluster-version 1.31.1
gcloud container clusters upgrade rutas-norte-pro --node-pool general

Per què continua exigint planificació

1. Les APIs eliminades continuen sent el teu problema. Si un manifest de k8s/base fa servir una API que desapareix a 1.31, el clúster s'actualitza i la teva aplicació deixa de desplegar-se.

# Abans d'actualitzar, SEMPRE
kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis

# I escanejar els manifestos
kubent --context rutas-norte-pro
pluto detect-files -d k8s/

2. Els complements de tercers tenen les seves pròpies matrius de compatibilitat. cert-manager, ingress-nginx, KEDA, Prometheus Operator, el controlador CSI: cadascun suporta un rang de versions. Una actualització del clúster sense actualitzar-los els pot trencar.

Complement Comprovar abans
cert-manager Matriu de compatibilitat del projecte
ingress-nginx Taula de versions suportades
KEDA Versió mínima de Kubernetes
Prometheus Operator Versió de les CRDs
Argo CD Versió suportada de l'apiserver
Controlador CSI del proveïdor Versió del complement gestionat

3. L'actualització dels nodes reinicia tots els pods. El proveïdor drena cada node per torns. Això exercita:

  • Els teus PodDisruptionBudgets (09-05): si estan mal configurats, el drenatge es bloqueja o, pitjor, se salta amb --force.
  • Les teves sondes de readiness (07-01): si api-reserves triga 45 segons a estar llest i el drenatge va ràpid, hi ha tall.
  • El teu terminationGracePeriodSeconds i el teu hook preStop.
  • La teva capacitat d'absorbir la pèrdua temporal d'un node: si el clúster està al 95 % d'ocupació, drenar un node deixa pods Pending.

4. El pla de control i els nodes s'actualitzen per separat, i cal respectar el desfasament de versions (10-02): nodes com a molt 3 menors per sota de l'apiserver, i mai per sobre.

Procediment recomanat per a Rutas Norte

1. Llegir les notes de la versió i les APIs eliminades.
2. Executar kubent/pluto sobre k8s/ i corregir el que surti.
3. Revisar la matriu de compatibilitat de cada complement;
   actualitzar els que calgui ABANS.
4. Actualitzar rutas-norte-dev. Esperar una setmana d'ús normal.
5. Actualitzar rutas-norte-pre. Executar les proves de càrrega amb k6 (09-06).
6. Verificar que els PDB permeten el drenatge:
     kubectl get pdb -A -o wide
     kubectl drain <un-node> --dry-run=server
7. Finestra de manteniment a rutas-norte-pro:
     a. Pla de control primer.
     b. Verificar que tot continua Synced i Healthy a Argo CD.
     c. Un grup de nodes cada vegada, amb maxUnavailable baix.
     d. Vigilar els panells de Grafana durant el procés.
8. Actualitzar la versió declarada a l'script local de 10-01
   perquè l'equip desenvolupi contra la mateixa versió.

Aquest últim punt tanca el cercle amb la primera lliçó del mòdul: l'entorn local ha de seguir producció.

  1. Cost real i palanques d'estalvi

De què es compon la factura

Concepte Pes típic Comentari
Nodes (còmput) 50-70 % La partida gran, gairebé sempre
Emmagatzematge 10-20 % Discos dels PV, snapshots, registres
Trànsit de xarxa 5-20 % Sortida a internet i entre zones
Balancejadors 5-10 % ~20-25 $/mes cadascun
Pla de control 2-5 % ~73 $/mes per clúster
Serveis gestionats Variable Mètriques, registres, escaneig

Una sorpresa habitual: el trànsit entre zones de disponibilitat es cobra. Si api-reserves és a la zona A i postgres-reserves a la zona B, cada consulta creua una frontera facturada. Amb volum alt, això són centenars d'euros al mes. És un argument a favor de l'afinitat de zona... que xoca amb l'alta disponibilitat de 09-05. És un compromís real que cal decidir conscientment.

Les palanques, per ordre de rendibilitat

1. Dimensionar bé (el que hem après a 09-02). És la palanca amb millor relació esforç/estalvi, i gairebé ningú la fa servir. El patró típic: algú va posar requests: {cpu: 1, memory: 2Gi} "per si de cas" i el pod fa servir 80m i 200Mi. El planificador reserva el que s'ha demanat, així que estàs pagant 12 vegades el que fas servir.

kubectl top pods -n rutas-norte-pro --sort-by=memory
kubectl describe vpa api-reserves -n rutas-norte-pro   # VPA en mode Off: recomana sense tocar
  Container Recommendations:
    Container Name:  api
    Target:  Cpu: 127m   Memory: 312Mi

Ajustar requests a la recomanació a tota la plataforma sol reduir la factura de còmput entre un 30 i un 50 %.

2. Autoescalat real (09-01 i 09-03). Amb l'HPA ajustant pods i Karpenter o el Cluster Autoscaler ajustant nodes, pagues per la càrrega real i no pel pic anual. Rutas Norte té pics als ponts: sense autoescalat, o dimensiones per al pont tot l'any, o caus el dia del pont.

3. Instàncies interrompibles. 60-90 % de descompte a worker-notificacions i informes-ocupacio.

4. Apagar els entorns que no es fan servir de nit. rutas-norte-dev no fa res de les 20:00 a les 8:00 ni els caps de setmana. Això són 128 de 168 hores setmanals: un 76 % d'estalvi en aquell entorn.

# CronJob que escala a zero durant la nit (06-03). El d'encesa és
# idèntic amb schedule "0 8 * * 1-5" i --replicas=1.
apiVersion: batch/v1
kind: CronJob
metadata: { name: apagar-dev, namespace: rutas-norte-dev }
spec:
  schedule: "0 20 * * 1-5"
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: escalador-entorn
          restartPolicy: OnFailure
          containers:
            - name: kubectl
              image: bitnami/kubectl:1.30.4
              command: ["sh", "-c", "kubectl scale deploy,statefulset --all --replicas=0 -n rutas-norte-dev"]

Atenció amb GitOps: si Argo CD té selfHeal actiu a rutas-norte-dev, revertirà l'escalat a zero en tres minuts. Per això a la solució de l'exercici 1 de 10-05 vam deixar selfHeal: false a dev. L'alternativa neta és escalar el grup de nodes en comptes dels Deployments, o fer servir una finestra de sincronització de l'AppProject.

5. Compromisos d'ús. Els tres proveïdors descompten un 30-55 % a canvi de comprometre una despesa o una capacitat durant 1 o 3 anys (Savings Plans i Reserved Instances a AWS, Reservations a Azure, Committed Use Discounts a GCP).

Estratègia recomanada: comprometre només la base estable (els nodes que estan encesos sempre), deixar el pic sota demanda i les càrregues tolerants en spot.

flowchart TB
    A["Càrrega total"] --> B["Base estable<br/>compromís 1-3 anys<br/>-40%"]
    A --> C["Pics<br/>sota demanda<br/>preu complet"]
    A --> D["Càrregues tolerants<br/>interrompibles<br/>-70%"]
    style B fill:#e8ffe8
    style D fill:#e8f4ff

6. Visibilitat del cost per equip. Eines com OpenCost o Kubecost reparteixen la despesa per namespace, etiqueta o equip. Sense això, ningú sap que informes-ocupacio costa 400 €/mes i ningú té incentiu per arreglar-ho. Les etiquetes de 02-07 (app.kubernetes.io/part-of, entorn) són la base d'aquell repartiment.

Resum de palanques

Palanca Estalvi típic Esforç Risc
Ajustar requests amb el VPA 30-50 % del còmput Baix Baix si es fa per fases
Autoescalat de pods i nodes 20-40 % Mitjà Baix
Instàncies interrompibles 60-90 % de les càrregues aptes Mitjà Mitjà (cal preparar l'app)
Apagar entorns de nit 60-75 % de dev/pre Baix Molt baix
Compromisos d'ús 30-55 % de la base Baix Mitjà (compromís a anys)
Reduir trànsit entre zones 5-15 % Alt Xoca amb l'HA
Revisar retenció de registres 10-30 % d'observabilitat Baix Baix

  1. Portabilitat i dependència del proveïdor

Tot això val de poc si d'aquí a dos anys vols canviar de núvol i no pots. Serem precisos sobre què és portable a Rutas Norte.

Què és portable

Element Portable Comentari
Deployment, StatefulSet, DaemonSet, Job, CronJob Total API estàndard
Service, Ingress (regles) Total Les regles sí; les anotacions no
ConfigMap, Secret Total
RBAC, ServiceAccount Total Tret de les anotacions d'identitat
HPA, PDB, topologySpreadConstraints Total
NetworkPolicy Total Si el CNI destí les implementa
PVC (nom de la classe) Alta El PVC és portable; la StorageClass cal redefinir-la
Manifestos de Kustomize Total
Charts de Helm Total
Applications d'Argo CD Total
Panells i alertes de Prometheus Total Si fas servir Prometheus i no el servei del proveïdor

Què NO és portable

Element Per què Cost de migrar
Anotacions de Service LoadBalancer service.beta.kubernetes.io/aws-* no existeix a Azure Baix: reescriure un bloc
parameters de la StorageClass ebs.csi.aws.com davant de disk.csi.azure.com Baix: un fitxer
Anotació d'identitat a la SA IRSA / Workload Identity Baix: una anotació
IngressClass del proveïdor ALB, Application Gateway, GKE Ingress Mitjà
Serveis de dades gestionats (RDS, Cloud SQL) Fora de Kubernetes Alt
Cues i missatgeria gestionades (SQS, Service Bus) Fora de Kubernetes Alt
Mètriques i registres del proveïdor Consultes i panells propietaris Alt
Infraestructura com a codi específica CloudFormation, ARM, Deployment Manager Mitjà (Terraform ho mitiga)

El patró evident: el que està dins de Kubernetes és portable; el que està fora no ho és. La dependència del proveïdor no ve d'EKS, AKS o GKE, sinó dels serveis que envolten el clúster.

Com minimitzar la dependència

1. Aïlla el que no és portable a les superposicions de Kustomize. Ja ho tens muntat des de 10-04:

k8s/
├── base/                      <- 100 % portable
├── components/
│   ├── proveidor-aws/         <- StorageClass, anotacions IRSA, ALB
│   ├── proveidor-azure/
│   └── proveidor-gcp/
└── entorns/pro/
    └── kustomization.yaml     <- activa el component del proveïdor actual

Canviar de núvol és canviar una línia a components:. La base no es toca.

2. Fes servir ingress-nginx en comptes del controlador del proveïdor. És una capa d'abstracció que costa una mica de rendiment i estalvia una migració sencera. Les regles d'Ingress i les anotacions de nginx funcionen igual als tres núvols.

3. Fes servir Prometheus per a les mètriques. Les teves alertes i panells són el coneixement operatiu acumulat d'anys. En PromQL són portables; en el llenguatge de consulta del proveïdor, no.

4. Terraform en comptes de l'eina nativa. No fa la infraestructura portable, però sí el model mental i els fluxos de treball.

5. Decideix conscientment sobre les bases de dades. Executar postgres-reserves com a StatefulSet amb un operador (06-01, 06-07) és portable; fer servir el servei gestionat del proveïdor és més còmode i menys portable. Totes dues són decisions defensables; el que no val és prendre-la sense adonar-se'n.

PostgreSQL a Kubernetes Servei gestionat
Portabilitat Alta Baixa
Operació (còpies, rèpliques, pedaços) Teva, amb l'operador Del proveïdor
Cost Menor Major
Rendiment a gran escala Requereix ajust Optimitzat
Risc de pèrdua de dades Depèn de la teva diligència Menor

6. Sigues realista. La portabilitat total costa diners i complexitat, i moltes empreses mai canvien de núvol. Un objectiu sensat: "podríem migrar en tres mesos amb esforç, no en tres dies". Això et dona poder de negociació i et protegeix d'un canvi de preus abusiu, sense pagar l'impost de l'abstracció total.

  1. La decisió per a Rutas Norte

Les dades del cas

  • Empresa petita: dues persones a plataforma, sis a desenvolupament, tres a suport.
  • Cap cobertura de guàrdia 24×7.
  • Trànsit amb pics forts i previsibles: ponts i vacances, 8-10 vegades la càrrega base.
  • Dades personals de clients a postgres-reserves: exigències del RGPD sobre localització i xifratge.
  • Pressupost ajustat.
  • Ja fa servir emmagatzematge d'objectes i correu transaccional d'AWS per a altres coses.

Descartem el clúster autogestionat

Amb dues persones a plataforma i sense guàrdia, kubeadm (10-02) queda descartat. La pregunta decisiva és: qui restaura etcd un diumenge a les 4 de la matinada? Si la resposta és "esperem al dilluns", no es pot operar rutas-norte-pro amb un clúster propi. L'estalvi del pla de control (uns 220 €/mes amb tres clústers) no compensa ni de bon tros el risc.

L'elecció: EKS

Criteri Pes Valoració
Ja fan servir AWS per a altres serveis Alt Un sol núvol, una sola facturació, una sola identitat
Karpenter per als pics dels ponts Alt El millor autoescalat de nodes dels tres
Spot per a worker-notificacions i informes-ocupacio Alt Estalvi directe sobre càrregues ja identificades com a tolerants
IRSA molt madur Mitjà Tanca 03-06 sense credencials a Secrets
Regions europees (RGPD) Alt eu-west-1 compleix
Finestra de suport de 14 mesos Mitjà Amb un equip petit, més temps entre actualitzacions és valuós
Cost del pla de control Baix 73 $/mes és assumible
Corba d'aprenentatge d'IAM Mitjà (en contra) És el punt fluix, però l'equip ja el coneix

Si Rutas Norte no fes servir ja AWS, la recomanació seria GKE: el model de xarxa més net, Workload Identity més senzilla, canals de versió que redueixen la feina d'actualització, i Autopilot com a opció per als entorns no productius.

El disseny concret

Compte d'AWS, regió eu-west-1

CLÚSTER 1: rutas-norte-pro
  Kubernetes 1.30, pla de control regional (3 zones)
  Grups de nodes:
    - general:      m6i.large, 3-12 nodes, sota demanda,
                    3 zones, amb compromís d'ús sobre 3 nodes
    - interrompible: gestionat per Karpenter, spot, 0-20,
                     amb taint rutasnorte.example/interrompible
  Càrregues:
    botiga-web, api-reserves, postgres-reserves, redis-cache -> general
    worker-notificacions, informes-ocupacio -> interrompible

CLÚSTER 2: rutas-norte-no-productiu
  Namespaces rutas-norte-dev i rutas-norte-pre
  Grup únic: m6i.large, 2-6 nodes, MAJORIA SPOT
  Apagada automàtica de 20:00 a 8:00 i caps de setmana

PLATAFORMA (als dos clústers, per GitOps):
  Argo CD, cert-manager, ingress-nginx, KEDA,
  kube-prometheus-stack, External Secrets Operator, Velero

DADES:
  postgres-reserves com a StatefulSet amb operador (portabilitat)
  Còpies amb Velero + snapshots d'EBS, replicades a una altra regió

Dos clústers, no tres. Separar pro de la resta és una frontera de seguretat real; separar dev de pre no ho justifica: els namespaces amb quotes (03-04) i NetworkPolicies (04-06) són suficients, i estalvien 73 $/mes més l'operació.

El cost estimat

Concepte Mensual
Pla de control × 2 clústers 146 $
Nodes de pro: 3 sota demanda amb compromís + 1 de mitjana ~210 $
Nodes interrompibles de pro (mitjana 2) ~35 $
Nodes no productius (spot, 40 h/setmana) ~45 $
Emmagatzematge EBS (300 GB gp3 + snapshots) ~45 $
Balancejadors (1 per clúster) ~50 $
Trànsit de sortida i entre zones ~60 $
Registre i mètriques gestionades ~40 $
Total aproximat ~630 $/mes

Sense les palanques d'estalvi (tot sota demanda, sense spot, sense apagada nocturna, sense compromisos, amb requests sobredimensionades) la mateixa plataforma costaria de l'ordre de 1.400-1.600 $/mes. Les palanques de l'apartat 7 no són teoria: són més de la meitat de la factura.

La conclusió honesta

Un clúster gestionat no és més barat que un de propi en cost d'infraestructura pura. És més barat en cost total, perquè inclou la feina que no has de fer i el risc que no assumeixes. Per a Rutas Norte S.L., amb dues persones a plataforma i sense guàrdia, és l'única opció defensable.

I hi ha una simetria que convé notar: gràcies a GitOps (10-05), la decisió és reversible. Tot l'estat de la plataforma és a Git. Si demà hi ha una raó per canviar de núvol o per muntar un clúster propi, s'aixeca el clúster nou, s'instal·la Argo CD, s'aplica l'Application arrel i en vint minuts la plataforma està dempeus. Les dades es migren a part, que és la part difícil, però la plataforma es reconstrueix sola.

Errors Comuns i Consells

1. Creure que "gestionat" inclou les còpies de les teves dades. El proveïdor fa còpia d'etcd, no dels teus volums. postgres-reserves és la teva responsabilitat: Velero (05-06) i snapshots, amb restauració assajada.

2. No planificar l'adreçament IP. A EKS amb VPC CNI, cada pod consumeix una IP real de la VPC. Un /24 s'esgota de seguida i el símptoma (FailedCreatePodSandBox) no menciona les IP de forma òbvia. Planifica rangs generosos i activa la delegació de prefixos.

3. Un Service de tipus LoadBalancer per aplicació. Cadascun costa 20-25 $/mes. Fes servir-ne un per al controlador d'Ingress i encamina amb regles d'Ingress (04-04).

4. StorageClass amb volumeBindingMode: Immediate en un clúster multizona. El disc es crea en una zona, el pod es planifica en una altra, i el pod no arrenca mai. Fes servir WaitForFirstConsumer.

5. Posar càrregues amb estat en instàncies interrompibles. postgres-reserves en spot és una recepta per perdre dades. Només càrregues que tolerin morir sense avís, amb terminationGracePeriodSeconds i hook preStop adequats.

6. Un sol tipus d'instància al grup interrompible. Si el proveïdor es queda sense aquell tipus, s'emporta tots els teus nodes alhora. Declara 4-6 tipus equivalents.

7. Continuar fent servir credencials estàtiques a Secrets. IRSA, Workload Identity i el seu equivalent a Azure eliminen les credencials permanents. És de les millores de seguretat amb millor relació cost/benefici que existeixen.

8. Actualitzar la versió sense comprovar les APIs eliminades ni els complements. El clúster s'actualitza sense problema i la teva aplicació deixa de desplegar-se, o ingress-nginx deixa d'arrencar. kubent i les matrius de compatibilitat, abans de tocar res.

9. requests sobredimensionades "per si de cas". És la principal causa de sobrecost a Kubernetes. El VPA en mode Off (09-02) et dona els números reals en una setmana.

10. Ignorar el trànsit entre zones. En aplicacions molt xerraires pot ser una partida important. Mesura-ho abans de decidir; és un compromís real amb l'alta disponibilitat de 09-05.

11. No etiquetar per al repartiment de costos. Sense entorn, app.kubernetes.io/part-of i una etiqueta d'equip, la factura és un número global que ningú pot reduir. Les etiquetes de 02-07 són la base d'OpenCost o Kubecost.

12. Adoptar Autopilot o Fargate sense comprovar les restriccions. Falco (08-06), alguns DaemonSets de xarxa i tot el que necessiti privilegis poden no funcionar. Prova primer en un entorn no productiu.

13. Confondre "menys operació" amb "sense operació". Continues sent responsable dels nodes, del RBAC, de les polítiques, dels secrets, de l'observabilitat i del cost. Els mòduls 2 a 9 d'aquest curs continuen sent enterament aplicables.

Exercicis

Exercici 1: manifestos per a instàncies interrompibles

Escriu el conjunt complet de manifestos que permet executar worker-notificacions en un grup de nodes interrompibles marcat amb el taint rutasnorte.example/interrompible=true:NoSchedule, sense deixar de funcionar si no hi ha capacitat spot. Ha d'incloure: tolerations, afinitat de node preferent, distribució topològica, terminationGracePeriodSeconds amb hook preStop, un PDB i un ScaledObject de KEDA. Explica per què fas servir afinitat preferent i no nodeSelector, i què protegeix realment el PDB davant d'una interrupció de spot.

Exercici 2: component de proveïdor amb Kustomize

Dissenya el component k8s/components/proveidor-aws/ que contingui tot l'específic d'AWS que Rutas Norte necessita: la StorageClass rutas-norte-ssd amb EBS gp3 xifrat, un pedaç que afegeixi l'anotació d'IRSA a les ServiceAccounts d'api-reserves i worker-notificacions, i un pedaç que afegeixi les anotacions de balancejador de xarxa al Service del controlador d'Ingress. Explica com s'activaria des de la superposició de pro i què caldria canviar per migrar a Azure.

Exercici 3: pla de reducció de costos

La factura de rutas-norte-pro és de 1.450 $/mes: 980 $ de còmput, 180 $ d'emmagatzematge, 120 $ de balancejadors (5 Services de tipus LoadBalancer), 90 $ de trànsit i 80 $ de registres. Tots els pods tenen requests: {cpu: 500m, memory: 1Gi} i kubectl top mostra un ús mitjà de 90m i 220Mi. No hi ha instàncies interrompibles ni compromisos. Proposa un pla prioritzat amb les ordres de diagnòstic, l'estalvi estimat de cada mesura i el risc associat.

Solucions

Solució 1

apiVersion: apps/v1
kind: Deployment
metadata:
  name: worker-notificacions
  namespace: rutas-norte-pro
spec:
  selector:
    matchLabels: { app: worker-notificacions }
  template:
    metadata:
      labels:
        app: worker-notificacions
        app.kubernetes.io/part-of: rutas-norte
    spec:
      tolerations:
        - key: rutasnorte.example/interrompible
          operator: Equal
          value: "true"
          effect: NoSchedule
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - { key: rutasnorte.example/interrompible, operator: In, values: ["true"] }
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: ScheduleAnyway
          labelSelector:
            matchLabels: { app: worker-notificacions }
      terminationGracePeriodSeconds: 90
      containers:
        - name: worker
          image: registry.rutasnorte.example/worker-notificacions:1.8.4
          lifecycle:
            preStop:
              exec: { command: ["/app/drenar", "--espera", "60s"] }
          resources:
            requests: { cpu: 200m, memory: 256Mi }
            limits:   { memory: 512Mi }
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: worker-notificacions, namespace: rutas-norte-pro }
spec:
  minAvailable: 1
  selector:
    matchLabels: { app: worker-notificacions }
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata: { name: worker-notificacions, namespace: rutas-norte-pro }
spec:
  scaleTargetRef: { name: worker-notificacions }
  minReplicaCount: 1
  maxReplicaCount: 30
  triggers:
    - type: redis
      metadata:
        address: redis-cache:6379
        listName: cua-correus
        listLength: "20"

Afinitat preferent i no nodeSelector: nodeSelector és una restricció dura. Si no hi ha capacitat spot a la regió —cosa que passa precisament als pics, quan més falta fa— els pods es quedarien Pending indefinidament. Amb preferredDuringScheduling, el planificador intenta el node interrompible i, si no n'hi ha, en fa servir un de sota demanda: es paga més, però el servei no s'atura.

Què protegeix el PDB: res davant de la interrupció en si (el proveïdor s'emporta la màquina). Protegeix durant el drenatge que el manejador de terminació (o Karpenter) executa en rebre l'avís de 2 minuts: aquell drenatge respecta el PDB, així que no es buiden dos nodes simultàniament deixant la cua sense consumidors.

Solució 2

# k8s/components/proveidor-aws/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
resources:
  - storageclass.yaml
patches:
  - path: irsa-patch.yaml
    target: { kind: ServiceAccount, name: "api-reserves|worker-notificacions" }
  - path: lb-patch.yaml
    target: { kind: Service, name: ingress-nginx-controller }

storageclass.yaml és el de l'apartat 5 (provisioner ebs.csi.aws.com, gp3 xifrat, WaitForFirstConsumer). Els dos pedaços són fusions estratègiques mínimes; el target s'encarrega de a qui s'apliquen, així que el metadata.name del pedaç és irrellevant:

# irsa-patch.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: IGNORAT
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/RutasNortePro
---
# lb-patch.yaml
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "external"
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
spec:
  externalTrafficPolicy: Local
# k8s/entorns/pro/kustomization.yaml
components:
  - ../../components/politiques-xarxa
  - ../../components/alta-disponibilitat
  - ../../components/proveidor-aws      # <-- una línia

Migrar a Azure: crear k8s/components/proveidor-azure/ amb provisioner: disk.csi.azure.com, l'anotació azure.workload.identity/client-id (més l'etiqueta azure.workload.identity/use: "true" als pods) i les anotacions de balancejador d'Azure. Després, canviar aquella única línia. k8s/base/ no es toca en absolut.

Solució 3

Prioritat Mesura Diagnòstic Estalvi Risc
1 Ajustar requests a 150m/320Mi kubectl top pods, VPA en mode Off ~450 $ Baix, per fases
2 Consolidar 5 balancejadors en 1 + Ingress kubectl get svc -A --field-selector spec.type=LoadBalancer ~96 $ Mitjà (canvi de DNS)
3 Spot per a worker i informes Ja identificades com a tolerants ~80 $ Mitjà
4 Compromís d'ús sobre la base Mitjana de nodes de 90 dies ~130 $ Mitjà (1-3 anys)
5 Retenció de registres de 30 a 7 dies; LOG_LEVEL=warn Volum ingerit per servei ~50 $ Baix
6 Revisar PV orfes i snapshots vells kubectl get pv --field-selector status.phase=Released ~40 $ Baix
Total ~846 $ Factura de ~1.450 a ~604 $
# Ordres de diagnòstic
kubectl top pods -A --sort-by=cpu
kubectl get vpa -A -o custom-columns='NS:.metadata.namespace,NOM:.metadata.name,CPU:.status.recommendation.containerRecommendations[0].target.cpu,MEM:.status.recommendation.containerRecommendations[0].target.memory'
kubectl get svc -A --field-selector spec.type=LoadBalancer
kubectl get pv --field-selector status.phase=Released

Ordre i risc: es comença per l'ajust de requests perquè és el de més estalvi i menys risc, aplicant-lo primer a pre, verificant amb k6 (09-06) i després a pro component a component. El compromís d'ús va l'últim a propòsit: només té sentit comprometre's a una capacitat després d'haver-la reduït, o estaries pagant per endavant el sobredimensionament.

Conclusió

Es tanca aquí el mòdul 10. L'essencial d'aquesta lliçó:

  • El model de responsabilitat compartida et treu el pla de control sencer —etcd, certificats, quòrum, actualitzacions, alta disponibilitat—, és a dir, aproximadament la meitat de la feina de 10-02. Tota la resta, inclosos els nodes, el RBAC, les polítiques, els secrets, l'observabilitat, les còpies de les teves dades i el cost, continua sent teu.
  • EKS, AKS i GKE s'assemblen més que no pas es diferencien. Les diferències que de debò decideixen són el model de xarxa i l'assignació d'IP als pods, el cicle de versions, l'autoescalat (Karpenter destaca) i els modes automàtics (Autopilot destaca). I per damunt de tot: el núvol on ja és la teva infraestructura.
  • La federació d'identitat —IRSA, Workload Identity i el seu equivalent a Azure— elimina les credencials permanents dels Secrets. El manifest que va a Git porta una anotació, no un secret. Tanca el que es va prometre a 03-06.
  • Les instàncies interrompibles són estalvi real per a worker-notificacions i informes-ocupacio, si s'acompanyen de tolerations, afinitat preferent, distribució topològica, preStop i PDB.
  • El que ja sabies canvia de forma concreta: la StorageClass és del proveïdor i necessita WaitForFirstConsumer, el LoadBalancer per fi obté IP (i factura), el registre s'autentica amb la identitat del node, i les mètriques i registres es poden delegar.
  • L'actualització continua exigint planificació: APIs eliminades, matrius de compatibilitat dels complements, PDB que permetin drenar i sondes que aguantin el reinici.
  • El cost es domina amb sis palanques, i les dues primeres —ajustar requests amb el VPA i apagar el que no es fa servir— són les de millor relació esforç/benefici.
  • I la portabilitat depèn del que posis fora de Kubernetes, no de quin gestionat triïs. Aïllar l'específic del proveïdor en un component de Kustomize deixa la base 100 % portable.

Per a Rutas Norte S.L., la recomanació és EKS a eu-west-1, amb dos clústers, Karpenter, instàncies interrompibles per a les càrregues tolerants i les palanques d'estalvi aplicades des del primer dia. I gràcies a GitOps, aquesta decisió és reversible.

Amb això acaba el recorregut per l'ecosistema. Ja tens totes les peces: clústers locals i propis, empaquetat amb Helm, superposicions amb Kustomize, desplegament continu amb GitOps i una plataforma gestionada on recolzar-ho tot. Sumades als nou mòduls anteriors —pods, serveis, configuració, xarxa, emmagatzematge, patrons avançats, observabilitat, seguretat i escalat—, ja coneixes totes les peces per separat.

El que falta és veure-les funcionar juntes. Al Mòdul 11: Estudis de Cas i Aplicacions del Món Real recorrerem escenaris complets de principi a fi: el desplegament d'una aplicació web des de zero, l'execució d'aplicacions amb estat, una canalització de CI/CD completa, les estratègies de desplegament blue-green i canary, la gestió multi-clúster i, per tancar, l'operació en producció de debò: incidències, runbooks i control de costos. Aquí és on el coneixement es converteix en criteri.

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