Módulo 7: Eks Specifics For Production
7. Manos a la obra: el YAML de EKS, mostrado y explicado
Descripción
Las seis lecciones anteriores de este módulo construyeron el vocabulario y la sintaxis pieza por pieza: el plano de control gestionado, las tres formas de cómputo, Karpenter, IRSA/Pod Identity, el AWS Load Balancer Controller. Esta lección las reúne en un único flujo, de principio a fin, tal como lo escribiría un equipo real preparando la migración de andes-cargo-cluster: un archivo de configuración de eksctl, un IAMServiceAccount, y el IngressClass del AWS Load Balancer Controller. Nada de esto corre. Cada bloque está verificado, campo por campo, contra la documentación oficial de eksctl.io/docs.aws.amazon.com/eks citada al pie de cada sección — pero la etiqueta "(representativo)" acompaña cada comando y cada salida esperada, sin ninguna excepción.
Conexión con el módulo
Esta es, deliberadamente, la lección "manos a la obra" de este módulo — con el nombre que ya viste en el mapa de la lección 1: no "manos a la obra ejecutado", como en los seis módulos anteriores, sino "el YAML mostrado y explicado". Si llegaste hasta aquí esperando ver una terminal real con eksctl create cluster corriendo contra una cuenta AWS, vuelve a la lección 1 — esa expectativa quedó corregida ahí, antes de que esta lección empezara.
Paso 1 — El archivo de configuración del clúster: cluster.yaml
eksctl, la herramienta oficial de línea de comandos para EKS, acepta dos formas de crear un clúster: banderas sueltas en la terminal, o un archivo de configuración declarativo (ClusterConfig). Para un clúster real de producción —con más de un tipo de nodo, con una región fija, con Fargate profiles y managed node groups conviviendo— el archivo de configuración es el patrón que la propia documentación de eksctl recomienda, por la misma razón que ya conoces de Terraform: es reproducible, revisable en un pull request, y no depende de que alguien recuerde la secuencia exacta de banderas.
cluster.yaml (representativo) — verificado, campo por campo, contra docs.aws.amazon.com/eks/latest/eksctl, reconstruyendo el clúster andes-cargo-cluster con lo que las lecciones 2-6 de este módulo ya cubrieron:
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: andes-cargo-cluster
region: us-east-1
version: "1.34"
managedNodeGroups:
- name: andes-cargo-managed-ng
instanceType: t3.medium
desiredCapacity: 2
minSize: 2
maxSize: 4
privateNetworking: true
fargateProfiles:
- name: andes-cargo-fargate-batch
selectors:
- namespace: andes-cargo
labels:
workload-type: batch
iam:
withOIDC: true
Cada bloque de nivel superior responde una pregunta distinta, exactamente como ya aprendiste a leer un Application de ArgoCD en el Módulo 5:
metadata.name: andes-cargo-cluster— el mismo nombre exacto, reutilizado por tercera vez en este ecosistema:aws-serverless-and-containers-guidelo dejó documentado en ECS,kindlo hizo real en el Módulo 1 de esta guía, y aquí aparece como el nombre que tendría el clúster EKS real si esta migración se ejecutara.managedNodeGroups— la forma de cómputo que la lección 3 de este módulo recomendó paraandes-cargo-status-api: sin requisitos especiales de AMI,always-on, el punto de partida por defecto.fargateProfiles— agregado aquí solo como ejemplo de sintaxis (no porqueandes-cargo-status-apilo necesite): un Fargate profile para cargas por lotes futuras del namespaceandes-cargo, mostrando que ambas formas de cómputo pueden convivir en el mismo clúster.iam.withOIDC: true— el campo que crea el proveedor OIDC del clúster, la pieza exacta que IRSA necesita (lección 5) para poder existir. Sin este campo entrue, ningúnIAMServiceAccountcon IRSA de este mismo archivo podría funcionar.
Comando de aplicación (representativo):
eksctl create cluster -f cluster.yaml
Qué esperar (representativo) — la secuencia de eventos documentada por eksctl para la creación de un clúster con esta configuración, reconstruida de la referencia oficial, nunca vista en una terminal real para esta lección:
[ℹ] eksctl version 0.<versión>
[ℹ] using region us-east-1
[ℹ] setting availability zones to [us-east-1a us-east-1b]
[ℹ] subnets for us-east-1a - public:192.168.0.0/19 private:192.168.32.0/19
[ℹ] subnets for us-east-1b - public:192.168.64.0/19 private:192.168.96.0/19
[ℹ] nodegroup "andes-cargo-managed-ng" will use "" [AmazonLinux2/1.34]
[ℹ] using Kubernetes version 1.34
[ℹ] creating EKS cluster "andes-cargo-cluster" in "us-east-1" region with managed nodes
[ℹ] will create 2 separate CloudFormation stacks for cluster itself and the initial managed nodegroup
[ℹ] if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=us-east-1 --cluster=andes-cargo-cluster'
[ℹ] building cluster stack "eksctl-andes-cargo-cluster-cluster"
[ℹ] deploying stack "eksctl-andes-cargo-cluster-cluster"
[ℹ] building managed nodegroup stack "eksctl-andes-cargo-cluster-nodegroup-andes-cargo-managed-ng"
[ℹ] deploying stack "eksctl-andes-cargo-cluster-nodegroup-andes-cargo-managed-ng"
[ℹ] waiting for the control plane to become ready
[✔] saved kubeconfig as "~/.kube/config"
[ℹ] EKS cluster "andes-cargo-cluster" in "us-east-1" region is ready
El tiempo real de este proceso, documentado de forma consistente por AWS y por la comunidad, es de 10 a 15 minutos — órdenes de magnitud más lento que el kind create cluster del Módulo 1, que terminó en menos de un minuto. Esa diferencia de tiempo, por sí sola, ya es evidencia indirecta de la diferencia de fondo entre ambos: kind levanta contenedores Docker locales; eksctl orquesta, por debajo, pilas completas de CloudFormation que crean VPC, subredes, un plano de control gestionado real, y un Auto Scaling Group con instancias EC2 reales.
Paso 2 — Un IAMServiceAccount con IRSA, para andes-cargo-status-api
Con iam.withOIDC: true ya aplicado (Paso 1), el clúster tiene un proveedor OIDC propio. Ahora se puede declarar el ServiceAccount con IRSA que la lección 5 de este módulo ya explicó, esta vez dentro del mismo cluster.yaml en vez de como un comando eksctl create iamserviceaccount suelto — el patrón que un equipo real preferiría para mantener todo el estado del clúster en un solo archivo versionado:
Fragmento agregado a cluster.yaml (representativo):
iam:
withOIDC: true
serviceAccounts:
- metadata:
name: status-api-service-account
namespace: andes-cargo
attachPolicyARNs:
- "arn:aws:iam::000000000000:policy/StatusApiReadOnlyPolicy"
roleName: StatusApiTaskRole
Qué esperar (representativo) al aplicar este cambio con eksctl update iamserviceaccount -f cluster.yaml --approve sobre un clúster ya existente:
[ℹ] 1 iamserviceaccount (andes-cargo/status-api-service-account) was included (based on the include/exclude rules)
[ℹ] 1 task: { create IAM role for serviceaccount "andes-cargo/status-api-service-account" }
[ℹ] building iamserviceaccount stack "eksctl-andes-cargo-cluster-addon-iamserviceaccount-andes-cargo-status-api-service-account"
[ℹ] deploying stack "eksctl-andes-cargo-cluster-addon-iamserviceaccount-andes-cargo-status-api-service-account"
[ℹ] created serviceaccount "andes-cargo/status-api-service-account"
El resultado, dentro del clúster, es exactamente el ServiceAccount anotado que la lección 5 ya mostró:
kubectl describe serviceaccount status-api-service-account -n andes-cargo
Name: status-api-service-account
Namespace: andes-cargo
Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::000000000000:role/StatusApiTaskRole
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: <none>
Y la última pieza, el Deployment de andes-cargo-status-api referenciando ese ServiceAccount — el único campo nuevo frente al deployment.yaml real del Módulo 2, agregado sin tocar ningún otro campo existente:
spec:
template:
spec:
serviceAccountName: status-api-service-account
containers:
- name: andes-cargo-status-api
image: 000000000000.dkr.ecr.us-east-1.amazonaws.com/andes-cargo-status-api:latest
Paso 3 — El IngressClass del AWS Load Balancer Controller
La última pieza de esta lección es el IngressClass que la lección 6 de este módulo ya explicó a fondo, mostrado aquí como parte del flujo completo de configuración, con el Ingress real de andes-cargo-status-api apuntándole:
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: alb
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: ingress.k8s.aws/alb
Comando de aplicación (representativo):
kubectl apply -f ingressclass.yaml
Qué esperar (representativo):
ingressclass.networking.k8s.io/alb created
Con la anotación ingressclass.kubernetes.io/is-default-class: "true", cualquier Ingress nuevo que no declare explícitamente un ingressClassName usaría esta clase por defecto — una decisión de conveniencia documentada por el propio proyecto, útil cuando un clúster tiene un solo controlador de Ingress instalado (a diferencia de un clúster con ingress-nginx y el AWS Load Balancer Controller conviviendo, donde declarar ingressClassName de forma explícita en cada Ingress, como ya hace el ingress.yaml real del Módulo 4, evita cualquier ambigüedad).
El flujo completo, de un vistazo
EL FLUJO REPRESENTATIVO COMPLETO DE ESTA LECCIÓN
1. eksctl create cluster -f cluster.yaml
└─ crea VPC, subredes, plano de control (AWS), managed node group,
Fargate profile, y el proveedor OIDC (iam.withOIDC: true)
│
2. eksctl update iamserviceaccount -f cluster.yaml --approve
└─ crea el rol StatusApiTaskRole + el ServiceAccount anotado
(IRSA, lección 5 de este módulo)
│
3. helm install aws-load-balancer-controller ...
└─ el propio controlador necesita SU PROPIO IAMServiceAccount
(lección 6 de este módulo), antes de este paso
│
4. kubectl apply -f ingressclass.yaml
└─ declara la clase "alb" para que el Ingress la use
│
5. kubectl apply -f andes-cargo-k8s/ (el mismo repositorio del M5)
└─ deployment.yaml (con serviceAccountName agregado),
service.yaml, ingress.yaml (con ingressClassName: alb) — TODO
EJECUTADO EN EL DOCUMENTO REAL DE LA LECCIÓN 8
El último paso de este diagrama es, deliberadamente, la conexión directa hacia la lección 8: el repositorio andes-cargo-k8s/ que ArgoCD ya sincroniza de verdad en kind (Módulo 5) es el mismo repositorio que, con los ajustes puntuales de este módulo, aplicaría contra un clúster EKS real — el documento de migración de la próxima lección detalla, recurso por recurso, exactamente cuáles de esos ajustes hacen falta.
Errores comunes
Copiar y ejecutar estos comandos contra una cuenta AWS real sin haber leído la lección 1 de este módulo (de expectativa de costo). Qué pasa: alguien, motivado por ver sintaxis real y completa, copia eksctl create cluster -f cluster.yaml y lo corre contra su propia cuenta de AWS, sin haber calculado el costo. Cómo detectarlo: si estás a punto de correr cualquier comando de esta lección contra una cuenta AWS con facturación activa, sin haber revisado antes el costo aproximado de un plano de control EKS (una tarifa por hora, documentada por AWS, además del costo de los nodos EC2 que este cluster.yaml también crea). Cómo corregirlo: si quieres experimentar con esta sintaxis contra una cuenta real, hazlo con conocimiento explícito del costo y con un plan de limpieza claro (eksctl delete cluster) — esta lección lo muestra con fines pedagógicos, verificado contra documentación oficial, no como una invitación a ejecutarlo sin evaluar el costo primero.
Asumir que managedNodeGroups y fargateProfiles son mutuamente excluyentes dentro del mismo cluster.yaml (técnico). Qué pasa: alguien piensa que un clúster tiene que elegir una sola forma de cómputo, y se sorprende al ver ambos bloques en el mismo archivo de esta lección. Cómo detectarlo: si tu propio cluster.yaml solo declara una de las dos secciones, asumiendo que es obligatorio elegir. Cómo corregirlo: como ya viste en la lección 3 de este módulo, un clúster EKS real puede combinar managed node groups, nodos autogestionados, y Fargate profiles al mismo tiempo — el cluster.yaml de esta lección lo demuestra a propósito, con un managed node group para la carga principal y un Fargate profile para una carga secundaria distinta.
Olvidar que el IAMServiceAccount de andes-cargo-status-api y el del propio AWS Load Balancer Controller son dos recursos completamente distintos (de superposición de conceptos vecinos). Qué pasa: alguien confunde el ServiceAccount que el Paso 2 crea para el código de la aplicación con el que la lección 6 ya mostró para el propio controlador de balanceadores. Cómo detectarlo: si tu plan de aplicación solo incluye un IAMServiceAccount, esperando que sirva para ambos propósitos. Cómo corregirlo: son dos identidades separadas, con roles y policies distintos — status-api-service-account (con StatusApiTaskRole, permiso de solo lectura sobre Shipments) es para el código de tu aplicación; aws-load-balancer-controller (con AWSLoadBalancerControllerIAMPolicy) es para el propio controlador, que necesita permiso de crear/modificar balanceadores en tu cuenta — la misma disciplina de mínimo privilegio, aplicada a dos consumidores distintos.
Ejercicios
Ejercicio 1 — Traza qué campo de cluster.yaml habilita cada pieza de este módulo. Sin volver a mirar el Paso 1, di qué campo exacto de cluster.yaml corresponde a: (a) el proveedor OIDC que IRSA necesita; (b) la forma de cómputo recomendada para andes-cargo-status-api; (c) la versión de Kubernetes del plano de control.
Ver solución
(a) iam.withOIDC: true. (b) managedNodeGroups (con el nombre andes-cargo-managed-ng). (c) metadata.version ("1.34" en el ejemplo de esta lección). Si nombraste los tres sin releer el Paso 1, tienes clara la anatomía completa del archivo.
Ejercicio 2 — Explica por qué el Deployment real de andes-cargo-status-api solo necesita un campo nuevo. Un colega te pregunta cuántos campos del deployment.yaml real (Módulo 2) tendrían que cambiar para correr en EKS con IRSA. Respóndele con el número exacto y el nombre del campo.
Ver solución
Uno solo: spec.template.spec.serviceAccountName, apuntando a status-api-service-account. Todo lo demás del Deployment —réplicas, resources.limits/requests, probes (Módulo 3), selector de etiquetas— permanece exactamente igual. El único otro cambio real, mencionado en el Paso 2, es la referencia de imagen (image:), que pasaría del registro local registry:3 de kind a un repositorio real de ECR — no relacionado con IRSA en sí, sino con dónde vive la imagen.
Ejercicio 3 — Predice qué pasaría si aplicaras ingressclass.yaml sin haber instalado antes el AWS Load Balancer Controller. Basándote en lo que ya sabes de IngressClass como objeto de Kubernetes (Módulo 4) y de la lección 6 de este módulo, predice: si aplicas el IngressClass de esta lección antes de que el controlador esté corriendo, ¿el comando falla?
Ver solución
No falla — un IngressClass es, en sí mismo, solo un objeto declarativo que Kubernetes acepta y guarda en etcd, sin verificar si algún controlador real está escuchando por ese nombre. El kubectl apply -f ingressclass.yaml se completaría con éxito (ingressclass.networking.k8s.io/alb created) igual que si el controlador ya estuviera corriendo. El problema aparecería después, recién cuando alguien aplique un Ingress real con ingressClassName: alb: ese objeto quedaría sin ninguna dirección asignada (ADDRESS vacío), porque no habría ningún controlador activo que reclamara esa clase — el mismo síntoma, por una causa distinta, del "Errores comunes" de la lección 6.
Resumen y siguiente paso
Esta lección reunió, en un único flujo verificado contra eksctl.io y docs.aws.amazon.com/eks, todo el YAML/CLI de este módulo: un cluster.yaml completo (managed node group, Fargate profile, proveedor OIDC), un IAMServiceAccount con IRSA para status-api-service-account/StatusApiTaskRole, y el IngressClass del AWS Load Balancer Controller — cada bloque etiquetado explícitamente como representativo, ninguno ejecutado contra una cuenta AWS real. Confirmaste que el Deployment real de andes-cargo-status-api (Módulo 2) solo necesitaría un campo nuevo (serviceAccountName) para correr con IRSA en EKS, y trazaste el flujo completo de dependencias: primero el clúster, después las identidades, recién al final los objetos de aplicación.
Antes de avanzar deberías poder: explicar qué campo de cluster.yaml habilita cada pieza de las lecciones 2-6 de este módulo; nombrar el único campo nuevo que necesitaría el Deployment real; y describir la secuencia de dependencias completa, del clúster al Ingress.
Siguiente lección: el proyecto de este módulo — el plan de migración de Andes Cargo, kind → EKS real. Ahí vas a construir el único documento de este módulo con evidencia ejecutada de verdad: una tabla, recurso por recurso, de qué cambia y qué no cambia en andes-cargo-k8s/ — construida contra los diez manifiestos reales de ese repositorio, no contra ninguna API externa.
Recursos
- Amazon EKS — Creating and managing clusters (
eksctl) — la fuente exacta del formatoClusterConfigy de la salida deeksctl create cluster -f cluster.yamlreconstruida en esta lección. eksctl— IAM Service Accounts — sintaxis completa del bloqueiam.serviceAccountsdentro de unClusterConfig.- Amazon EKS — Assign IAM roles to Kubernetes service accounts — verificación cruzada del
ServiceAccountanotado resultante. - AWS Load Balancer Controller — IngressClass — la fuente exacta del
IngressClassde esta lección. kubernetes-and-eks-in-production-guide(NIEVA), Módulo 2, lección 5 — eldeployment.yamlreal deandes-cargo-status-api, el archivo base al que esta lección agrega un único campo nuevo.