La lliçó anterior va acabar amb una pregunta que algú de l'equip anava a fer aquesta mateixa setmana: tothom parla de Kubernetes. És l'estàndard de facto de l'orquestració de contenidors, té un ecosistema enorme, funciona igual a qualsevol núvol i hi ha molta gent que el sap fer servir. Si MercadoFresco acaba de moure tota la seva botiga a contenidors i s'ha quedat a ECS, s'està equivocant?

Aquesta lliçó respon amb dades. Veuràs què és Kubernetes de debò i què resol que ECS no resol, quina part gestiona Amazon EKS i què continua sent feina teva, com es desplega la botiga de MercadoFresco amb manifests complets, i quin és el cost que gairebé mai apareix a les comparatives: la feina recurrent de les actualitzacions de versió. Al final, la decisió raonada de MercadoFresco i els criteris objectius que la canviarien. I amb ella, el tancament del mòdul i el salt al mòdul 11.

Avís de cost. El pla de control d'EKS costa 0,10 USD per hora i clúster, uns 73 USD al mes, independentment de la mida. Amb suport estès —quan la versió de Kubernetes surt del suport estàndard— passa a 0,60 USD per hora, uns 438 USD al mes, i és un càrrec automàtic que sorprèn molta gent. A això s'hi sumen els nodes (EC2 o Fargate), el balancejador i els complements. Un clúster de proves oblidat és dels residus més cars del curs: segueix la neteja del final. Dades i identificadors ficticis.

Contingut

  1. Què és Kubernetes i què resol que ECS no
  2. Vocabulari mínim però real
  3. El model declaratiu i el bucle de reconciliació
  4. Què gestiona EKS i què continua sent teu
  5. Opcions de còmput: nodes, Fargate i Auto Mode
  6. Crear un clúster amb eksctl i amb CDK
  7. kubectl i l'accés al clúster
  8. Autenticació: entrades d'accés enfront d'aws-auth
  9. IRSA i EKS Pod Identity: permisos d'AWS per a un pod
  10. Els manifests de la botiga de MercadoFresco
  11. Ingress i l'AWS Load Balancer Controller
  12. Autoescalat: HPA, Cluster Autoscaler i Karpenter
  13. Complements: per què un EKS buit no serveix de res
  14. Actualitzacions de versió: el cost ocult
  15. Observabilitat: Container Insights, Prometheus i Grafana
  16. ECS enfront d'EKS: la comparativa honesta
  17. La decisió de MercadoFresco i què la canviaria
  18. Cost i neteja
  19. Errors habituals i consells
  20. Exercicis
  21. Tancament del mòdul i transició al mòdul 11

Què és Kubernetes i què resol que ECS no

Kubernetes és un orquestrador de contenidors de codi obert, nascut a Google i donat a la Cloud Native Computing Foundation. Fa el mateix que ECS —mantenir contenidors en execució, repartir-los entre màquines, reposar-los, exposar-los per xarxa— amb una diferència fonamental de disseny: Kubernetes és una plataforma extensible amb una API pròpia, no un servei d'un proveïdor.

Aquesta diferència produeix tres capacitats que ECS no té:

  • Portabilitat real. Un manifest de Kubernetes funciona a EKS, a Google Kubernetes Engine, a Azure Kubernetes Service, en un clúster propi i al portàtil del Luis amb kind. Una definició de tasca d'ECS només funciona a AWS. Per a MercadoFresco això no és urgent avui, però és l'argument amb més pes a favor d'EKS.
  • Un ecosistema que resol problemes que tu no vols resoldre. Existeixen milers de components llestos per instal·lar: gestió de certificats (cert-manager), desplegaments progressius (Argo Rollouts, Flagger), GitOps (Argo CD, Flux), malles de servei (Istio, Linkerd), operadors que gestionen bases de dades, cues o motors de cerca com si fossin recursos natius. A ECS, gairebé tot això o bé ho posa AWS o bé ho escrius tu.
  • Extensibilitat mitjançant l'API. Amb CRD (Custom Resource Definitions) i operadors es poden crear tipus de recurs propis. És el que permet escriure kubectl apply sobre un fitxer que declara «vull un clúster de Kafka amb tres rèpliques» i que un operador el construeixi i el mantingui. ECS no en té equivalent.

I produeix un cost que cal anomenar amb la mateixa claredat: Kubernetes és substancialment més complex. Hi ha més conceptes, més peces per instal·lar, més configuració que pot estar malament i més versions per actualitzar. La pregunta correcta no és «quin és millor?» sinó «el problema que tinc justifica aquesta complexitat?».

Vocabulari mínim però real

Aquests són els objectes que cal conèixer per llegir un manifest sense perdre's. La columna de la dreta ancora cadascun al que ja saps dels mòduls anteriors.

Objecte Què és Equivalent aproximat
Clúster El conjunt: pla de control més nodes Clúster d'ECS
Node (node) Una màquina que executa càrregues; sol ser una instància EC2 Instància del clúster d'ECS
Pod La unitat mínima: un o diversos contenidors que comparteixen xarxa i emmagatzematge Tasca d'ECS
ReplicaSet Garanteix que hi ha N pods iguals La part del servei d'ECS que reposa
Deployment Gestiona ReplicaSets i orquestra actualitzacions progressives Servei d'ECS
Service Nom i IP estables per a un conjunt de pods Cloud Map o ECS Service Connect
Ingress Encaminament HTTP extern cap a Services Regles d'un ALB (03-03)
Namespace Partició lògica dins del clúster Agrupació per projecte o entorn
ConfigMap Configuració no sensible com a parells clau-valor environment de la definició de tasca
Secret Configuració sensible (codificada en base64, no xifrada per omissió) secrets des de Secrets Manager
DaemonSet Un pod per node No existeix a Fargate; sí a ECS sobre EC2
Job / CronJob Tasca que acaba, puntual o programada run-task / EventBridge Scheduler

Dos advertiments sobre aquesta taula, perquè són font constant d'errors:

  • Un Secret de Kubernetes no està xifrat per omissió. Està codificat en base64, que no és xifratge: qualsevol amb permís de lectura sobre el namespace el veu en clar. Es resol amb xifratge a etcd mitjançant KMS i, encara millor, amb el controlador CSI de secrets que porta els valors des de Secrets Manager (més avall).
  • El Deployment no és el servei i el Service no és el Deployment. El Deployment gestiona els pods; el Service els dona un nom estable. A ECS, totes dues coses són dins de l'objecte «servei», i aquesta fusió és una de les raons per les quals ECS és més simple i menys flexible.

El model declaratiu i el bucle de reconciliació

Kubernetes no executa ordres: compara estats. Tu declares l'estat desitjat i un conjunt de controladors treballa contínuament perquè l'estat real hi coincideixi.

graph LR
  U["kubectl apply -f<br/>manifest.yaml"] --> API["Servidor d'API<br/>pla de control"]
  API --> ETCD[("etcd<br/>estat desitjat")]
  API --> CM["Controladors<br/>(Deployment, ReplicaSet...)"]
  CM -->|"compara desitjat<br/>amb real"| API
  CM --> SCH["Planificador<br/>tria node"]
  SCH --> KL["kubelet al node"]
  KL --> POD["Pods en execucio"]
  POD -->|"estat real"| API
  API -.->|"diferencia detectada"| CM

La conseqüència pràctica és que esborrar un pod a mà no serveix de res: el ReplicaSet detecta que falten rèpliques i en crea un altre en segons. És la mateixa lògica del desiredCount d'ECS, generalitzada a tots els objectes del sistema i exposada com una API que qualsevol pot estendre.

I la conseqüència cultural és més important: a Kubernetes el manifest és la infraestructura. Això encaixa de manera natural amb el que s'ha après al mòdul 9 i porta a GitOps: un repositori Git és la font de veritat i un agent al clúster (Argo CD o Flux) aplica el que hi hagi, revertint qualsevol canvi manual. És una manera de treballar més rigorosa que la del mòdul 8, i també una peça més per instal·lar, actualitzar i operar.

Què gestiona EKS i què continua sent teu

Amazon EKS és Kubernetes gestionat: AWS opera el pla de control i tu operes tota la resta. La llista concreta importa perquè gairebé totes les decepcions amb EKS vénen d'esperar més del que ofereix.

AWS gestiona Detall
El pla de control Servidor d'API, planificador, controladors i etcd, replicats en tres AZ
La seva disponibilitat i escalat Es dimensiona sol; no hi ha mestres per administrar
Les còpies d'etcd Automàtiques; ni les veus ni les gestiones
El procés d'actualització de versió Un botó o una comanda, amb validacions prèvies
La integració amb IAM Autenticació d'usuaris i rols d'AWS contra el clúster
Els complements gestionats VPC CNI, CoreDNS, kube-proxy, EBS CSI i altres, amb versions compatibles
Continua sent teu Detall
Els nodes El seu tipus, la seva AMI, el seu apedaçament i el seu reemplaçament (excepte Fargate o Auto Mode)
Decidir quan actualitzar I provar-ho, i executar-ho, tres o quatre vegades l'any
La compatibilitat dels teus manifests Les API de Kubernetes es retiren entre versions i trenquen coses
Tot el programari del clúster Controlador de balancejadors, autoescalador, controlador de secrets, mètriques
La xarxa VPC, subxarxes, grups de seguretat, assignació d'IP a pods
La seguretat dins del clúster RBAC, polítiques de xarxa, polítiques de seguretat de pods
L'observabilitat Mètriques, registres i traces, igual que a ECS

La frase que cal retenir: EKS gestiona el pla de control, no el clúster. Un clúster d'EKS acabat de crear no pot desplegar res útil: li falta el controlador de balancejadors, l'autoescalador, el controlador de secrets i les mètriques. Aquesta distància entre «clúster creat» i «clúster operatiu» és on viu la major part de la feina.

Opcions de còmput: nodes, Fargate i Auto Mode

Els pods s'han d'executar en algun lloc, i EKS ofereix quatre models.

Opció Qui gestiona els nodes Apedaçament Escalat Quan té sentit
Grups de nodes gestionats AWS crea i substitueix; tu llances l'actualització Guiat per AWS, sense tall si el configures Cluster Autoscaler o Karpenter El cas general
Nodes autogestionats Tu, amb un ASG propi Tot teu Teu AMI personalitzada, requisits molt concrets
EKS sobre Fargate Ningú: no hi ha nodes Cap Per pod Càrregues aïllades, sense DaemonSet
EKS Auto Mode AWS gestiona nodes, escalat i pedaços AWS, amb reciclatge automàtic Integrat, basat en Karpenter Qui vol EKS amb l'operació mínima

EKS sobre Fargate funciona amb perfils de Fargate: es declara quins namespaces o etiquetes fan que un pod s'executi sense node. Té limitacions importants que cal conèixer abans de comptar-hi: no admet DaemonSet, ni emmagatzematge persistent diferent d'EFS, ni hostPort, ni pods privilegiats, i un pod per tasca de Fargate amb l'arrodoniment de recursos que això implica. La conseqüència pràctica és que molts complements —que s'instal·len com a DaemonSet— no poden executar-se sobre Fargate, així que gairebé sempre cal almenys un grup de nodes petit per a ells.

EKS Auto Mode és la resposta d'AWS a l'argument d'aquesta lliçó: gestiona els nodes, l'escalat, l'apedaçament i diversos complements essencials, reciclant els nodes automàticament cada cert temps. Redueix molt la feina operativa a canvi d'un recàrrec sobre el preu del còmput i de menys control. El que no elimina és la part que més pesa: continues decidint i executant les actualitzacions de versió de Kubernetes i continues sent responsable que els teus manifests siguin compatibles.

Crear un clúster amb eksctl i amb CDK

eksctl és l'eina oficial de la comunitat i la via més ràpida. És declarativa: se li dona un fitxer i construeix la VPC —o fa servir la teva—, el pla de control, els grups de nodes i els complements.

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: eks-mercadofresco
  region: eu-west-1
  version: "1.31"
  tags:
    Proyecto: mercadofresco
    Entorno: desarrollo
    Componente: orquestacion
    Propietario: plataforma
    CentroCoste: tecnologia

vpc:
  id: vpc-mercadofresco                     # es reutilitza la VPC del modul 3
  subnets:
    private:
      eu-west-1a: { id: snet-mercadofresco-app-a }
      eu-west-1b: { id: snet-mercadofresco-app-b }
    public:
      eu-west-1a: { id: snet-mercadofresco-publica-a }
      eu-west-1b: { id: snet-mercadofresco-publica-b }
  clusterEndpoints:
    publicAccess: true                      # restringit per CIDR mes avall
    privateAccess: true
  publicAccessCIDRs: ["203.0.113.0/24"]     # nomes la xarxa corporativa

managedNodeGroups:
  - name: ng-mercadofresco-general
    instanceTypes: ["m6g.large"]            # Graviton, com a 10-02
    amiFamily: AmazonLinux2023
    minSize: 2
    maxSize: 8
    desiredCapacity: 3
    privateNetworking: true                 # nodes nomes en subxarxes privades
    volumeSize: 50
    volumeType: gp3
    labels: { rol: general }
    updateConfig: { maxUnavailablePercentage: 25 }

addons:
  - name: vpc-cni
    version: latest
    configurationValues: '{"env":{"ENABLE_PREFIX_DELEGATION":"true"}}'
  - name: coredns
  - name: kube-proxy
  - name: aws-ebs-csi-driver
  - name: eks-pod-identity-agent

iam:
  withOIDC: true                            # imprescindible per a IRSA
  serviceAccounts:
    - metadata: { name: aws-load-balancer-controller, namespace: kube-system }
      wellKnownPolicies: { awsLoadBalancerController: true }

cloudWatch:
  clusterLogging:
    enableTypes: ["api", "audit", "authenticator"]
eksctl create cluster -f eks-mercadofresco.yaml     # de 15 a 20 minuts

Quatre decisions d'aquest fitxer que mereixen atenció:

  • publicAccessCIDRs restringit. Per omissió el punt d'enllaç del servidor d'API és accessible des de qualsevol IP d'internet —protegit per autenticació, però exposat—. Restringir-lo a la xarxa corporativa o passar a només accés privat és una de les primeres coses que revisa una auditoria.
  • privateNetworking: true. Els nodes en subxarxes privades, coherent amb tot el que va fer MercadoFresco al mòdul 3 i amb el que s'ha après a 10-02 sobre punts d'enllaç de VPC. Els mateixos punts d'enllaç de la lliçó anterior són necessaris aquí.
  • withOIDC: true. Sense el proveïdor OIDC no hi ha IRSA, i sense IRSA l'única manera de donar permisos d'AWS a un pod és el rol del node, que és un mal greu: tots els pods d'aquest node heretarien aquests permisos.
  • ENABLE_PREFIX_DELEGATION. Sense això, cada pod consumeix una IP secundària de l'ENI del node i el límit de pods per node s'assoleix molt abans d'esgotar CPU o memòria. És l'equivalent a EKS de l'awsvpcTrunking de 10-01, i sorprèn igual.

En CDK (09-02), l'equivalent és més curt però exigeix entendre el mateix:

const cluster = new eks.Cluster(this, 'MercadoFrescoEks', {
  clusterName: 'eks-mercadofresco',
  version: eks.KubernetesVersion.V1_31,
  kubectlLayer: new KubectlV31Layer(this, 'CapaKubectl'),
  vpc,
  vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }],
  defaultCapacity: 0,                                   // els nodes es declaren a part
  authenticationMode: eks.AuthenticationMode.API,       // entrades d'acces, sense aws-auth
  clusterLogging: [eks.ClusterLoggingTypes.API, eks.ClusterLoggingTypes.AUDIT],
});

cluster.addNodegroupCapacity('General', {
  instanceTypes: [new ec2.InstanceType('m6g.large')],
  amiType: eks.NodegroupAmiType.AL2023_ARM_64_STANDARD,
  minSize: 2, maxSize: 8, desiredSize: 3,
  subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
});

// Els manifests tambe son infraestructura: es despleguen amb la pila.
cluster.addHelmChart('LoadBalancerController', {
  chart: 'aws-load-balancer-controller',
  repository: 'https://aws.github.io/eks-charts',
  namespace: 'kube-system',
  values: { clusterName: cluster.clusterName, serviceAccount: { create: false, name: 'aws-load-balancer-controller' } },
});

kubectl i l'accés al clúster

kubectl és el client. Es configura amb una comanda que escriu el context a ~/.kube/config fent servir les credencials d'AWS actuals:

aws eks update-kubeconfig --name eks-mercadofresco --region eu-west-1
kubectl get nodes                 # els 3 nodes a Ready
kubectl get pods -A               # els pods del sistema: coredns, aws-node, kube-proxy
kubectl config current-context    # verificar SEMPRE contra quin cluster apuntes

L'última comanda sembla trivial i no ho és: kubectl apunta a l'últim clúster configurat, i kubectl delete deployment tienda no pregunta en quin entorn ets. És el cdk destroy --all de 09-02 amb una altra forma. La disciplina que ho evita és doble: comptes separats per entorn (09-04), de manera que les credencials de desenvolupament no puguin tocar el clúster de producció, i un indicador del context actiu al prompt del terminal.

Autenticació: entrades d'accés enfront d'aws-auth

Kubernetes té el seu propi sistema de permisos, RBAC, amb Role, ClusterRole, RoleBinding i ClusterRoleBinding. EKS connecta aquest sistema amb IAM: IAM diu qui ets, RBAC diu què pots fer dins del clúster.

Històricament aquesta connexió es feia amb un ConfigMap anomenat aws-auth, i era una font notòria d'incidents: un error de sintaxi en editar-lo podia deixar tothom fora del clúster de manera irreversible, sense més sortida que recrear-lo. Avui la forma correcta són les entrades d'accés (access entries), que són API d'AWS: es creen amb la CLI o amb CDK, s'auditen a CloudTrail i no depenen d'editar YAML a mà.

# L'equip de plataforma, administrador del cluster
aws eks create-access-entry --cluster-name eks-mercadofresco \
  --principal-arn arn:aws:iam::333344445555:role/AWSReservedSSO_AdministracionPlataforma_abc \
  --type STANDARD
aws eks associate-access-policy --cluster-name eks-mercadofresco \
  --principal-arn arn:aws:iam::333344445555:role/AWSReservedSSO_AdministracionPlataforma_abc \
  --access-scope type=cluster \
  --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy

# Els desenvolupadors, nomes al seu namespace i sense poder esborrar el cluster
aws eks associate-access-policy --cluster-name eks-mercadofresco \
  --principal-arn arn:aws:iam::333344445555:role/AWSReservedSSO_DesarrolloCompleto_def \
  --access-scope type=namespace,namespaces=mercadofresco \
  --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy

Els conjunts de permisos són els de 09-04: AdministracionPlataforma, DesarrolloCompleto i AnalisisDatos. La correspondència queda neta: el mateix mecanisme d'identitat que governa els cinc comptes governa també el clúster, sense usuaris de Kubernetes paral·lels ni credencials a part. És un dels avantatges reals d'EKS enfront d'un Kubernetes autogestionat.

IRSA i EKS Pod Identity: permisos d'AWS per a un pod

L'equivalent exacte del rol de tasca d'ECS (10-01). Un pod de la botiga necessita enviar a cola-mercadofresco-pedidos i llegir mercadofresco-carritos, i necessita fer-ho amb permisos propis, no amb els del node.

Mecanisme Com funciona Avantatges Inconvenients
Rol del node Tots els pods hereten el rol de la instància Zero configuració Inacceptable: qualsevol pod fa tot el que fa qualsevol altre
IRSA (rols per a comptes de servei) Proveïdor OIDC del clúster + política de confiança que el referencia Madur, funciona a tot arreu Configuració per rol una mica laboriosa, lligada a un clúster
EKS Pod Identity Un agent al node i una associació entre compte de servei i rol Més simple, reutilitzable entre clústers, sense OIDC Requereix el complement; no aplica a EKS sobre Fargate

Amb Pod Identity, que és l'opció recomanada avui:

aws eks create-pod-identity-association --cluster-name eks-mercadofresco \
  --namespace mercadofresco --service-account sa-tienda \
  --role-arn arn:aws:iam::333344445555:role/rol-pod-mercadofresco-tienda

I la política de confiança del rol, que és el que canvia respecte a un rol normal:

{"Version": "2012-10-17", "Statement": [{
  "Effect": "Allow",
  "Principal": { "Service": "pods.eks.amazonaws.com" },
  "Action": ["sts:AssumeRole", "sts:TagSession"]
}]}

La política de permisos és literalment la mateixa que la del rol de tasca d'ECS a 10-01: sqs:SendMessage sobre la cua, dynamodb:*Item sobre la taula de carros i s3:GetObject sobre les fotos del catàleg. El mateix principi de mínim privilegi, diferent manera d'ancorar-lo a la càrrega.

Els manifests de la botiga de MercadoFresco

Aquí hi ha el que a ECS era una definició de tasca més un servei. Comencem pel Namespace, el ServiceAccount i el ConfigMap:

apiVersion: v1
kind: Namespace
metadata:
  name: mercadofresco
  labels: { proyecto: mercadofresco, entorno: desarrollo }
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sa-tienda
  namespace: mercadofresco
# Sense anotacions: amb Pod Identity l'associacio es fa des de l'API d'AWS.
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: cfg-tienda
  namespace: mercadofresco
data:
  ENTORNO: "desarrollo"
  COLA_PEDIDOS: "cola-mercadofresco-pedidos"
  TABLA_CARRITOS: "mercadofresco-carritos"

El Deployment, que és el cor del desplegament:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tienda
  namespace: mercadofresco
  labels: { app: tienda, proyecto: mercadofresco }
spec:
  replicas: 3
  revisionHistoryLimit: 5                      # quantes revisions es poden revertir
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0                        # equival a minimumHealthyPercent 100 d'ECS
      maxSurge: 2                              # equival a maximumPercent 200
  selector:
    matchLabels: { app: tienda }
  template:
    metadata:
      labels: { app: tienda, proyecto: mercadofresco }
    spec:
      serviceAccountName: sa-tienda
      # Reparteix els pods entre AZ: l'equivalent de spread per AZ a ECS.
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels: { app: tienda }
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        fsGroup: 10001
      terminationGracePeriodSeconds: 30        # equival a stopTimeout d'ECS
      containers:
        - name: tienda
          image: 555566667777.dkr.ecr.eu-west-1.amazonaws.com/mercadofresco/tienda@sha256:9c1e...f4a2
          ports:
            - containerPort: 8080
              name: http
          envFrom:
            - configMapRef: { name: cfg-tienda }
          env:
            - name: BD_CONTRASENA
              valueFrom:
                secretKeyRef: { name: sec-tienda-rds, key: password }
          resources:
            requests: { cpu: "500m", memory: "1Gi" }   # el que el planificador reserva
            limits:   { cpu: "1000m", memory: "2Gi" }  # el sostre dur
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities: { drop: ["ALL"] }
          volumeMounts:
            - { name: temporal, mountPath: /tmp }
          startupProbe:                        # dona temps a arrencar sense que liveness mati
            httpGet: { path: /salud, port: 8080 }
            periodSeconds: 5
            failureThreshold: 12               # fins a 60 s per a la primera arrencada
          readinessProbe:                      # decideix si rep transit
            httpGet: { path: /salud, port: 8080 }
            periodSeconds: 10
            timeoutSeconds: 3
            failureThreshold: 3
          livenessProbe:                       # decideix si es reinicia el contenidor
            httpGet: { path: /salud, port: 8080 }
            periodSeconds: 20
            timeoutSeconds: 3
            failureThreshold: 3
          lifecycle:
            preStop:
              exec: { command: ["sleep", "5"] } # marge perque l'ALB desregistri
      volumes:
        - name: temporal
          emptyDir: { sizeLimit: 1Gi }

Els punts que cal entendre d'aquest manifest:

  • Les tres sondes són tres preguntes diferents i confondre-les causa incidents. startupProbe respon «ha acabat d'arrencar?» i suspèn les altres dues fins que encerta. readinessProbe respon «pot rebre trànsit ara?» i la seva fallada treu el pod del balanceig sense matar-lo. livenessProbe respon «està penjat?» i la seva fallada reinicia el contenidor. L'error clàssic és apuntar livenessProbe a un /salud que consulta Aurora: quan la base de dades va lenta, Kubernetes reinicia tots els pods alhora i converteix un problema de latència en una caiguda total.
  • requests i limits no són el mateix. requests és el que el planificador reserva per decidir en quin node cap el pod; limits és el sostre. Si la CPU supera el límit, el pod s'estrangula (s'alenteix); si la memòria el supera, el kernel el mata amb OOM i veuràs OOMKilled als esdeveniments. Posar requests molt per sota de l'ús real produeix nodes sobrecompromesos que cauen alhora.
  • maxUnavailable: 0 amb maxSurge: 2 és exactament el 100/200 d'ECS: mai no hi ha menys capacitat sana que la desitjada, a costa de pagar uns minuts de capacitat doble durant el desplegament.
  • preStop amb una espera curta resol el mateix problema que el drenatge del grup de destinació: quan Kubernetes decideix acabar un pod, l'ALB pot trigar uns segons a deixar d'enviar-li trànsit. Sense aquesta espera, es perden peticions a cada desplegament.
  • El Secret referenciat no s'escriu a mà. MercadoFresco fa servir el controlador CSI de secrets amb el proveïdor d'AWS, que munta mercadofresco/produccion/rds/mfadmin des de Secrets Manager i opcionalment el sincronitza com a Secret de Kubernetes. Així el secret no viu a Git ni a etcd en base64.

El Service, que dona als pods un nom estable:

apiVersion: v1
kind: Service
metadata:
  name: svc-tienda
  namespace: mercadofresco
spec:
  type: ClusterIP                # nomes intern: qui exposa a l'exterior es l'Ingress
  selector: { app: tienda }
  ports:
    - port: 80
      targetPort: 8080
      name: http

Ingress i l'AWS Load Balancer Controller

Un Ingress per si sol no fa res: és una declaració que necessita un controlador que la tradueixi a un balancejador real. A EKS, aquest controlador és l'AWS Load Balancer Controller, que crea i configura un ALB de debò (03-03) a partir de les anotacions.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ing-tienda
  namespace: mercadofresco
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip          # directe al pod, sense salt pel node
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
    alb.ingress.kubernetes.io/ssl-redirect: '443'
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:eu-west-1:333344445555:certificate/abc-123
    alb.ingress.kubernetes.io/healthcheck-path: /salud
    alb.ingress.kubernetes.io/healthcheck-interval-seconds: '15'
    alb.ingress.kubernetes.io/wafv2-acl-arn: arn:aws:wafv2:eu-west-1:333344445555:regional/webacl/waf-mercadofresco-cdn/abc
    alb.ingress.kubernetes.io/tags: Proyecto=mercadofresco,Entorno=desarrollo,Componente=tienda,Propietario=plataforma,CentroCoste=tecnologia
    alb.ingress.kubernetes.io/group.name: mercadofresco   # diversos Ingress comparteixen un ALB
spec:
  ingressClassName: alb
  rules:
    - host: tienda.mercadofresco.example
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: svc-tienda
                port: { number: 80 }

Tres observacions que connecten amb mòduls anteriors. target-type: ip fa que l'ALB enviï trànsit directament a la IP del pod, sense passar pel NodePort del node: és l'equivalent exacte del grup de destinació de tipus ip de 10-01, i estalvia un salt de xarxa. group.name permet que diversos Ingress comparteixin un únic ALB, cosa que importa perquè cada ALB costa uns 17 USD al mes de cost fix i crear-ne un per servei es paga ràpid. I les anotacions de WAF i certificat demostren que, per sota, continua sent el mateix ALB del mòdul 3 amb les mateixes proteccions del mòdul 4: EKS canvia com es declara, no què s'obté.

Autoescalat: HPA, Cluster Autoscaler i Karpenter

A Kubernetes hi ha dos escalats i són independents: el de pods i el de nodes. Confondre'ls és la causa de la queixa «he configurat l'autoescalat i no escala».

L'HorizontalPodAutoscaler és l'equivalent de l'autoescalat del servei d'ECS:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-tienda
  namespace: mercadofresco
spec:
  scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: tienda }
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource: { name: cpu, target: { type: Utilization, averageUtilization: 65 } }
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30           # creixer rapid, com a 10-01
      policies: [{ type: Percent, value: 100, periodSeconds: 30 }]
    scaleDown:
      stabilizationWindowSeconds: 300          # decreixer a poc a poc
      policies: [{ type: Percent, value: 25, periodSeconds: 60 }]

Escalar per peticions per destinació —que a 10-01 es va demostrar millor que la CPU— exigeix aquí instal·lar a més KEDA o l'adaptador de mètriques de CloudWatch, perquè l'HPA natiu només entén CPU i memòria. És un exemple petit i representatiu del patró general: el que a ECS és un paràmetre, a Kubernetes sol ser un component més per instal·lar i mantenir.

Per als nodes hi ha dues opcions:

Cluster Autoscaler Karpenter
Com decideix Ajusta la mida de grups de nodes predefinits Tria el tipus d'instància adequat per als pods pendents
Velocitat Minuts Segons fins a llançar; l'arrencada continua sent d'EC2
Eficiència Limitada pels tipus del grup Alta: consolida i tria la mida òptima
Spot Possible, amb grups a part Natiu, amb substitució en rebre l'avís
Complexitat Baixa, és el tradicional Mitjana: un altre controlador per instal·lar i actualitzar

Karpenter és avui l'opció recomanada i és genuïnament bona: observa els pods que no caben, decideix quina instància els allotjaria millor, la llança i, quan en sobra, consolida les càrregues i apaga nodes. Però convé veure el conjunt amb perspectiva: per aconseguir a EKS el que a Fargate ve posat —que la capacitat aparegui quan cal i desaparegui quan no—, cal instal·lar, configurar, actualitzar i vigilar un controlador més. I fins i tot amb Karpenter, el pod nou espera que arrenqui una instància EC2: els dos minuts que MercadoFresco volia eliminar reapareixen quan no hi ha lloc.

Complements: per què un EKS buit no serveix de res

Un clúster acabat de crear té tres coses i li'n falten moltes. Aquesta és la llista mínima real per poder executar la botiga de MercadoFresco:

Complement Per a què Gestionat per AWS?
VPC CNI (aws-node) Dona a cada pod una IP de la VPC
CoreDNS Resolució DNS dins del clúster
kube-proxy Regles de xarxa per als Services
EKS Pod Identity Agent Permisos d'AWS per pod
EBS CSI / EFS CSI Volums persistents
AWS Load Balancer Controller Converteix Ingress en ALB No: l'instal·les i l'actualitzes tu
Secrets Store CSI + proveïdor d'AWS Porta secrets des de Secrets Manager No
Metrics Server Mètriques que necessita l'HPA No
Karpenter o Cluster Autoscaler Escalat de nodes No
CloudWatch Observability Container Insights i registres Sí (complement)
KEDA (opcional) Escalat per mètriques externes No
cert-manager (opcional) Certificats interns No

Els cinc marcats com a «No» són programari que MercadoFresco hauria d'instal·lar amb Helm, versionar, provar i actualitzar, cadascun amb el seu propi cicle de vida i la seva pròpia matriu de compatibilitat amb les versions de Kubernetes. Cap no és difícil per separat. La suma sí que ho és, i és la part d'EKS que no apareix a les comparatives d'una tarda.

A ECS, l'equivalent d'aquesta taula sencera és: res. El balancejador és un paràmetre del servei, els secrets són un camp de la definició de tasca, l'escalat és Application Auto Scaling i les mètriques vénen amb Container Insights.

Actualitzacions de versió: el cost ocult

Aquest és l'argument central de la lliçó i el que més pes té a la decisió final.

Kubernetes publica unes tres versions menors l'any. EKS dona suport estàndard durant uns 14 mesos per versió; després entra automàticament en suport estès, que dura uns 12 mesos més i costa 0,60 USD per hora en lloc de 0,10, és a dir, uns 438 USD al mes per clúster en lloc de 73. En acabar l'estès, AWS actualitza el clúster pel seu compte.

El que implica en feina real, tres o quatre vegades l'any i per clúster:

  1. Llegir les notes de la versió i detectar les API retirades. Kubernetes retira versions d'API entre versions menors, i un manifest que funcionava deixa d'aplicar-se. Eines com kubent ajuden a trobar-los, però la revisió és teva.
  2. Comprovar la compatibilitat de cada complement: el controlador de balancejadors, Karpenter, el CSI de secrets, KEDA, el CNI. Cadascun té la seva matriu, i de vegades cal actualitzar el complement abans que el clúster.
  3. Actualitzar el pla de control, que triga uns 25-40 minuts i no es pot revertir.
  4. Actualitzar els nodes, substituint instàncies en tandes i drenant pods, amb el risc habitual que un PodDisruptionBudget mal posat bloquegi el drenatge.
  5. Provar-ho tot en un clúster de preproducció abans, cosa que implica mantenir aquest clúster, amb el seu pla de control de 73 USD al mes i els seus mateixos complements.
Activitat Freqüència Esforç estimat
Actualització de versió menor (preproducció i producció) 3 l'any 2-4 dies d'una persona cada vegada
Actualització de complements fora de cicle 6-10 l'any 2-4 hores cada vegada
Seguiment de CVE dels components del clúster Continu 2-4 hores al mes
Total anual aproximat 20-30 dies de feina de plataforma

Vint o trenta dies l'any són més d'un mes d'una persona. Aquest és el cost ocult d'EKS, i no l'elimina Auto Mode: Auto Mode s'encarrega dels nodes, però les versions de Kubernetes, la compatibilitat dels teus manifests i els complements que instal·lis continuen sent teus.

A ECS aquesta feina simplement no existeix. No hi ha versió d'ECS per actualitzar; AWS fa evolucionar el servei sense que ningú planifiqui res. És la comparació més honesta que es pot fer entre tots dos, i per a un equip de plataforma petit és decisiva.

Observabilitat: Container Insights, Prometheus i Grafana

Tot el del mòdul 5 continua aplicant-se; canvien les eines natives de l'ecosistema.

  • Container Insights s'activa amb el complement d'observabilitat de CloudWatch i dona mètriques de clúster, node, pod i contenidor, més els registres dels contenidors a /aws/containerinsights/eks-mercadofresco/application. És la via més directa i la que menys peces afegeix.
  • Amazon Managed Service for Prometheus és un Prometheus gestionat i compatible amb PromQL. Encaixa bé perquè la majoria dels components de l'ecosistema —el controlador de balancejadors, Karpenter, KEDA— ja exposen mètriques en format Prometheus. Es recull amb l'agent de CloudWatch o amb el recol·lector d'OpenTelemetry, i es paga per mostres ingerides i emmagatzemades.
  • Amazon Managed Grafana és el Grafana gestionat, amb accés mitjançant IAM Identity Center (09-04) i fonts de dades cap a Prometheus, CloudWatch i X-Ray. Es paga per usuari actiu al mes.
  • Els registres del pla de controlapi, audit, authenticator— s'activen al clúster i van a CloudWatch Logs. El d'auditoria és el que respon «qui va esborrar aquest Deployment», i convé activar-lo des del primer dia perquè no és retroactiu.

L'observació honesta: la combinació Prometheus més Grafana és més potent que CloudWatch per a mètriques de Kubernetes, amb millor consulta i un catàleg de taulers llestos. I també és més peces: dos serveis més per configurar, dues factures més i dos llocs on mirar durant un incident. Per a MercadoFresco, amb els taulers mercadofresco-produccion i -negocio ja construïts a 05-01, el guany no compensa la fragmentació.

ECS enfront d'EKS: la comparativa honesta

Criteri Amazon ECS Amazon EKS
Corba d'aprenentatge Dies: cinc conceptes Setmanes o mesos: desenes d'objectes i patrons
Cost del pla de control 0 USD 73 USD/mes, o 438 amb suport estès
Portabilitat Cap: només AWS Total: qualsevol núvol o servidor propi
Ecosistema El que ofereix AWS Enorme: operadors, GitOps, malles, milers de gràfics de Helm
Extensibilitat Molt limitada CRD i operadors: s'estén l'API
Integració amb AWS Nativa i sense peces Bona, però mitjançant controladors que instal·les tu
Actualitzacions Cap: AWS ho fa 3 l'any, 20-30 dies de feina anuals
Complements per mantenir Cap 5-8 components amb el seu propi cicle
Personal necessari Qualsevol equip amb AWS Algú que sàpiga Kubernetes, no que l'estigui aprenent
Mercat laboral Menor Ampli: és una habilitat transferible
Fargate Sí, plenament integrat Sí, amb limitacions (sense DaemonSet, sense EBS)
Multinúvol i híbrid No , incloses instal·lacions pròpies
Velocitat per arribar a producció Dies Setmanes
Sostre de complexitat que suporta Mitjà Molt alt

Cap de les dues columnes és «la bona». La lectura correcta és: EKS compra flexibilitat, portabilitat i ecosistema, i ho paga amb complexitat, personal i feina recurrent. Si necessites el que compra, el preu és raonable. Si no ho necessites, has comprat feina.

La decisió de MercadoFresco i què la canviaria

MercadoFresco es queda a ECS amb Fargate. I convé que la raó estigui escrita, perquè una decisió sense arguments escrits es revisa cada sis mesos en funció de l'última conferència a què va anar algú.

Els arguments, en ordre de pes:

  1. L'equip de plataforma és la Marta i mig Luis. Vint o trenta dies l'any d'actualitzacions sobre dues persones que a més porten la xarxa, la seguretat, el pipeline i les guàrdies no és un cost assumible: seria el 10-15 % de la seva capacitat total dedicat a mantenir l'orquestrador, no la botiga.
  2. No hi ha cap requisit que ECS no cobreixi. La botiga és un servei HTTP amb estat extern, uns treballadors que consumeixen cues i unes funcions per esdeveniments. Res d'això no necessita CRD, operadors, malles de servei ni planificació avançada de pods.
  3. No hi ha requisit multinúvol. MercadoFresco és a AWS i no hi ha pla ni pressió comercial per canviar-ho. La portabilitat de Kubernetes és una assegurança, i una assegurança que no es farà servir és només una prima.
  4. La velocitat importa. El mòdul 10 ha portat la botiga d'EC2 a contenidors en setmanes. Amb EKS, aquestes mateixes setmanes haurien estat per aixecar el clúster, els complements i l'aprenentatge de l'equip, sense haver mogut encara la botiga.
  5. El cost directe també compta, encara que sigui l'argument menor: 73 USD al mes per clúster i per entorn són uns 220 USD mensuals per a desenvolupament, preproducció i producció, enfront de 0 USD d'ECS. Són pocs diners comparats amb el cost de personal, però no són zero.

I els criteris objectius que canviarien la decisió, escrits per endavant perquè la revisió futura sigui una comprovació i no un debat:

Criteri Llindar concret Per què inclinaria la balança
Experiència prèvia de l'equip Dues o més persones amb Kubernetes en producció La corba deixa de ser un cost i passa a ser un actiu
Requisit multinúvol o híbrid Un contracte, una normativa o un magatzem amb còmput local És l'única cosa que ECS no pot donar de cap manera
Necessitat d'operadors de l'ecosistema Un component que només existeix com a operador de Kubernetes Reimplementar-lo costa més que adoptar EKS
Mida de l'equip de plataforma Quatre o més persones dedicades Hi ha capacitat per absorbir la feina recurrent
Nombre de serveis Més de 30-40 serveis amb equips diferents Els namespaces, RBAC i GitOps comencen a guanyar de debò
Requisits de planificació avançats Afinitats complexes, GPU compartides, prioritat entre càrregues El planificador de Kubernetes és molt superior

Mentre cap d'aquests llindars no es compleixi, la resposta a «ens hauríem de passar a Kubernetes?» és no, i aquí està el perquè. El dia que se'n compleixin dos, la resposta canvia, i aquesta taula diu exactament quan.

Amb un matís que evita l'error contrari: el Luis hauria d'aprendre Kubernetes igualment. És una habilitat de mercat i dona criteri per jutjar arquitectures alienes. Aprendre'l no obliga a adoptar-lo, i adoptar-lo sense saber-lo és la pitjor combinació possible.

Cost i neteja

Concepte Cost aproximat mensual
Pla de control, suport estàndard 73 USD per clúster
Pla de control, suport estès 438 USD per clúster
3 nodes m6g.large sota demanda ≈ 165 USD
Volums EBS dels nodes (3 × 50 GB gp3) ≈ 12 USD
ALB creat per l'Ingress ≈ 17 USD més consum
Managed Prometheus i Grafana Des de ~20 USD segons ús i usuaris
Clúster de desenvolupament mínim i realista ≈ 270-290 USD/mes

Multiplicat per tres entorns, un EKS equivalent al muntatge actual de MercadoFresco costaria de l'ordre de 800 USD al mes enfront dels aproximadament 250 USD d'ECS amb Fargate i arm64 —i aquesta comparació no inclou els 20-30 dies anuals de feina de plataforma, que és el cost més gran.

Neteja, i aquí l'ordre importa més que en cap altra lliçó: els recursos d'AWS creats des de dins del clúster —l'ALB de l'Ingress, els volums EBS dels PersistentVolumeClaim— no s'esborren en esborrar el clúster i queden orfes facturant.

# 1. PRIMER els objectes de Kubernetes que creen recursos d'AWS
kubectl delete ingress --all -n mercadofresco     # esborra l'ALB
kubectl delete pvc --all -n mercadofresco         # esborra els volums EBS
kubectl delete svc --all -n mercadofresco         # esborra qualsevol NLB de type LoadBalancer
# 2. Comprovar que de debo han desaparegut abans de seguir
aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName,`k8s`)].LoadBalancerArn'
aws ec2 describe-volumes --filters Name=status,Values=available --query 'Volumes[].VolumeId'
# 3. Ara si, el cluster complet (eksctl esborra nodes, rols i la pila)
eksctl delete cluster --name eks-mercadofresco --region eu-west-1
# 4. Residus habituals: grups de registres i el proveidor OIDC
aws logs delete-log-group --log-group-name /aws/eks/eks-mercadofresco/cluster
aws iam list-open-id-connect-providers

Si s'esborra el clúster abans que els Ingress, el controlador que sabia esborrar l'ALB desapareix amb ell i el balancejador es queda facturant indefinidament. És el residu més car i més freqüent d'EKS.

Errors Habituals i Consells

Error: livenessProbe que consulta la base de dades. Quan Aurora va lenta, Kubernetes reinicia tots els pods alhora i una degradació es converteix en caiguda. Consell: liveness comprova només que el procés respon; les dependències van a readiness.

Error: no posar startupProbe. Una aplicació que triga 40 segons a arrencar és reiniciada per liveness abans d'acabar, en bucle. Consell: startupProbe amb failureThreshold generós; suspèn les altres dues fins que encerta.

Error: requests molt per sota de l'ús real. Els nodes queden sobrecompromesos i cauen diversos pods alhora. Consell: requests proper al percentil 95 real i limits amb marge, mesurat amb Container Insights.

Error: donar permisos d'AWS mitjançant el rol del node. Tots els pods del node ho hereten tot. Consell: Pod Identity o IRSA sempre, fins i tot a desenvolupament, perquè el que es fa a desenvolupament es copia a producció.

Error: editar aws-auth a mà. Un error de sintaxi deixa tothom fora del clúster sense marxa enrere. Consell: fes servir entrades d'accés (AuthenticationMode: API) i no toquis aquest ConfigMap.

Error: guardar Secret de Kubernetes a Git. Base64 no és xifratge. Consell: controlador CSI de secrets amb Secrets Manager, i xifratge d'etcd amb KMS activat en la creació del clúster.

Error: esborrar el clúster abans que els Ingress. L'ALB queda orfe facturant. Consell: l'ordre de neteja de dalt, amb verificació entre passos.

Error: deixar que el clúster entri en suport estès sense adonar-se'n. La factura es multiplica per sis en silenci. Consell: una alarma al calendari als 12 mesos de cada versió i un pressupost per compte (11-04) que detecti el salt.

Error: un ALB per cada Ingress. Deu serveis, deu balancejadors, 170 USD al mes. Consell: alb.ingress.kubernetes.io/group.name per compartir un ALB entre Ingress.

Error: triar EKS per currículum. És una raó real i sincera, i una mala raó tècnica. Consell: si l'equip vol aprendre Kubernetes, que munti un clúster de laboratori amb pressupost propi; la producció es decideix amb els criteris de la taula.

Consell: si tries EKS, tria també Karpenter i Auto Mode des del principi. Començar amb Cluster Autoscaler i migrar després és feina duplicada.

Consell: activa els registres d'auditoria del pla de control el primer dia. No són retroactius, i són l'única cosa que respon «qui va esborrar aquest Deployment».

Consell: fixa topologySpreadConstraints per zona en tot Deployment de producció. Sense ells, el planificador pot posar les tres rèpliques a la mateixa AZ i una zona caiguda et deixa sense servei.

Exercicis

Exercici 1: traduir el servei de treballadors a Kubernetes

Els treballadors de cola-mercadofresco-pedidos corren avui a Fargate amb 0,5 vCPU i 1 GB, escalant de 2 a 8 tasques segons el retard de la cua, amb un repartiment 1:4 entre sota demanda i Spot. Escriu l'equivalent a Kubernetes: (a) el Deployment complet, amb les sondes que corresponguin a un procés sense servidor HTTP i l'apagada ordenada; (b) com resols l'autoescalat per profunditat de cua, indicant quin component addicional cal i per què l'HPA natiu no basta; (c) com aconsegueixes l'equivalent del repartiment entre capacitat sota demanda i Spot; i (d) enumera totes les peces que has hagut d'afegir al clúster per aconseguir el que a ECS eren tres paràmetres.

Exercici 2: l'incident de les sondes

Un divendres a les 19:20, amb el pic en marxa, Aurora pateix una degradació i les consultes passen de 20 ms a 900 ms. Als dos minuts, la botiga desapareix sencera: tots els pods són a CrashLoopBackOff, l'ALB no té destinacions sanes i mercadofresco-alb-latencia-alta ha donat pas a un error total. La base de dades, mentrestant, continua responent, lenta però viva. Explica (a) la cadena causal exacta que ha convertit una degradació en una caiguda total; (b) quina configuració concreta del manifest és la culpable i per què a ECS amb la comprovació d'estat del grup de destinació el resultat hauria estat diferent; (c) com es redissenyen les tres sondes perquè aquest incident sigui una degradació i no una caiguda; (d) quin mecanisme afegiries al codi de la botiga per degradar amb elegància; i (e) quina alarma hauria avisat abans.

Exercici 3: la revisió de la decisió, divuit mesos després

Han passat divuit mesos. MercadoFresco ha crescut: opera a quatre ciutats, té 22 serveis en lloc de 3, l'equip de plataforma són ara 4 persones de les quals 2 han treballat amb Kubernetes a la seva feina anterior, i el client majorista més gran exigeix per contracte que la seva instància del servei de comandes pugui executar-se al seu propi centre de dades. A més, l'equip de dades vol fer servir un operador de Kubernetes per gestionar els seus fluxos de processament. Aplica la taula de criteris de la lliçó i respon: (a) quins llindars es compleixen ara; (b) quina és la teva recomanació i amb quin nivell de confiança; (c) si recomanes migrar, com ho faries sense repetir els errors del pla de migració de 09-04; (d) què es queda a ECS encara que migris; i (e) quin cost anual total estimaries per a la decisió, incloent-hi personal.

Solucions

Solució 1

(a) El Deployment.

apiVersion: apps/v1
kind: Deployment
metadata: { name: trabajadores-pedidos, namespace: mercadofresco }
spec:
  replicas: 2
  selector: { matchLabels: { app: trabajadores-pedidos } }
  template:
    metadata: { labels: { app: trabajadores-pedidos } }
    spec:
      serviceAccountName: sa-trabajadores
      terminationGracePeriodSeconds: 120        # equival a stopTimeout 120 d'ECS
      topologySpreadConstraints:
        - { maxSkew: 1, topologyKey: topology.kubernetes.io/zone,
            whenUnsatisfiable: ScheduleAnyway, labelSelector: { matchLabels: { app: trabajadores-pedidos } } }
      containers:
        - name: trabajador
          image: 555566667777.dkr.ecr.eu-west-1.amazonaws.com/mercadofresco/trabajadores@sha256:71ab...c3d9
          resources:
            requests: { cpu: "500m", memory: "1Gi" }
            limits:   { cpu: "500m", memory: "1Gi" }
          securityContext: { runAsNonRoot: true, runAsUser: 10002, readOnlyRootFilesystem: true }
          # Sense HTTP: s'executa una comanda que comprova la marca de batec a /tmp.
          livenessProbe:
            exec: { command: ["python", "-m", "trabajadores.salud"] }
            periodSeconds: 30
            failureThreshold: 3
          startupProbe:
            exec: { command: ["python", "-m", "trabajadores.salud"] }
            periodSeconds: 5
            failureThreshold: 12
          volumeMounts: [{ name: temporal, mountPath: /tmp }]
      volumes: [{ name: temporal, emptyDir: {} }]

No hi ha readinessProbe i és deliberat: readiness decideix si un pod rep trànsit d'un Service, i a un treballador de cua ningú no li envia trànsit. Posar-ne una seria soroll sense efecte. liveness sí que és imprescindible, per la mateixa raó que a 10-01: un treballador penjat no mor, deixa de consumir la cua i ningú no se n'assabenta. L'apagada ordenada és el mateix gestor de SIGTERM d'aquell exercici, amb terminationGracePeriodSeconds: 120 com a equivalent del stopTimeout.

(b) Autoescalat per profunditat de cua. L'HPA natiu només entén CPU i memòria de les mètriques de recursos, i ja s'ha vist que la CPU d'un consumidor de cua no reflecteix el retard. Cal KEDA, que instal·la un adaptador de mètriques externes i porta un escalador específic per a SQS:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata: { name: escalado-trabajadores, namespace: mercadofresco }
spec:
  scaleTargetRef: { name: trabajadores-pedidos }
  minReplicaCount: 2
  maxReplicaCount: 8
  cooldownPeriod: 300
  triggers:
    - type: aws-sqs-queue
      metadata:
        queueURL: https://sqs.eu-west-1.amazonaws.com/333344445555/cola-mercadofresco-pedidos
        queueLength: "15"                 # missatges per replica, com a 10-02
        awsRegion: eu-west-1
      identityOwner: operator

L'alternativa sense KEDA és l'adaptador de mètriques de CloudWatch més un HPA amb mètrica externa, que és més artesanal. En qualsevol cas, és un component més per instal·lar, versionar i actualitzar per aconseguir el que a ECS era una política d'Application Auto Scaling.

(c) L'equivalent del repartiment sota demanda / Spot. No hi ha un capacityProviderStrategy; s'aconsegueix amb Karpenter declarant dos NodePool, un amb capacity-type: on-demand i un altre amb spot, i fent servir al Deployment nodeAffinity preferida cap a Spot més topologySpreadConstraints sobre l'etiqueta karpenter.sh/capacity-type per garantir que almenys una rèplica caigui a sota demanda. També cal el gestor d'interrupcions, que Karpenter inclou: escolta l'avís de dos minuts d'EC2 i drena el node. Resultat: el que a ECS eren dues línies de --capacity-provider-strategy aquí són dos NodePool, regles d'afinitat, restriccions de topologia i un controlador.

(d) Les peces afegides. Per igualar tres paràmetres d'ECS ha calgut: Karpenter (nodes i Spot), KEDA (escalat per cua), Metrics Server (base de l'HPA), EKS Pod Identity Agent (permisos d'SQS del pod) i, si es vol observabilitat equivalent, el complement de CloudWatch Observability. Cinc components, cadascun amb la seva versió, la seva compatibilitat amb la versió de Kubernetes i el seu propi cicle d'actualització. Aquesta és exactament l'aritmètica de la secció de complements.

Solució 2

(a) La cadena causal. Aurora es degrada a 900 ms. El /salud de la botiga consulta la base de dades, així que comença a trigar més dels 3 segons de timeoutSeconds. La livenessProbe acumula 3 fallades consecutives —uns 60 segons amb periodSeconds: 20— i Kubernetes reinicia el contenidor. En reiniciar-se, el pod ha d'arrencar de nou, precarregar la memòria cau del catàleg i tornar a connectar amb la mateixa Aurora lenta, amb la qual cosa falla també l'arrencada. Com que tots els pods consulten la mateixa base de dades, això els passa als tres alhora i de manera sincronitzada. Els reinicis repetits porten a CrashLoopBackOff, amb esperes creixents entre intents. I hi ha un agreujant: els reinicis massius obren i tanquen connexions contra una Aurora ja saturada, empitjorant la causa arrel. Un bucle de realimentació complet.

(b) La culpable i la diferència amb ECS. La culpable és la livenessProbe apuntant a un /salud que comprova dependències externes. A ECS, la comprovació d'estat del grup de destinació té un efecte diferent: una destinació no sana es treu del balanceig, i el servei només reposa tasques si la tasca mor o si el desplegament ho exigeix. L'equivalent de liveness a ECS és el healthCheck del contenidor, que la majoria de la gent configura de manera més laxa o ni tan sols posa. És a dir: Kubernetes és més agressiu per omissió i et dona una eina més perillosa, i per això exigeix entendre-la. La diferència no és que ECS sigui millor, és que Kubernetes et deixa disparar més lluny.

(c) El redisseny de les tres sondes.

  • livenessProbe/vivo, un punt d'enllaç que només confirma que el procés respon i el bucle d'esdeveniments no està bloquejat. Sense tocar Aurora, sense tocar la memòria cau, sense tocar la xarxa. Amb periodSeconds: 20 i failureThreshold: 5, per no reiniciar per un pic transitori.
  • readinessProbe/listo, que sí que comprova les dependències: connexió a Aurora i a ElastiCache. La seva fallada treu el pod del balanceig però no el mata, així que quan Aurora es recupera el pod torna a rebre trànsit sense haver reiniciat. Aquí la fallada és reversible i barata.
  • startupProbe/listo amb failureThreshold ampli (12 × 5 s = 60 s), que cobreix la precàrrega del catàleg i suspèn les altres dues mentrestant.

Amb aquest disseny, l'incident del divendres hauria estat: tots els pods marcats com a no llestos, l'ALB retornant 503 a les peticions que necessiten base de dades, els pods vius i amb la memòria cau calenta, i recuperació immediata en recuperar-se Aurora. Degradació, no caiguda.

(d) Degradació amb elegància al codi. Que /listo distingeixi entre «no puc fer res» i «puc fer una part». La botiga pot servir el catàleg des de mercadofresco-catalogo a ElastiCache encara que Aurora vagi lenta; el que no pot és confirmar comandes. Amb un interruptor de circuit (07-05) al voltant de les crides a Aurora, la botiga deixa d'intentar-ho després de N fallades, retorna un missatge clar al pas de pagament i continua servint navegació. El client veu «ara mateix no podem confirmar comandes» en lloc d'una pàgina en blanc, i Aurora deixa de rebre l'allau de reintents que impedia la seva recuperació.

(e) L'alarma que hauria avisat abans. La de latència d'Aurora, ReadLatency i WriteLatency, amb llindar sobre el percentil 99 i no sobre la mitjana —la mitjana triga molt a moure's—. Hauria saltat al minut de la degradació, abans que la primera livenessProbe acumulés les seves tres fallades, donant un marge d'acció real. Complementàriament, una alarma sobre reinicis de pods (pod_number_of_container_restarts a Container Insights) hauria identificat el bucle de realimentació immediatament, que és la informació que faltava per diagnosticar en calent.

Solució 3

(a) Llindars que es compleixen. Quatre dels sis:

Criteri Es compleix? Detall
Experiència prèvia 2 de 4 persones amb Kubernetes en producció
Multinúvol o híbrid Sí, i és contractual El majorista exigeix execució al seu centre de dades
Operadors de l'ecosistema L'equip de dades vol un operador de fluxos
Mida de l'equip 4 persones dedicades
Nombre de serveis Parcialment 22, per sota del llindar de 30-40 però a prop
Planificació avançada No Res no ho exigeix encara

(b) Recomanació. Sí, migrar a EKS, amb confiança alta. El criteri decisiu és el segon: el requisit híbrid contractual és l'única cosa que ECS no pot satisfer de cap manera, i no és una preferència sinó una obligació amb un client. Els altres tres reforcen que ara el cost és assumible: hi ha experiència, hi ha equip i hi ha una necessitat concreta d'ecosistema. La decisió de fa divuit mesos no era errònia: era correcta amb les dades d'aleshores, i aquesta és precisament la utilitat d'haver escrit els criteris per endavant.

(c) Com migrar. Aplicant les lliçons de 09-04 i del mateix mòdul 10:

  1. Un clúster nou, no una conversió. El clúster de desenvolupament primer, amb Auto Mode o Karpenter des del principi per no duplicar feina.
  2. Primer un servei no crític. El servei d'informes o el de notificacions, no la botiga. S'aprèn amb una cosa la fallada de la qual no costa comandes.
  3. Els complements abans que les càrregues, amb les seves versions fixades a Git i desplegats amb Helm des del pipeline, mai a mà.
  4. Convivència real i prolongada. ECS i EKS alhora durant mesos, amb l'ALB repartint per pesos igual que a 10-01 i 10-02. Res d'una nit de tall.
  5. La botiga, l'última, i amb el mateix repartiment 90/10 que es va fer servir per passar d'EC2 a Fargate.
  6. Data límit explícita per fase, que és l'error que 09-04 assenyalava: les migracions a mitges duren anys si ningú no posa fites.
  7. La instal·lació del client majorista al final, quan l'equip ja operi EKS amb desimboltura, no com a primer projecte.

(d) Què es queda a ECS encara que migris. El que no guanya res amb el canvi: les Lambdes (-cobrar-pago, -reservar-stock, -generar-miniaturas, -asignar-reparto), que no són contenidors i continuen sent la millor opció per a feina esporàdica; les tasques programades de càrrega a Redshift, que funcionen perfectament amb EventBridge Scheduler; i probablement els treballadors de les cues menys crítiques durant força temps, perquè migrar-los no aporta i sí que consumeix atenció. Migrar-ho tot per coherència estètica és un error car: la coherència es mesura en criteris, no en tecnologies.

(e) Cost anual estimat. Infraestructura: 3 clústers × 73 USD × 12 = 2.628 USD de plans de control, més el diferencial de nodes enfront de Fargate, que amb Karpenter i Spot ben fets servir pot ser fins i tot favorable —diguem-ne neutre—. Personal: 25 dies l'any de feina de plataforma en actualitzacions i manteniment del clúster, més un cost únic de 2-3 mesos-persona per a la migració. Amb un cost intern estimat de 400 USD per dia, el recurrent són uns 10.000 USD l'any i la migració, uns 20.000-25.000 USD d'una vegada. Total del primer any: de l'ordre de 35.000 USD. Aquest és el número que cal posar davant de la direcció juntament amb el contracte del client majorista, perquè la decisió no és tècnica: és una inversió amb una justificació comercial concreta.

Conclusió

MercadoFresco ha acabat el mòdul amb la botiga fora de les màquines i amb una resposta raonada a la pregunta de Kubernetes.

Tens Kubernetes entès de debò: un orquestrador extensible amb API pròpia, el valor real del qual és a la portabilitat, l'ecosistema i l'extensibilitat amb CRD i operadors; el seu vocabulari mínim —Pod, ReplicaSet, Deployment, Service, Ingress, Namespace, ConfigMap i Secret, amb l'advertiment que un Secret és base64 i no xifratge—; i el model declaratiu amb bucle de reconciliació que explica per què esborrar un pod a mà no serveix de res i per què GitOps hi encaixa de manera natural. Tens clar què gestiona EKS —pla de control en tres AZ, etcd, el mecanisme d'actualització i la integració amb IAM— i la frase que evita la majoria de les decepcions: EKS gestiona el pla de control, no el clúster.

Tens les quatre opcions de còmput amb els seus límits reals —inclosa la que EKS sobre Fargate no admet DaemonSet, cosa que obliga a mantenir nodes per als complements—, la creació del clúster amb eksctl i amb CDK, aws eks update-kubeconfig i la disciplina de mirar el context abans d'escriure un delete. I l'autenticació moderna: entrades d'accés en lloc d'aws-auth, amb els conjunts de permisos de 09-04 governant també el clúster, i Pod Identity com a equivalent exacte del rol de tasca d'ECS.

Tens els manifests complets de la botiga: Deployment amb les tres sondes ben separades —startup per arrencar, readiness per rebre trànsit, liveness per reiniciar—, requests i limits com a conceptes diferents, repartiment per AZ amb topologySpreadConstraints, preStop per no perdre peticions i context de seguretat sense privilegis; el Service; i l'Ingress amb l'AWS Load Balancer Controller, que crea el mateix ALB del mòdul 3 amb el WAF del mòdul 4, amb target-type: ip i group.name per no pagar un balancejador per servei. Més l'autoescalat en dos plans: HPA per als pods, Karpenter per als nodes, i l'observació que per igualar tres paràmetres d'ECS van caldre cinc components.

I tens l'argument que gairebé ningú no posa per escrit: el cost ocult de les actualitzacions. Tres versions l'any, 14 mesos de suport estàndard, un salt automàtic de 73 a 438 USD mensuals en entrar a l'estès, i 20-30 dies de feina de plataforma l'any revisant API retirades, matrius de compatibilitat i drenatges de nodes. A ECS aquesta feina no existeix. Amb la comparativa honesta entre tots dos —EKS compra flexibilitat, portabilitat i ecosistema, i ho paga amb complexitat, personal i feina recurrent— i la decisió de MercadoFresco: es queda a ECS amb Fargate, per un equip de plataforma de dues persones, l'absència de requisits que ECS no cobreixi, l'absència de necessitat multinúvol i la velocitat; amb els sis criteris objectius, escrits per endavant, que canviarien aquesta decisió el dia que es compleixin.

Què ha canviat per a MercadoFresco en aquest mòdul. Ja no hi ha AMI per reconstruir quan canvia una dependència: hi ha una imatge a mercadofresco/tienda versionada per commit i desplegada per digest. Ja no hi ha sistema operatiu per apedaçar: l'amfitrió és d'AWS i l'escaneig continu d'Inspector avisa del que sí que és teu. I ja no hi ha arrencades de dos minuts: hi ha tasques que reben trànsit en quaranta segons, amb un escalat programat que a més les té llestes abans que arribi el pic del divendres.

graph TB
  U[Clients] --> CF["CloudFront E2QWERTY123ABC<br/>+ waf-mercadofresco-cdn"]
  CF --> ALB["alb-mercadofresco-tienda<br/>tg-mercadofresco-tienda / -verde"]
  ALB --> FG["ECS Fargate<br/>svc-mercadofresco-tienda-fg<br/>arm64, 2-20 tasques"]
  FG --> AU[("aurora-mercadofresco-pedidos")]
  FG --> EC[("mercadofresco-catalogo<br/>ElastiCache")]
  FG --> DY[("mercadofresco-carritos")]
  FG --> SQS["cola-mercadofresco-pedidos"]
  SQS --> TR["ECS Fargate + Spot<br/>svc-mercadofresco-trabajadores"]
  TR --> SF["mercadofresco-procesar-pedido<br/>Lambdes de pagament, estoc i repartiment"]
  ECR["ECR 555566667777<br/>mercadofresco/tienda"] -.digest.-> FG
  PIPE["pipeline-mercadofresco-tienda<br/>blue/green canari"] -.desplega.-> FG
  OBS["Container Insights, X-Ray<br/>mercadofresco-produccion"] -.observa.-> FG

I aquí arriba la pregunta que ningú no ha fet encara i que el gerent farà dilluns al matí. L'arquitectura està completa: és elàstica, segura, observable, desplegable des d'un pipeline, descrita en codi i repartida en cinc comptes. Però quant costa tot això? Està ben gastat? Com es reparteix entre producció, preproducció i desenvolupament, i entre les quatre ciutats? Hi ha res encès que ningú no faci servir? I què es podria comprometre per endavant per pagar menys?

Al mòdul 11, «Millors pràctiques i gestió de costos», es respon amb mètode. 11-01 revisa l'arquitectura completa amb el Well-Architected Framework i els seus sis pilars, que és la manera estructurada d'auditar el que s'ha construït. 11-02 posa ordre a l'etiquetatge i l'assignació de costos, convertint les cinc etiquetes obligatòries en informes que algú pugui llegir. 11-03 analitza la factura de debò amb Cost Explorer. 11-04 posa límits i avisos amb Budgets. 11-05 compromet capacitat amb Savings Plans i instàncies reservades, que és on la comparació Fargate enfront d'EC2 de 10-02 queda per fi tancada. I 11-06 tanca el curs amb el projecte final: dissenyar, justificar i pressupostar una arquitectura completa per a MercadoFresco, amb tot el que s'ha après en onze mòduls.

Curs d'AWS

Mòdul 1: Introducció a AWS

Mòdul 2: Serveis principals d'AWS

Mòdul 3: Xarxes i lliurament de contingut

Mòdul 4: Seguretat i identitat

Mòdul 5: Monitoratge i gestió

Mòdul 6: Bases de dades

Mòdul 7: Integració d'aplicacions

Mòdul 8: Eines per a desenvolupadors

Mòdul 9: Infraestructura com a codi i govern de comptes

Mòdul 10: Contenidors a AWS

Mòdul 11: Millors pràctiques i gestió de costos

© Copyright 2026. Tots els drets reservats