Módulo 4: Networking Ingress And Networkpolicy
8. Proyecto: `status-api-service` expuesto y protegido
Descripción
Este proyecto cierra el Módulo 4 con la prueba que las siete lecciones anteriores prepararon: Ingress (lecciones 2-5) y NetworkPolicy (lecciones 6-7) funcionando juntos, verificados en la misma sesión, uno junto al otro. No hay ningún manifiesto nuevo en este proyecto — cada pieza ya existe, aplicada en su propia lección. Lo que este proyecto agrega es la vista completa: el camino que debería funcionar, funcionando; el camino que debería estar bloqueado, bloqueado; y un checklist que confirma que ninguna de las dos cosas es casualidad. Todo lo que sigue corrió contra andes-cargo-cluster.
Conexión con el módulo
Cada lección de este módulo construyó una pieza: el modelo de red (lección 2), Ingress explicado y construido (lecciones 3-5), NetworkPolicy explicado y construido (lecciones 6-7). Este proyecto es la primera vez que ves las dos mitades del módulo —"tráfico entrante" y "quién puede generarlo"— confirmadas en el mismo lugar, cerrando el arco que la lección 1 abrió: de "solo kubectl port-forward" a un servicio con una puerta HTTP permanente y un guardrail de red real.
Paso 1 — Confirma el estado completo antes de verificar nada
kubectl get all,ingress,networkpolicy,configmap,secret -n andes-cargo
Qué esperar (nombres de Pod, IPs, NodePort de hpa y AGE son tus valores variables — el resto, incluidos los nombres de cada objeto y el patrón de tres réplicas, es literal):
NAME READY STATUS RESTARTS AGE
pod/andes-cargo-status-api-548966dd97-bpqjq 1/1 Running 0 5m56s
pod/andes-cargo-status-api-548966dd97-jwx5r 1/1 Running 0 5m56s
pod/andes-cargo-status-api-548966dd97-ls2bc 1/1 Running 0 5m56s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/status-api-service ClusterIP 10.96.239.125 <none> 80/TCP 5m56s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/andes-cargo-status-api 3/3 3 3 5m56s
NAME DESIRED CURRENT READY AGE
replicaset.apps/andes-cargo-status-api-548966dd97 3 3 3 5m56s
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
horizontalpodautoscaler.autoscaling/andes-cargo-status-api-hpa Deployment/andes-cargo-status-api cpu: <unknown>/50% 2 6 3 5m56s
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/status-api-ingress nginx andes-cargo.local localhost 80 4m53s
NAME POD-SELECTOR AGE
networkpolicy.networking.k8s.io/allow-from-ingress-nginx app=andes-cargo-status-api 53s
networkpolicy.networking.k8s.io/default-deny-ingress <none> 3m24s
NAME DATA AGE
configmap/andes-cargo-status-api-config 3 5m56s
configmap/kube-root-ca.crt 1 5m57s
NAME TYPE DATA AGE
secret/andes-cargo-status-api-secrets Opaque 2 5m56s
HorizontalPodAutoscalermuestracpu: <unknown>/50%porque este clúster se recreó en la lección 4 ymetrics-server(Módulo 3, lección 7) todavía no se reinstaló — elHPAen sí sigue configurado y funcionando, solo sin métricas que leer todavía. Esto no afecta nada de lo que este proyecto verifica;IngressyNetworkPolicyno dependen demetrics-serveren absoluto.
Nueve tipos de objeto, un solo namespace: el Deployment/Service/HPA del Módulo 2-3, el ConfigMap/Secret del Módulo 3, y el Ingress/dos NetworkPolicy de este módulo — el estado completo y acumulativo de esta guía hasta hoy.
Paso 2 — Verifica el camino permitido: Ingress → Service
curl -i -s --max-time 8 --resolve andes-cargo.local:80:127.0.0.1 http://andes-cargo.local/health
Qué esperar (literal, ejecutado — Date es tu valor variable):
HTTP/1.1 200 OK
Date: Fri, 14 Aug 2026 20:42:41 GMT
Content-Type: application/json
Content-Length: 51
Connection: keep-alive
{"service":"andes-cargo-status-api","status":"ok"}
200, sin ningún kubectl port-forward, exactamente como confirmó la lección 5 — y ahora, además, con default-deny-ingress y allow-from-ingress-nginx activas al mismo tiempo. Este resultado es la prueba de que las dos mitades del módulo conviven sin conflicto: la puerta HTTP sigue abierta para el tráfico legítimo, aunque el guardrail de red esté activo.
Confirma también, una vez más y con la misma honestidad de siempre, el límite que este módulo nunca prometió resolver:
curl -i -s --max-time 30 --resolve andes-cargo.local:80:127.0.0.1 http://andes-cargo.local/shipments/4471
Qué esperar (literal, ejecutado — sin cambios frente a la lección 5; sigue sin ser un error de este proyecto):
HTTP/1.1 500 INTERNAL SERVER ERROR
Date: Fri, 14 Aug 2026 20:43:24 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 265
Connection: keep-alive
<!doctype html>
<html lang=en>
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
Paso 3 — Verifica el camino bloqueado: cualquier otro Pod → Service directo
kubectl exec -n andes-cargo test-client -- curl -s -o /dev/null -w "HTTP_STATUS:%{http_code}\n" --max-time 8 http://status-api-service.andes-cargo.svc.cluster.local/health
Qué esperar (literal, ejecutado — el mismo test-client de la lección 7, todavía corriendo):
HTTP_STATUS:000
command terminated with exit code 28
Bloqueado — de verdad, con el mismo mecanismo que investigó a fondo la lección 7. Ningún Pod que no venga del namespace ingress-nginx puede alcanzar status-api-service, sin importar que esté en el mismo namespace que Andes Cargo, sin importar que use el nombre DNS correcto, sin importar nada — salvo la única excepción explícita que declaraste.
El resumen visual: los dos caminos, uno junto al otro
EL ESTADO FINAL DEL MÓDULO 4, VERIFICADO
Cliente externo (curl, tu máquina)
│
│ --resolve andes-cargo.local:80:127.0.0.1
▼
┌─────────────────┐
│ Ingress │ status-api-ingress
│ (extraPortMappings │ host: andes-cargo.local
│ → control-plane) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ ingress-nginx │ namespace: ingress-nginx
│ controller │
└────────┬────────┘
│ permitido por allow-from-ingress-nginx
▼
┌─────────────────┐ test-client
│ status-api- │◄── X ── (namespace: andes-cargo,
│ service │ BLOQUEADO sin pasar por Ingress)
│ (andes-cargo- │ por default-deny-ingress
│ status-api Pods) │
└─────────────────┘
/health → 200 OK (ambos caminos verificados)
/shipments/<id> → 500 (backend DynamoDB fuera del alcance $0 —
representativo en toda esta guía)
Checklist final del Módulo 4
Tres verificaciones, todas confirmadas por este proyecto, ninguna hipotética:
-
ingress-nginxinstalado y sano, corriendo enandes-cargo-cluster-control-plane(el único nodo conextraPortMappings), confirmado conkubectl get pods -n ingress-nginx. -
status-api-ingressenruta tráfico real, sinkubectl port-forward, confirmado concurlcontrahttp://andes-cargo.local/healthrespondiendo200. -
default-deny-ingress+allow-from-ingress-nginxbloquean y permiten exactamente lo esperado, verificado en ambas direcciones: el camino deIngressfunciona, cualquier otro Pod queda bloqueado — con la corrección honesta de la lección 7 sobrekindnetddocumentada y citada.
Nota para el Módulo 5: qué hereda GitOps de este estado
El Módulo 5 va a tomar namespace.yaml, deployment.yaml, service.yaml, configmap.yaml, secret.yaml, hpa.yaml, ingress.yaml y las dos NetworkPolicy de este módulo, y entregárselos a ArgoCD como la fuente de verdad de un repositorio Git — el mismo patrón acumulativo que ya viste en cada módulo anterior, ahora con ocho manifiestos en vez de seis. Dos cosas valen la pena anotar antes de llegar ahí:
- Un backend real de DynamoDB (LocalStack u otra opción), si algún equipo lo agregara, no chocaría con
default-deny-ingress. La política de esta lección tienemetadata.namespace: andes-cargo— solo afecta a Pods de ese namespace. LocalStack viviría en su propio namespace (localstack), completamente fuera del alcance de estaNetworkPolicy. Esta guía no instala esa pieza en ningún módulo — la capa de datos queda fuera de su alcance $0 — pero vale la pena dejar anotado que la arquitectura de red de este módulo no le pondría ningún obstáculo. andes-cargo-status-apipodría alcanzar ese backend sin ningún cambio a las políticas de este módulo. AmbasNetworkPolicyde este módulo declaran únicamentepolicyTypes: [Ingress]—confirmado explícitamente en elkubectl describede la lección 7 (Not affecting egress traffic)—, así que la conexión saliente deandes-cargo-status-apihacia cualquier servicio externo, si existiera, no encontraría ninguna restricción nueva de este módulo.
Limpieza: retira el Pod de prueba
test-client cumplió su propósito (lecciones 7 y 8) y no es parte del estado permanente del laboratorio — a diferencia de andes-cargo-status-api, ingress-nginx, o las dos NetworkPolicy, que sí siguen siendo necesarios para el resto de la guía:
kubectl delete pod test-client -n andes-cargo
Qué esperar:
pod "test-client" deleted
Errores comunes
Dar por cerrado el módulo sin correr las verificaciones de este proyecto, confiando en que "cada lección ya funcionó por separado" (de flujo, el mismo patrón que ya viste en el Módulo 1). Qué pasa: alguien asume que, porque las lecciones 5 y 7 ya mostraron resultados correctos por separado, no hace falta confirmar que ambas piezas siguen funcionando juntas. Cómo detectarlo: si no puedes pegar, ahora mismo, la salida real de los Pasos 2 y 3 de este proyecto. Cómo corregirlo: la razón de este proyecto es exactamente esa — confirmar que Ingress y NetworkPolicy, aplicados en lecciones distintas, no interfieren entre sí. No es redundante repetir la verificación con ambos activos al mismo tiempo.
Borrar default-deny-ingress o allow-from-ingress-nginx "para simplificar" antes de seguir con el Módulo 5 (operativo, con consecuencias reales para la continuidad de la guía). Qué pasa: alguien, pensando que las NetworkPolicy ya cumplieron su propósito pedagógico en este módulo, las borra antes de avanzar. Cómo detectarlo: kubectl get networkpolicy -n andes-cargo no muestra ninguno de los dos objetos. Cómo corregirlo: ambas políticas son parte del estado permanente del laboratorio de aquí en adelante — el Módulo 5 las hereda tal cual, como parte del repositorio Git que ArgoCD sincroniza. Si las borraste, vuelve a aplicar networkpolicy-default-deny.yaml y networkpolicy-allow-ingress-nginx.yaml (lección 7) antes de continuar.
No notar que el HPA muestra <unknown> después de recrear el clúster, y asumir que algo se rompió (de continuidad, ya explicado en el Paso 1 pero fácil de alarmar si lo ves sin contexto). Qué pasa: alguien ve cpu: <unknown>/50% en kubectl get hpa y se preocupa de que el autoscaling dejó de funcionar. Cómo detectarlo: si comparas contra el resultado del Módulo 3, donde sí aparecía un porcentaje real. Cómo corregirlo: la recreación del clúster en la lección 4 de este módulo no reinstaló metrics-server —fuera del alcance de este módulo—; el HPA en sí sigue configurado correctamente y va a volver a mostrar métricas reales en cuanto metrics-server esté disponible de nuevo. Ningún módulo posterior de esta guía depende de esto.
Ejercicios
Ejercicio 1 — Reconstruye el diagrama del estado final de memoria. Sin volver a la lección, dibuja (en texto, con flechas) el camino completo desde curl en tu máquina hasta un Pod de andes-cargo-status-api, marcando en qué punto interviene cada pieza de este módulo (Ingress, ingress-nginx, NetworkPolicy).
Ver solución
curl (tu máquina) → extraPortMappings (kind-config.yaml, hacia control-plane) → ingress-nginx (lee la regla de Ingress) → status-api-service → NetworkPolicy evalúa el origen (namespace ingress-nginx, permitido por allow-from-ingress-nginx) → Pod de andes-cargo-status-api. Un Pod que intente el mismo camino saltándose ingress-nginx llega hasta el paso de NetworkPolicy, y ahí se detiene — default-deny-ingress bloquea cualquier origen que no coincida con la excepción explícita.
Ejercicio 2 — Explica por qué un backend de datos externo no necesitaría ningún cambio a estas políticas. Un colega pregunta si, en caso de conectar andes-cargo-status-api a un backend real de DynamoDB (vía LocalStack o una cuenta AWS real — algo que esta guía no hace, pero que un equipo real podría agregar), habría que modificar default-deny-ingress o allow-from-ingress-nginx. Explícale, en dos o tres frases, por qué no.
Ver solución
Una explicación razonable: "Ninguna de las dos políticas toca el tráfico saliente — ambas declaran solo policyTypes: [Ingress], confirmado explícitamente con kubectl describe en la lección 7 (Not affecting egress traffic). La conexión hacia un backend externo es una conexión que andes-cargo-status-api inicia hacia afuera, no una que recibe — ese tipo de tráfico está completamente fuera del alcance de lo que este módulo restringió."
Ejercicio 3 — Diseña la verificación para un tercer camino. Si Andes Cargo agregara, en el futuro, un tercer namespace (monitoring, con una herramienta de observabilidad que necesita hacerle curl a /health para verificar que el servicio está vivo), ¿qué política nueva necesitarías, y qué verificación —siguiendo el patrón de este proyecto— confirmaría que funciona sin abrir la puerta a nada más?
Ver solución
Necesitarías una tercera NetworkPolicy (o una regla adicional dentro de allow-from-ingress-nginx, aunque separarla en un objeto propio es más claro) con podSelector: app: andes-cargo-status-api y una regla from con namespaceSelector: kubernetes.io/metadata.name: monitoring, restringida al puerto 8080 — el mismo patrón exacto que allow-from-ingress-nginx, con el namespace de origen cambiado. La verificación seguiría el mismo patrón de dos pasos de este proyecto: un Pod de prueba dentro de monitoring confirmando 200 contra status-api-service, y test-client (o cualquier Pod fuera de ingress-nginx/monitoring) confirmando que sigue bloqueado — la prueba de que la excepción nueva no abrió la puerta más de lo necesario.
Resumen y siguiente paso
Este proyecto confirmó, con evidencia real y ambas mitades activas al mismo tiempo, el estado completo del Módulo 4: Ingress enrutando tráfico externo real hacia status-api-service (200 en /health, sin ningún kubectl port-forward), y NetworkPolicy bloqueando por completo cualquier camino que se salte esa puerta (test-client, bloqueado con evidencia literal), mientras el camino legítimo sigue funcionando sin fricción. andes-cargo-status-api es, desde hoy, un servicio con una puerta HTTP permanente y un guardrail de red real — las dos piezas que "3 réplicas balanceadas" (Módulo 2) y "configuración/salud/autoscaling" (Módulo 3) todavía no cubrían.
Antes de avanzar deberías poder: ejecutar el checklist completo de memoria; explicar por qué un backend de datos externo, si algún equipo lo agregara, no necesitaría ningún cambio a las NetworkPolicy de este módulo; y describir, con el diagrama de esta lección, el camino completo de una solicitud HTTP real desde tu máquina hasta un Pod, incluidas las dos piezas de seguridad de red que la protegen.
Siguiente módulo: GitOps con ArgoCD. El Módulo 5 toma exactamente los ocho manifiestos que existen hoy —namespace.yaml, deployment.yaml, service.yaml, configmap.yaml, secret.yaml, hpa.yaml, ingress.yaml, y las dos NetworkPolicy— y los convierte en la fuente de verdad de un repositorio Git real, servido por Gitea dentro del propio clúster, sincronizado por un operador que hace watch en vez de esperar a que alguien corra kubectl apply a mano.
Recursos
kubernetes-and-eks-in-production-guide(NIEVA), Módulo 4, lecciones 4, 5 y 7 — el origen exacto de cada manifiesto y cada verificación que este proyecto reutiliza sin cambios.- Kubernetes — Debug Services — guía oficial de diagnóstico de conectividad, útil como referencia general para cualquier problema de red que encuentres más allá del alcance de esta guía.
- Kubernetes — Ingress y Kubernetes — Network Policies — las dos referencias centrales de todo este módulo, ambas confirmadas con evidencia real en este proyecto.