Módulo 8: Capstone Andes Cargo On Kubernetes

8. Proyecto final: `andes-cargo-k8s/` como entregable del capstone

Descripción

Este proyecto cierra la guía completa con el mismo criterio de disciplina que cerró cada módulo anterior: no agrega ningún concepto nuevo, reúne todo lo que ya existe en un solo entregable defendible. El repositorio andes-cargo-k8s/ —más gatekeeper/, kyverno/, kind-config.yaml y el script de escaneo del Módulo 6— es, hoy, once archivos reales que corrieron contra un clúster real, verificados una última vez contra andes-cargo-cluster para escribir este proyecto. El checklist de esta lección es la pieza que puedes llevar a una entrevista y defender línea por línea: qué corrió de verdad, qué quedó representativo, y por qué cada decisión fue la correcta.

Conexión con el módulo

Cada lección anterior de este módulo construyó una pieza de esta síntesis final: el mapa de arquitectura (lección 2), el cambio que pasa (lección 3), el cambio que el gate detiene (lección 4), la honestidad sobre EKS (lección 5), la corrección de continuidad (lección 6), y el mapa hacia el resto del ecosistema (lección 7). Este proyecto reúne las seis en un solo entregable, y termina esta guía.


El repositorio completo, verificado hoy

andes-cargo-k8s/ — diez manifiestos, sincronizados por ArgoCD

cd andes-cargo-k8s && ls
git log --oneline

Qué esperar (literal, ejecutado — tu historial de commits crece según cuántas lecciones de esta guía completaste; el mínimo son los siete commits de los Módulos 5 y 8):

application.yaml
configmap.yaml
deployment.yaml
hpa.yaml
ingress.yaml
namespace.yaml
networkpolicy-allow-ingress-nginx.yaml
networkpolicy-default-deny.yaml
secret.yaml
service.yaml

dcdd4b3 Revert "Temporarily remove resource limits from andes-cargo-status-api to debug OOM"
229cd7b Temporarily remove resource limits from andes-cargo-status-api to debug OOM
d0b98c6 Add tier=backend label to the andes-cargo-status-api pod template
db09bbe Ignore spec.replicas on the Deployment: the HorizontalPodAutoscaler owns it, not Git
cd0b5ca Scale andes-cargo-status-api from 3 to 5 replicas
820515f Add ArgoCD Application pointing at this same repository
45b14f6 Initial GitOps source: namespace, deployment, service, config, hpa, ingress, networkpolicy (inherited from M1-M4)

Siete commits, y ninguno de ellos es un kubectl apply disfrazado — cada uno es un cambio real, con su propia historia: la carga inicial heredada de M1-M4, el Application autorreferencial de ArgoCD (M5), un escalado de negocio real (M5), la corrección de la tensión HPA/GitOps (M5), y los dos commits de este mismo módulo — uno que pasó el gate, uno que el gate detuvo y su reversión.

gatekeeper/, kyverno/, kind-config.yaml — la infraestructura de guardrails, fuera del repositorio sincronizado

ls gatekeeper/ kyverno/
cat kind-config.yaml

Qué esperar:

gatekeeper/:
constraint-andes-cargo-required-resources.yaml
constraint-template-required-resources.yaml

kyverno/:
policy-andes-cargo-require-resources.yaml

# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: andes-cargo-cluster
nodes:
  - role: control-plane
  - role: worker
  - role: worker

Estos tres archivos no viven dentro de andes-cargo-k8s/, y esa separación es intencional, explicada desde el Módulo 6: un ConstraintTemplate/Constraint de Gatekeeper y una ClusterPolicy de Kyverno son infraestructura del clúster mismo —se aplican una vez con kubectl apply directo, no cambian con cada git push de andes-cargo-status-api—, exactamente igual que kind-config.yaml describe la forma del clúster, no el contenido que corre adentro. Mezclarlos en el mismo repositorio que ArgoCD sincroniza confundiría dos capas de responsabilidad distintas.

scripts/scan-and-load.sh — el gate de imagen del Módulo 6

cat scripts/scan-and-load.sh

Qué esperar (el mismo script del Módulo 6, lección 8, sin ningún cambio):

#!/usr/bin/env bash
# scan-and-load.sh — trivy image gate before kind load docker-image
# Usage: ./scan-and-load.sh <image:tag>
set -euo pipefail

IMAGE="$1"
CLUSTER="andes-cargo-cluster"

echo "==> scanning ${IMAGE} for CRITICAL vulnerabilities before loading into ${CLUSTER}"

if trivy image --exit-code 1 --severity CRITICAL --quiet "${IMAGE}"; then
  echo "==> gate PASSED — no CRITICAL findings, loading ${IMAGE} into ${CLUSTER}"
  kind load docker-image "${IMAGE}" --name "${CLUSTER}"
  echo "==> ${IMAGE} loaded"
else
  echo "==> gate FAILED — ${IMAGE} has CRITICAL findings, NOT loaded into ${CLUSTER}"
  exit 1
fi

Once archivos en total —diez manifiestos sincronizados, dos piezas de Gatekeeper, una de Kyverno, kind-config.yaml, y este script— son el entregable completo de esta guía.


El checklist de portfolio: qué corrió de verdad, verificado una última vez

kind get clusters
kubectl get nodes
kubectl get application -n argocd
kubectl get k8srequiredresources
kubectl get clusterpolicy
kubectl get pods -n andes-cargo -l app=andes-cargo-status-api
curl -i -s --max-time 8 --resolve andes-cargo.local:80:127.0.0.1 http://andes-cargo.local/health

Qué esperar (literal, ejecutado — la auditoría final completa de esta guía, en un solo bloque):

andes-cargo-cluster

NAME                                STATUS   ROLES           AGE    VERSION
andes-cargo-cluster-control-plane   Ready    control-plane   3h5m   v1.36.1
andes-cargo-cluster-worker          Ready    <none>          3h5m   v1.36.1
andes-cargo-cluster-worker2         Ready    <none>          3h5m   v1.36.1

NAME                     SYNC STATUS   HEALTH STATUS
andes-cargo-status-api   Synced        Healthy

NAME                                    ENFORCEMENT-ACTION   TOTAL-VIOLATIONS
andes-cargo-must-have-resource-limits   deny                 0

NAME                                  ADMISSION   BACKGROUND   READY   AGE   MESSAGE
andes-cargo-require-resource-limits   true        true         True    62m   Ready

NAME                                      READY   STATUS    RESTARTS   AGE
andes-cargo-status-api-669755d655-np5mt   1/1     Running   0          12m
andes-cargo-status-api-669755d655-qhgwx   1/1     Running   0          12m

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 51

{"service":"andes-cargo-status-api","status":"ok"}

Un clúster de tres nodos, sano; un Application de ArgoCD sincronizado y saludable; ambos motores de admission control activos y sin violaciones; los Pods de producción respondiendo tráfico real. Este es el estado final de once semanas de contenido, confirmado con comandos reales, no una promesa.


El checklist de honestidad: EJECUTADO frente a REPRESENTATIVO, módulo por módulo

MóduloPiezaEstadoEvidencia
1kind + kubectl, el clúster, la imagen cargadaEjecutadokind get clusters, crictl images en los tres nodos (M1.7/M1.8)
2Pod, Deployment, ReplicaSet, ServiceEjecutadokubectl get all -n andes-cargo (M2.8)
3ConfigMap, Secret, tres probes, HorizontalPodAutoscalerEjecutadoFallo de readiness/liveness inducido y observado; HPA escalando bajo carga real (M3.6/M3.8)
4ingress-nginx, Ingress, dos NetworkPolicyEjecutadocurl real vía Ingress; tráfico bloqueado/permitido con NetworkPolicy (M4.8)
5Gitea, ArgoCD, GitOps pull-basedEjecutadogit push sin kubectl apply, convergencia medida con timestamps reales (M5.8)
6Gatekeeper, Kyverno, trivy imageEjecutadoRechazo real con mensaje literal de ambos motores; gate de imagen fallando contra la imagen real (M6.8)
7Plano de control gestionado, node groups, IRSA, ALB Controller, autoscaling de nodosRepresentativoRazón de costo exacta declarada por cada pieza (M7.1-M7.8, M8.5 de este módulo)
8Cambio que pasa el gate, cambio que el gate detieneEjecutadoEsta guía completa — M8.3/M8.4, con el hallazgo real sobre autogen de Kyverno

Seis de ocho módulos, ejecutados de punta a punta contra un clúster real. Uno, representativo, con la razón exacta declarada en cada pieza. Uno —este— integra ambos.


Cómo defender este entregable en una entrevista

Tres preguntas que cualquier entrevistador técnico haría frente a este repositorio, con la respuesta exacta que este proyecto ya construyó:

"¿Cómo sé que esto no es solo YAML que copiaste de un tutorial?" — El historial de git log de andes-cargo-k8s/ tiene siete commits reales, con mensajes que describen decisiones de negocio (escalar réplicas, corregir una tensión entre GitOps y autoscaling, revertir un cambio peligroso) — no un solo commit "initial setup". Y el hallazgo de la lección 4 de este módulo (Kyverno capturando un Deployment que Gatekeeper no cubría) es, literalmente, imposible de obtener copiando un tutorial: es el tipo de comportamiento que solo aparece al correr dos motores de políticas reales, juntos, contra un caso real.

"¿Qué harías distinto en un clúster EKS real?" — La respuesta está completa en la lección 5 de este módulo, y en el Módulo 7, lección 8: cinco de diez manifiestos de andes-cargo-k8s/ no cambiarían ni una línea; los otros cinco cambian en campos específicos y nombrables (imagen, serviceAccountName, una clave de ConfigMap, ingressClassName, y el tipo de selector de una NetworkPolicy) — nunca "todo sería distinto".

"¿Qué es lo que no probaste?" — La respuesta honesta, sin evasivas, está en la lección 5 de este módulo: el plano de control gestionado, node groups reales, autoscaling de nodos, IRSA/EKS Pod Identity, y el AWS Load Balancer Controller — cada uno con la razón de costo exacta, no una generalidad.


Analogía: la carpeta de trabajo, cerrada y sellada

Si cada módulo de esta guía fue una estación de la fábrica automatizada de la lección 1, este proyecto es la carpeta que un auditor sella al final de una inspección completa: no contiene ninguna promesa sin verificar, ni ningún "esto debería funcionar" — cada página tiene una firma, un timestamp, y el comando exacto que produjo el resultado. Un entrevistador que abra esta carpeta no encuentra un plano bonito de una fábrica que nunca se construyó — encuentra el registro de una fábrica que corrió, con dos incidentes reales documentados (la tensión HPA/GitOps del Módulo 5, la asimetría Gatekeeper/Kyverno de este módulo) y su resolución, exactamente como quedaría el archivo de un equipo de plataforma real después de una semana ocupada.


Errores comunes

Presentar este entregable sin el historial de git log (de omisión de la evidencia más fuerte). Qué pasa: alguien, al mostrar andes-cargo-k8s/ como pieza de portafolio, comparte solo el estado final de los archivos, sin el historial de commits. Cómo detectarlo: si tu versión del repositorio para mostrar a otros es una carpeta sin .git/, o un git log con un solo commit "initial commit". Cómo corregirlo: el historial completo, con sus siete commits reales y sus mensajes, es exactamente la evidencia que responde la primera pregunta de la sección anterior — sin él, el repositorio se ve igual de genérico que cualquier tutorial copiado.

No poder explicar el hallazgo de autogen sin volver a leer la lección 4 (de preparación incompleta para una entrevista). Qué pasa: alguien memoriza que "Gatekeeper y Kyverno son distintos" sin poder explicar la razón técnica específica (match.kinds de Gatekeeper frente a background: true/autogen de Kyverno). Cómo detectarlo: si, al intentar explicar el hallazgo de la lección 4 a un colega, tu explicación se queda en "a veces uno detecta cosas que el otro no". Cómo corregirlo: repasa la lección 4 hasta poder explicar, sin ayuda, exactamente por qué Gatekeeper no evaluó el Deployment (su Constraint nunca declaró Deployment en match.kinds) y por qué Kyverno sí (su regla background: true generó automáticamente una regla equivalente, con el prefijo autogen-, para controladores de Pods).

Tratar "6 de 8 módulos ejecutados" como una debilidad a esconder (de framing defensivo innecesario). Qué pasa: alguien, al presentar este entregable, evita mencionar que el Módulo 7 es representativo, con la esperanza de que nadie pregunte. Cómo detectarlo: si tu plan frente a la pregunta "¿esto corrió en EKS real?" es cambiar de tema. Cómo corregirlo: la honestidad sobre qué es representativo y por qué —con razones técnicas exactas, no genéricas— es, en sí misma, una señal de madurez profesional que un entrevistador técnico valora más que una afirmación vaga de "sí, todo corrió en producción". La tabla de esta lección existe exactamente para responder esa pregunta con confianza, no para evitarla.


Ejercicios

Ejercicio 1 — Reconstruye el árbol de once archivos de memoria. Sin volver a esta lección, enumera los once archivos que componen el entregable completo de esta guía, agrupados en sus cuatro categorías (manifiestos sincronizados, Gatekeeper, Kyverno, infraestructura de clúster/script).

Ver solución

Sincronizados por ArgoCD (10): namespace.yaml, deployment.yaml, service.yaml, configmap.yaml, secret.yaml, hpa.yaml, ingress.yaml, networkpolicy-default-deny.yaml, networkpolicy-allow-ingress-nginx.yaml, application.yaml. Gatekeeper (2): constraint-template-required-resources.yaml, constraint-andes-cargo-required-resources.yaml. Kyverno (1): policy-andes-cargo-require-resources.yaml. Infraestructura de clúster y automatización (2, fuera de las categorías anteriores): kind-config.yaml, scripts/scan-and-load.sh. Total: diez más cuatro = catorce si cuentas cada pieza individual — pero, agrupando gatekeeper/, kyverno/, kind-config.yaml y scripts/ como "las piezas fuera del repositorio sincronizado", el entregable completo son once archivos de contenido real.

Ejercicio 2 — Practica la respuesta a las tres preguntas de entrevista, en voz alta. Sin mirar la sección "Cómo defender este entregable", responde en voz alta, con tus propias palabras, las tres preguntas de esa sección: ¿cómo sabes que no es un tutorial copiado?, ¿qué cambiarías en EKS real?, ¿qué no probaste?

Ver solución

No hay una única respuesta correcta — la prueba real es si tu respuesta, sin mirar el texto, incluye evidencia específica (el historial de git log con mensajes de negocio reales, el hallazgo de autogen como algo que no aparece en tutoriales genéricos, la tabla de cinco archivos que cambian frente a cinco que no al migrar a EKS, y la lista de cinco piezas representativas con su razón de costo exacta). Si tu respuesta a cualquiera de las tres preguntas fue vaga o genérica, vuelve a la lección correspondiente (4, 5, o el Módulo 7, lección 8) antes de dar esta guía por terminada.

Ejercicio 3 — Diseña tu propia extensión honesta de este entregable. Basándote en la lección 7 de este módulo, elige uno de los tres huecos (FinOps de Kubernetes, SRE del clúster, GPU/EKS) y describe, en dos o tres frases, cuál sería el primer archivo concreto que agregarías a andes-cargo-k8s/ si decidieras cerrar ese hueco por tu cuenta.

Ver solución

Una respuesta razonable, para el hueco de SRE: "Agregaría un poddisruptionbudget.yaml, con minAvailable: 1 sobre el Deployment andes-cargo-status-api — el objeto que ninguna lección de esta guía construyó, y que un ejercicio real de SRE (como el que describe sre-and-incident-response-guide) identificaría como el primer guardrail faltante antes de cualquier operación de mantenimiento planeada sobre los nodos del clúster." Cualquier respuesta que nombre un archivo concreto, con un campo específico y una razón conectada a la disciplina elegida, demuestra que la lección 7 se entendió como un mapa de trabajo real, no como una lista de nombres de guías.


Resumen y siguiente paso

Este proyecto cerró la guía completa con el entregable final: andes-cargo-k8s/ (diez manifiestos, siete commits reales), gatekeeper//kyverno/ (los guardrails de runtime), kind-config.yaml (la forma del clúster), y scripts/scan-and-load.sh (el gate de imagen) — once piezas de contenido real, verificadas una última vez contra andes-cargo-cluster sano, con Application: Synced/Healthy, ambos motores de políticas activos sin violaciones, y andes-cargo-status-api respondiendo tráfico real. La tabla de honestidad, módulo por módulo, deja el balance final de esta guía en un solo lugar: seis de ocho módulos ejecutados de punta a punta, uno representativo con razón exacta, y este capstone integrando ambos con dos hallazgos reales no planeados (la tensión HPA/GitOps del Módulo 5, la asimetría Gatekeeper/Kyverno de este módulo).

andes-cargo-status-api —el componente que aws-serverless-and-containers-guide dejó "documentado, nunca ejecutado" en ECS— termina esta guía corriendo de verdad, en un clúster Kubernetes real, con GitOps pull-based real y admission control real encima. Esa es, en una frase, la promesa completa que esta guía hizo desde su Módulo 1, lección 1, y que este proyecto final confirma con evidencia, no con una afirmación.

No hay siguiente lección. Esta es la última lección de kubernetes-and-eks-in-production-guide. El mapa hacia adelante —FinOps de Kubernetes, SRE del clúster, GenAI/GPU sobre EKS— quedó en la lección 7 de este mismo módulo, con las guías reales que ya existen para continuar.

Recursos

  1. Kubernetes — Production Environment — referencia oficial sobre las consideraciones de producción que esta guía completa, con kind como laboratorio, preparó para transferir sin traducción a un clúster real.
  2. Argo CD — Best Practices — prácticas oficiales de GitOps, la mayoría ya aplicadas en andes-cargo-k8s/ a lo largo de esta guía.
  3. kubernetes-and-eks-in-production-guide (NIEVA), Módulos 1-8 — la fuente completa de cada pieza de este entregable final.
  4. aws-serverless-and-containers-guide (NIEVA), Módulo 8 — el punto exacto del ecosistema donde andes-cargo-status-api quedó "documentado, nunca ejecutado", y que esta guía completa cerró con evidencia real.