Módulo 2: Pods Deployments And Services
1. Introducción: de un contenedor a una carga de trabajo administrada
Descripción
El Módulo 1 te dejó con un clúster real, sano, con la imagen andes-cargo-status-api:latest disponible en el containerd interno de sus tres nodos — y ningún Pod corriendo todavía. Ese es exactamente el punto donde termina lo que docker run te puede dar solo, y donde empieza este módulo: la primera vez, en toda esta guía, que un objeto de Kubernetes ejecuta de verdad el servicio de Andes Cargo. Vas a construir, en orden, las tres primitivas que sostienen absolutamente todo lo demás que aparece en los seis módulos que siguen — Ingress, NetworkPolicy, ArgoCD, Gatekeeper, hasta lo específico de EKS — porque ninguna de esas piezas tiene sentido sin un Pod corriendo detrás.
Conexión con el módulo
Este módulo tiene una progresión deliberada, de lo más simple a lo más cercano a producción: primero entiendes qué es un Pod y por qué casi nadie crea uno suelto (lecciones 2 y 4); después entiendes por qué, en la práctica, casi todo el mundo usa un Deployment en su lugar (lecciones 3 y 5); y por último resuelves el problema que ni un Pod ni un Deployment resuelven por sí solos —cómo le hablas a un grupo de Pods que van y vienen sin que la dirección a la que apuntas cambie nunca— con un Service (lecciones 6 y 7). La lección 8, el proyecto, junta las tres piezas en un solo sistema: andes-cargo-status-api corriendo con varias réplicas, expuesto por un Service con nombre estable, sobreviviendo a que borres un Pod a propósito.
Qué le falta a docker run que Kubernetes resuelve
Ya corriste andes-cargo-status-api con docker run en aws-serverless-and-containers-guide (Módulo 6). Funcionó — el contenedor arrancó, respondió a curl, todo bien. La pregunta de este módulo es otra: ¿qué pasa después? Cuatro escenarios, cada uno con la respuesta real de docker run solo frente a la de Kubernetes:
| Escenario | docker run solo | Kubernetes (lo que este módulo construye) |
|---|---|---|
El proceso dentro del contenedor muere (excepción no controlada, OOMKilled) | El contenedor se detiene. Nadie lo reinicia a menos que hayas agregado --restart=always a mano, y nadie audita si esa bandera está en todos los docker run de tu flota | El kubelet del nodo detecta el contenedor caído y lo reinicia según la restartPolicy del Pod — por defecto, siempre. Sin que nadie lo pida en el momento |
| Necesitas tres instancias corriendo al mismo tiempo, no una | Corres docker run tres veces a mano, con tres nombres distintos, y llevas la cuenta tú mismo de cuáles siguen vivas | Declaras replicas: 3 una sola vez; un controlador dentro del clúster mantiene ese número exacto, siempre, sin que nadie lo revise a mano |
| Una de esas tres instancias se cae | Alguien (una persona, un script externo) tiene que notar la caída y correr docker run de nuevo | El mismo controlador que mantiene el número de réplicas nota la diferencia entre "3 deseadas" y "2 corriendo", y crea una cuarta sin que nadie intervenga — es, literalmente, el tema completo de la lección 3 |
| Necesitas una sola dirección estable para hablarle a cualquiera de esas tres instancias, sin que te importe cuál responde | Tú armas tu propio mecanismo — un balanceador externo, una lista de IPs que mantienes a mano, algo — porque cada contenedor tiene su propia IP, y esa IP cambia cada vez que lo recreas | Un Service te da un nombre y una dirección que nunca cambian, aunque los Pods detrás roten constantemente — el tema completo de las lecciones 6 y 7 |
Ninguna de las columnas de la derecha es magia. Cada una es un controlador corriendo dentro del propio clúster, comparando constantemente "lo que declaraste" contra "lo que existe de verdad", y actuando solo cuando hay una diferencia — el mismo patrón, sin excepción, que vas a ver una y otra vez en el resto de esta guía (GitOps con ArgoCD en el Módulo 5 es exactamente este mismo patrón, aplicado a todo un repositorio Git en vez de a un número de réplicas).
El mapa de este módulo: las 8 lecciones
| # | Lección | Qué construyes |
|---|---|---|
| 1 | Introducción (esta) | Qué le falta a docker run, el mapa del módulo |
| 2 | Qué es un Pod, y por qué casi nunca se crea uno suelto | El concepto: la unidad mínima programable de Kubernetes |
| 3 | Deployments: ReplicaSets y el bucle de control declarativo | El concepto: estado deseado vs. imperativo, quién reconcilia |
| 4 | Manos a la obra: tu primer Pod | Ejecutado: kubectl run/kubectl apply -f pod.yaml, describe, delete |
| 5 | Manos a la obra: el Deployment de andes-cargo-status-api | Ejecutado: el Deployment real corriendo, un Pod borrado y repuesto solo |
| 6 | Services: ClusterIP, NodePort, y direcciones estables | El concepto: el problema de las IPs efímeras, los tipos de Service |
| 7 | Manos a la obra: exponiendo status-api-service | Ejecutado: port-forward, curl /health real contra el Service |
| 8 | Proyecto: andes-cargo-status-api con N réplicas | Ejecutado: 3 réplicas, balanceo confirmado, un Pod repuesto solo |
La analogía de este módulo: un edificio de oficinas, no un escritorio suelto
Vas a ver esta misma analogía desarrollarse en las próximas lecciones, así que vale la pena dejarla planteada desde ahora. Piensa en docker run como rentar un escritorio suelto en un coworking sin ningún tipo de administración: si te vas, nadie nota que el escritorio quedó vacío; si necesitas tres escritorios, los rentas tú mismo, uno por uno; y si alguien pregunta "¿dónde te siento hoy?", la respuesta cambia cada vez que te mudas de escritorio.
Un edificio de oficinas real, en cambio, tiene sistemas que resuelven exactamente esos tres problemas sin que nadie los opere a mano: un termostato que mantiene la temperatura del edificio en el rango que alguien configuró una sola vez, sin que nadie tenga que ajustarlo cada vez que la puerta se abre y se cierra — eso es un Deployment (lección 3); y una centralita telefónica con un número de extensión fijo, que enruta la llamada a quien esté disponible detrás de esa extensión en ese momento, sin que quien llama necesite saber quién responde hoy — eso es un Service (lección 6). Un Pod, en esta misma analogía, es la oficina individual en sí: tiene todo lo que necesita adentro (escritorio, silla, línea de red), pero es el edificio —no la oficina— quien decide cuándo se reasigna o se retira.
DE UN ESCRITORIO SUELTO A UN EDIFICIO ADMINISTRADO
docker run (M1 y antes) Este módulo: Kubernetes real
┌─────────────────┐ ┌──────────────────────────────────┐
│ un contenedor │ │ Deployment (el termostato) │
│ sin supervisión │ ──▶ │ mantiene N Pods siempre │
│ IP que cambia │ │ ┌────┐ ┌────┐ ┌────┐ │
│ cada vez que │ │ │Pod │ │Pod │ │Pod │ (oficinas) │
│ se recrea │ │ └────┘ └────┘ └────┘ │
└─────────────────┘ │ │ │ │ │
│ └───────┴───────┘ │
│ │ │
│ Service (la centralita) │
│ una dirección que nunca cambia │
└──────────────────────────────────┘
Errores comunes
Asumir que este módulo va a repetir lo que ya se hizo con docker run en aws-serverless-and-containers-guide (de expectativa). Qué pasa: alguien llega esperando que este módulo re-explique contenedores, capas de imagen o docker build, y se sorprende de que la lección 2 salte directo al vocabulario de Kubernetes sin ese repaso. Por qué pasa: es natural asumir que "correr un contenedor en Kubernetes" empieza donde terminó "correr un contenedor con Docker". Cómo detectarlo: si en algún punto de este módulo te preguntas "¿y esto no era lo mismo que ya vi?", la respuesta es no — Kubernetes no reemplaza a Docker, coordina contenedores que Docker (o, más precisamente, containerd, el motor de bajo nivel que kind usa dentro de cada nodo) ya sabe correr. Cómo corregirlo: si el vocabulario de imágenes/capas/docker build se siente oxidado, docker-essentials-guide sigue siendo la referencia — este módulo no lo repite.
Pensar que un Deployment es "un Pod con más pasos" (conceptual, el error central que la lección 3 corrige a fondo). Qué pasa: alguien trata al Deployment como una forma alternativa de crear un solo Pod, en vez de entender que es un objeto de un tipo completamente distinto — un controlador que administra Pods, no un Pod en sí mismo. Cómo detectarlo: si esperas que kubectl get pods muestre un objeto llamado exactamente igual que tu Deployment, sin ningún sufijo adicional. Cómo corregirlo: la lección 5 de este módulo te muestra, con evidencia real, que un Deployment nunca aparece en kubectl get pods — ahí solo aparecen los Pods que ese Deployment, a través de un ReplicaSet intermedio, creó por debajo.
Saltarse la lección 4 (el Pod suelto) por parecer "el paso más simple, no hace falta practicarlo" (de flujo). Qué pasa: alguien va directo a la lección 5 (el Deployment real de andes-cargo-status-api), sin pasar por la lección 4, y se pierde la única oportunidad de este módulo de ver, con sus propias manos, la diferencia de comportamiento entre un Pod suelto (que nadie repone si lo borras) y un Pod administrado por un Deployment (que sí se repone solo). Por qué pasa: un Pod suelto se siente como "la versión de práctica", no como el ejercicio que sí importa. Cómo detectarlo: si en la lección 5 te sorprende que el Pod borrado se reponga solo, es señal de que te saltaste la comparación de la lección 4. Cómo corregirlo: la lección 4 existe exactamente para instalar esa expectativa antes de verla confirmada en la lección 5 — no te la saltes, aunque parezca el paso menos interesante del módulo.
Ejercicios
Ejercicio 1 — Completa la tabla de memoria. Sin volver a mirar la sección "Qué le falta a docker run que Kubernetes resuelve", escribe los cuatro escenarios de esa tabla y, para cada uno, en una frase, qué objeto de Kubernetes lo resuelve.
Ver solución
- El proceso muere dentro del contenedor → el
kubeletlo reinicia según larestartPolicydel Pod. - Necesitas varias instancias corriendo a la vez → un
Deploymentconreplicas: Nmantiene ese número. - Una de esas instancias se cae → el mismo controlador del
Deployment(a través de suReplicaSet) nota la diferencia y crea una nueva. - Necesitas una dirección estable para hablarle al grupo → un
Serviceda un nombre y una dirección que nunca cambian.
Si reconstruiste los cuatro sin mirar, ya tienes instalado el motivo de fondo por el que este módulo existe.
Ejercicio 2 — Extiende la analogía del edificio. Con la analogía de esta lección (edificio de oficinas, termostato, centralita), predice: si un Deployment es el termostato y un Service es la centralita, ¿qué sería, en esta misma analogía, el ReplicaSet que la lección 3 va a presentar como una pieza intermedia entre ambos?
Ver solución
Una respuesta razonable: el ReplicaSet sería el técnico de mantenimiento que el termostato (Deployment) realmente instruye para que abra o cierre oficinas — el termostato decide cuántas oficinas deberían estar ocupadas según la temperatura deseada, pero es una capa intermedia la que ejecuta esa decisión oficina por oficina. La lección 3 desarrolla esta relación con precisión técnica: casi nunca interactúas con el ReplicaSet directamente, de la misma forma en que casi nunca hablas directo con el técnico de mantenimiento — le hablas al termostato, y el resto pasa solo.
Ejercicio 3 — Predice el orden de aparición. Basado en el mapa de las 8 lecciones de esta lección, predice: ¿por qué crees que este módulo enseña primero el Pod suelto (lecciones 2 y 4), antes que el Deployment (lecciones 3 y 5), en vez de ir directo al Deployment, que es lo que realmente vas a usar en producción?
Ver solución
Porque entender qué es un Pod —la unidad mínima, sin ningún controlador detrás— es el requisito para entender qué agrega exactamente un Deployment encima. Si empezaras directo por el Deployment, no tendrías forma de distinguir "esto lo hace Kubernetes en general" de "esto lo hace específicamente el controlador del Deployment" — la lección 4 (Pod suelto, nadie lo repone) y la lección 5 (Pod administrado, sí se repone) están diseñadas, a propósito, para que veas ese contraste con tus propias manos, no solo lo leas.
Resumen y siguiente paso
En esta lección viste, con una tabla concreta, los cuatro problemas reales que docker run solo no resuelve y que sí resuelve un clúster de Kubernetes: reinicio automático, mantener un número de réplicas, reponer las que se caen, y dar una dirección estable a un grupo de Pods que cambia todo el tiempo por debajo. Conociste el mapa completo de las 8 lecciones de este módulo, y la analogía que las va a acompañar: un edificio de oficinas administrado, con un termostato (Deployment) y una centralita (Service), en vez de un escritorio suelto sin ningún tipo de supervisión.
Antes de avanzar deberías poder: explicar, con tus propias palabras, la diferencia de fondo entre docker run y un Deployment; nombrar las tres primitivas que este módulo construye, en el orden en que aparecen; y anticipar por qué la lección 4 (Pod suelto) viene antes que la lección 5 (Pod administrado).
Siguiente lección: qué es un Pod, y por qué casi nunca se crea uno suelto. Ahí empieza la primera pieza real de este módulo — la unidad mínima programable de todo Kubernetes, la oficina individual de la analogía de esta lección.
Recursos
- Kubernetes — Workloads — la puerta de entrada oficial a Pods, Deployments y ReplicaSets, referencia central de este módulo completo.
- Kubernetes — Pods — referencia oficial de la unidad mínima programable, tema de la lección 2.
aws-serverless-and-containers-guide(NIEVA), Módulo 7 — el modelo de ECS que esta guía usa constantemente como punto de comparación con Kubernetes.