Cerrábamos la lección anterior diciendo que ya tenías clúster, CLI y lenguaje de manifiestos, y que solo faltaba el paciente. Aquí está. Esta lección presenta a fondo Rutas Norte S.L., la empresa ficticia cuya plataforma de venta de billetes de autobús vamos a desplegar y operar sobre Kubernetes durante los once módulos restantes. No es un ejemplo decorativo: cada concepto que aprendas a partir de ahora entrará en escena porque Rutas Norte lo necesita, y al final del curso tendrás una plataforma completa, segura, observable y autoescalada, construida pieza a pieza. Verás la situación de partida de la empresa, cada uno de sus seis componentes, la arquitectura objetivo, el mapa de qué módulo aporta qué, y las convenciones que seguiremos siempre. Y terminarás con el primer despliegue real: tienda-web corriendo en tu clúster y respondiendo en tu navegador.
Contenido
- La empresa y su problema actual
- Los seis componentes de la plataforma
- Arquitectura objetivo sobre Kubernetes
- Hoja de ruta: qué aporta cada módulo
- Convenciones del proyecto
- Primer despliegue:
tienda-webenrutas-norte-dev - Por qué este pod es frágil
- La empresa y su problema actual
Rutas Norte S.L. es un operador de autobuses interurbanos que vende sus billetes por internet a través de la plataforma Rutas Norte. Trece personas en el departamento técnico, unas 4.000 reservas diarias de media y 25.000 en los días punta.
Su infraestructura actual:
- Dos máquinas alquiladas con Docker Compose. Una sirve la web y la API; la otra, la base de datos.
- Despliegues manuales: alguien se conecta por SSH, ejecuta
docker compose pull && docker compose up -dy cruza los dedos. Hay un corte de servicio de entre uno y dos minutos, así que solo se despliega los martes de madrugada. - Sin autorreparación: en marzo, el contenedor de la API murió a las 02:40 por una fuga de memoria y nadie se dio cuenta hasta las 07:15. Cuatro horas y media sin vender billetes.
- Sin elasticidad: en los picos de puentes y vacaciones el tráfico se multiplica por seis. La respuesta ha sido contratar una máquina dimensionada para agosto y pagarla los doce meses.
- Secretos por todas partes: la contraseña de PostgreSQL vive en un fichero
.envque se ha compartido por correo. Esa base de datos contiene nombre, DNI, teléfono y correo de cada cliente. - Sin observabilidad: los logs están en ficheros dentro de cada contenedor. Diagnosticar un problema implica entrar por SSH y buscar con
grep.
La dirección técnica ha aprobado la migración a Kubernetes con cuatro objetivos medibles: cero cortes en los despliegues, recuperación automática de caídas, capacidad elástica en los picos y trazabilidad y control de acceso sobre los datos personales. Esos cuatro objetivos son, uno a uno, el temario que tienes por delante.
- Los seis componentes de la plataforma
Estos nombres te acompañarán durante todo el curso. Consérvalos exactamente así.
2.1. tienda-web
- Qué es: el frontal público. Una SPA servida por
nginxcon los resultados de búsqueda, el selector de asientos y el proceso de pago. - Estado: sin estado. Cualquier réplica sirve cualquier petición.
- Imagen:
registry.rutasnorte.example/tienda-web:1.8.0 - Dominio:
www.rutasnorte.example - Qué necesita del clúster: varias réplicas, exposición HTTP/HTTPS al exterior, configuración inyectada (la URL de la API varía por entorno) y actualización sin corte.
2.2. api-reservas
- Qué es: la API REST en Node.js. Consulta disponibilidad de plazas, calcula precios, crea reservas y emite billetes. Es el corazón del negocio.
- Estado: sin estado. Toda la persistencia está en PostgreSQL y en Redis.
- Imagen:
registry.rutasnorte.example/api-reservas:2.4.0 - Dominio:
api.rutasnorte.example - Qué necesita del clúster: réplicas, autoescalado por carga, credenciales de base de datos como secreto, sondas de salud, límites de recursos y acceso restringido a la base de datos.
2.3. postgres-reservas
- Qué es: PostgreSQL 16 con las tablas de reservas, clientes, rutas y expediciones.
- Estado: con estado. Es el componente crítico del sistema.
- Imagen:
postgres:16 - Qué necesita del clúster: almacenamiento persistente que sobreviva a la recreación del pod, identidad de red estable, credenciales gestionadas como secreto, copias de seguridad y una política de red que impida que cualquiera se conecte a él.
2.4. redis-cache
- Qué es: caché en memoria de la disponibilidad de plazas por expedición y fecha. Absorbe la mayoría de las consultas de búsqueda y evita saturar PostgreSQL.
- Estado: con estado, pero prescindible. Si se pierde, se repuebla desde la base de datos; solo se degrada el rendimiento durante unos minutos.
- Imagen:
redis:7.2-alpine - Qué necesita del clúster: un nombre estable, límites de memoria estrictos y configuración de política de expulsión.
2.5. worker-notificaciones
- Qué es: proceso en segundo plano que consume una cola y envía los correos de confirmación de reserva y los recordatorios de viaje.
- Estado: sin estado, pero no recibe tráfico entrante: no necesita ser accesible desde fuera.
- Imagen:
registry.rutasnorte.example/worker-notificaciones:1.2.0 - Qué necesita del clúster: réplicas escaladas por longitud de cola y no por CPU, credenciales SMTP como secreto, y apagado ordenado para no perder un envío a medias.
2.6. informes-ocupacion
- Qué es: tarea programada que cada noche a las 02:30 calcula la ocupación por línea y expedición del día anterior y deja un informe para el departamento comercial.
- Estado: sin estado, de ejecución finita: arranca, trabaja unos minutos y termina.
- Imagen:
registry.rutasnorte.example/informes-ocupacion:1.0.3 - Qué necesita del clúster: ejecución programada, control de reintentos, límite de ejecuciones simultáneas e historial de resultados.
Resumen comparativo
| Componente | Estado | Tráfico entrante | Objeto de Kubernetes que lo gobernará | Módulo |
|---|---|---|---|---|
tienda-web |
Sin estado | Público (HTTP) | Deployment + Service + Ingress | 2, 4 |
api-reservas |
Sin estado | Público (HTTP) e interno | Deployment + Service + Ingress + HPA | 2, 4, 9 |
postgres-reservas |
Con estado | Solo interno, restringido | StatefulSet + PVC + Service headless | 5, 6 |
redis-cache |
Con estado prescindible | Solo interno | Deployment o StatefulSet + Service | 2, 5 |
worker-notificaciones |
Sin estado | Ninguno | Deployment (sin Service) + KEDA | 2, 9 |
informes-ocupacion |
Sin estado, finito | Ninguno | CronJob | 6 |
Esa tabla contiene ya una lección de diseño: el tipo de objeto de Kubernetes que necesitas se deduce de dos preguntas —¿tiene estado? y ¿recibe tráfico?— antes de escribir una sola línea de YAML.
- Arquitectura objetivo sobre Kubernetes
Este es el destino al que llegaremos al final del módulo 11.
flowchart TB
U["Clientes<br/>navegador y móvil"]
DNS["DNS<br/>www / api .rutasnorte.example"]
U --> DNS
subgraph CL["Clúster de Kubernetes"]
ING["Ingress Controller<br/>TLS con cert-manager"]
subgraph NS["namespace rutas-norte-pro"]
SVCW["Service tienda-web"]
SVCA["Service api-reservas"]
TW1["Pod tienda-web"]
TW2["Pod tienda-web"]
API1["Pod api-reservas"]
API2["Pod api-reservas"]
API3["Pod api-reservas"]
SVCR["Service redis-cache"]
RED["Pod redis-cache"]
SVCP["Service postgres-reservas"]
PG["StatefulSet<br/>postgres-reservas-0"]
PVC[("PVC 20 GiB")]
WK1["Pod worker-notificaciones"]
WK2["Pod worker-notificaciones"]
CJ["CronJob<br/>informes-ocupacion"]
end
end
DNS --> ING
ING --> SVCW
ING --> SVCA
SVCW --> TW1
SVCW --> TW2
SVCA --> API1
SVCA --> API2
SVCA --> API3
API1 --> SVCR
API2 --> SVCR
API3 --> SVCP
SVCR --> RED
SVCP --> PG
PG --- PVC
WK1 --> SVCP
WK2 --> SVCP
CJ --> SVCP
Puntos que conviene leer en el diagrama:
- Una sola puerta de entrada: el Ingress Controller termina el TLS y enruta por dominio hacia el Service correspondiente.
- Ningún componente conoce IPs: todo se comunica por el nombre del Service, resuelto por el DNS interno del clúster.
postgres-reservases el único con disco persistente, y solo debe ser alcanzable desdeapi-reservas, el worker y el CronJob. Eso se garantizará con una NetworkPolicy en el módulo 4.worker-notificacionesno tiene Service: nadie le habla, él consume trabajo y sale a hablar con otros.
- Hoja de ruta: qué aporta cada módulo
Esta tabla es el contrato del curso: dice qué pieza de Rutas Norte añadimos o mejoramos en cada módulo.
| Módulo | Qué se añade o se mejora en Rutas Norte |
|---|---|
| 1. Introducción | Clúster de prácticas, convenciones, namespace rutas-norte-dev y primer pod de tienda-web |
| 2. Componentes principales | tienda-web y api-reservas como Deployments con réplicas; Services internos; actualización de versión sin corte y rollback; separación en namespaces por entorno |
| 3. Configuración y secretos | Configuración de la tienda y la API en ConfigMaps; contraseña de PostgreSQL y credenciales SMTP en Secrets; requests y limits en todos los componentes; cuotas por entorno; ServiceAccounts propias |
| 4. Redes | Publicación de www.rutasnorte.example y api.rutasnorte.example con Ingress y TLS; DNS interno entre componentes; NetworkPolicy que aísla postgres-reservas |
| 5. Almacenamiento | Volumen persistente para la base de datos vía PVC y StorageClass; expansión del volumen; copias de seguridad y restauración de las reservas |
| 6. Conceptos avanzados | postgres-reservas migrado a StatefulSet; informes-ocupacion como CronJob; init container que espera a la base de datos; sidecar de métricas; agente de logs como DaemonSet |
| 7. Monitoreo y registro | Sondas de vida y disponibilidad en todos los componentes; kubectl top; Prometheus y Grafana con panel de reservas por minuto; logs centralizados; depuración de incidencias |
| 8. Seguridad | RBAC por equipo y entorno; contenedores sin privilegios y con sistema de ficheros de solo lectura; Pod Security Standards; endurecimiento de red; firma y escaneo de imágenes |
| 9. Escalado y rendimiento | HPA sobre api-reservas para los picos de puentes; VPA para dimensionar; autoescalado de nodos; escalado de worker-notificaciones por longitud de cola con KEDA; PodDisruptionBudgets |
| 10. Ecosistema | Empaquetado de la plataforma con Helm; variantes por entorno con Kustomize; despliegue continuo con GitOps; traslado a un clúster gestionado |
| 11. Casos reales | Puesta en producción completa; pipeline de CI/CD; despliegue canary de una versión de la API; operación diaria, runbooks y control de costes |
| 12. Certificación | Repaso transversal orientado a CKA, CKAD y CKS usando la plataforma como banco de pruebas |
- Convenciones del proyecto
Estas reglas se aplican en todas las lecciones. Adóptalas desde ahora; son también buenas prácticas del mundo real.
5.1. Namespaces por entorno
| Namespace | Uso | Quién despliega |
|---|---|---|
rutas-norte-dev |
Desarrollo. Datos ficticios, recursos reducidos | Cualquier persona del equipo |
rutas-norte-pre |
Preproducción. Réplica fiel de producción para validar | Pipeline automático |
rutas-norte-pro |
Producción. Datos reales de clientes | Solo el pipeline, con aprobación |
Durante el módulo 1 y buena parte del 2 trabajaremos solo en rutas-norte-dev.
5.2. Esquema de etiquetas
Todos los objetos llevan estas tres etiquetas como mínimo:
metadata:
labels:
app: tienda-web # el componente
app.kubernetes.io/part-of: rutas-norte # la plataforma completa
entorno: dev # el entornoEsto permite consultas como estas, que valen oro cuando el clúster crece:
kubectl get all -l app.kubernetes.io/part-of=rutas-norte -n rutas-norte-dev
kubectl get pods -l entorno=pro -A
kubectl logs -l app=api-reservas -n rutas-norte-dev --tail=505.3. Registro e imágenes: etiquetas inmutables
El registro de imágenes de la empresa es registry.rutasnorte.example. La regla más importante del proyecto en cuanto a imágenes:
Cada versión se publica con una etiqueta única e inmutable, que nunca se reutiliza. Prohibido
latesten cualquier entorno.
| Referencia | ¿Válida? | Por qué |
|---|---|---|
registry.rutasnorte.example/api-reservas:2.4.0 |
Sí | Versión semántica, inmutable |
registry.rutasnorte.example/api-reservas:2.4.0-a3f9c1b |
Sí, mejor | Incluye el commit: trazabilidad total |
registry.rutasnorte.example/api-reservas@sha256:9f3a1c... |
Sí, la más estricta | Digest: imposible de suplantar |
registry.rutasnorte.example/api-reservas:latest |
No | Dos réplicas del mismo Deployment pueden acabar ejecutando código distinto; el rollback deja de ser fiable y no sabes qué hay en producción |
5.4. Estructura del repositorio
La que definimos en la lección Objetos, Manifiestos YAML y el Modelo Declarativo:
rutas-norte/
└── k8s/
├── base/ # definición común
├── entornos/
│ ├── dev/
│ ├── pre/
│ └── pro/
├── entorno-local/
└── README.mdY la regla de oro que la acompaña: si un cambio no está en Git, no existe. Nada de kubectl edit como método de despliegue.
- Primer despliegue:
tienda-web en rutas-norte-dev
tienda-web en rutas-norte-devSuficiente teoría. Vamos a desplegar el primer trozo real de Rutas Norte.
Paso 1: comprobar el clúster
rutas-norte
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
rutas-nortePaso 2: crear el namespace de forma declarativa
# k8s/base/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: rutas-norte-dev
labels:
app.kubernetes.io/part-of: rutas-norte
entorno: devFíjate en que lo hemos creado con un manifiesto, no con kubectl create namespace. Es una decisión deliberada: el namespace forma parte de la definición del proyecto y debe estar en Git como todo lo demás.
Y ahora, el consejo de productividad de la lección 01-05, que te ahorrará escribir -n rutas-norte-dev cientos de veces:
Paso 3: escribir el manifiesto del pod
# k8s/base/tienda-web-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: tienda-web
namespace: rutas-norte-dev
labels:
app: tienda-web
app.kubernetes.io/part-of: rutas-norte
entorno: dev
annotations:
rutasnorte.example/responsable: [email protected]
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- name: http
containerPort: 80
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "128Mi"Repaso campo a campo, apoyándonos en lo aprendido:
apiVersion: v1ykind: Pod: el Pod pertenece al grupo core, por eso no lleva prefijo de grupo.metadata.name: nombre único dentro del namespace.metadata.labels: las tres etiquetas del proyecto. En el módulo 2 serán las que use el Service para encontrar sus pods.metadata.annotations: metadato informativo, no consultable.spec.containers: una lista. Aquí un solo elemento, como será habitual.image: nginx:1.27-alpine: usamos la imagen públicanginxporqueregistry.rutasnorte.examplees un registro ficticio y no existe. En todo el curso, cuando aparezca una imagen deregistry.rutasnorte.example, sustitúyela mentalmente —y en tu clúster— por su equivalente público. Etiqueta explícita, nuncalatest, según la convención del proyecto.ports: es documentación informativa; no abre nada por sí sola. La exposición real vendrá con el Service (módulo 2).resources:requestses lo que el scheduler reserva para decidir en qué nodo cabe;limitses el techo que el kubelet impone. Los estudiaremos a fondo en el módulo 3, pero conviene ponerlos desde el primer día.
Paso 4: validar y aplicar
kubectl apply -f k8s/base/tienda-web-pod.yaml --dry-run=client
kubectl apply -f k8s/base/tienda-web-pod.yamlPaso 5: observar el arranque
NAME READY STATUS RESTARTS AGE
tienda-web 0/1 Pending 0 0s
tienda-web 0/1 ContainerCreating 0 1s
tienda-web 1/1 Running 0 4sEstás viendo, en directo, el recorrido de la lección Arquitectura de Kubernetes: Pending mientras el scheduler elige nodo, ContainerCreating mientras el kubelet descarga la imagen y prepara la red, Running cuando el contenedor está vivo. Corta con Ctrl+C.
Paso 6: inspeccionar
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
tienda-web 1/1 Running 0 45s 10.244.0.21 rutas-norte <none>Name: tienda-web
Namespace: rutas-norte-dev
Node: rutas-norte/192.168.49.2
Labels: app=tienda-web
app.kubernetes.io/part-of=rutas-norte
entorno=dev
Status: Running
IP: 10.244.0.21
Containers:
nginx:
Image: nginx:1.27-alpine
Port: 80/TCP
State: Running
Ready: True
Restart Count: 0
Limits: cpu: 200m, memory: 128Mi
Requests: cpu: 50m, memory: 64Mi
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 50s default-scheduler Successfully assigned rutas-norte-dev/tienda-web to rutas-norte
Normal Pulled 49s kubelet Container image "nginx:1.27-alpine" already present on machine
Normal Created 49s kubelet Created container nginx
Normal Started 49s kubelet Started container nginxLos cuatro eventos cuentan la historia completa: el scheduler asignó nodo, y después el kubelet obtuvo la imagen, creó el contenedor y lo arrancó. Exactamente el reparto de responsabilidades que estudiamos.
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/08/05 10:22:14 [notice] 1#1: nginx/1.27.0
2026/08/05 10:22:14 [notice] 1#1: start worker processesPaso 7: verlo en el navegador
El pod tiene IP 10.244.0.21, pero esa IP solo existe dentro del clúster. Para alcanzarla desde tu máquina usamos port-forward:
Abre http://localhost:8080 en el navegador: verás la página de bienvenida de nginx. En Rutas Norte, esa sería la portada de la tienda de billetes. Deja el comando corriendo mientras pruebas y córtalo con Ctrl+C.
Desde otra terminal también puedes comprobarlo sin navegador:
Enhorabuena: acabas de desplegar el primer componente de Rutas Norte en Kubernetes.
- Por qué este pod es frágil
Antes de celebrarlo demasiado, hagamos el experimento que da sentido al módulo 2.
Ha desaparecido y no vuelve. Compáralo con lo que dijimos en la lección de conceptos: un pod suelto no tiene ningún controlador que lo vigile. Nadie ejecuta un bucle de reconciliación sobre él, porque el estado deseado que declaraste era literalmente "que exista este pod", y al borrarlo también borraste esa declaración.
Los cuatro problemas de un pod suelto:
| Problema | Consecuencia para Rutas Norte |
|---|---|
| No se recrea si muere | Reproduce exactamente la caída nocturna de cuatro horas y media que sufrió la empresa en marzo |
| No se puede escalar | Para tener tres copias habría que escribir tres manifiestos con tres nombres distintos |
| No se puede actualizar sin corte | Cambiar de imagen exige borrar y recrear: hay un intervalo sin servicio |
| No tiene dirección estable | Cada recreación cambia la IP, y nadie sabe a dónde conectarse |
La solución es la jerarquía que ya conoces del mapa conceptual: Deployment → ReplicaSet → Pod. Un Deployment declara "quiero 3 réplicas de esta imagen", el ReplicaSet las mantiene contra viento y marea, y un Service les da un nombre estable.
Vuelve a crear el pod para dejar el clúster como estaba y cerrar el módulo con la plataforma en marcha:
Errores Comunes y Consejos
- Aplicar en el namespace equivocado. Si
kubectl get podsno muestra nada, comprueba el namespace conkubectl config view --minify | grep namespaceo usa-A. Declararmetadata.namespaceexplícitamente en cada manifiesto, como hacemos aquí, elimina el problema de raíz. - Usar
registry.rutasnorte.exampleliteralmente. Es un registro ficticio: no existe. El pod se quedaría enImagePullBackOff. En los ejercicios prácticos usa siempre la imagen pública equivalente (nginx,postgres,redis,node). - Creer que
ports.containerPortexpone el pod. No hace nada por sí solo: es documentación. El acceso real llega conport-forward(para pruebas), Service (módulo 2) o Ingress (módulo 4). - Dejar
port-forwardcomo solución. Es una herramienta de depuración de un solo usuario, atada a tu terminal. Nunca es una forma de exponer un servicio. - Crear pods sueltos y acostumbrarse. Este ha sido un ejercicio deliberado para entender la fragilidad. A partir del módulo 2, todo va en Deployments.
- Olvidar
resources. Sinrequests, el scheduler no puede planificar bien; sinlimits, un contenedor descontrolado puede tumbar a sus vecinos. Ponlos desde el principio aunque los ajustes vengan después. - Consejo: crea ya el repositorio con la estructura
k8s/y haz un commit connamespace.yamlytienda-web-pod.yaml. Trabajar así desde la primera lección es la diferencia entre terminar el curso con una plataforma reproducible y terminar con un clúster lleno de cosas que nadie sabe cómo se crearon.
Ejercicios
Ejercicio 1: Diseñar la plataforma sobre el papel
Sin escribir ningún manifiesto, completa una tabla con los seis componentes de Rutas Norte y, para cada uno, responde: (a) ¿tiene estado?, (b) ¿recibe tráfico entrante y de dónde?, (c) ¿qué objeto de Kubernetes lo gobernará?, y (d) ¿qué pasaría hoy, con Docker Compose, si la máquina que lo aloja se apagara? Después, justifica en tres líneas por qué redis-cache y postgres-reservas, siendo ambos "con estado", reciben tratamientos muy distintos.
Ejercicio 2: Desplegar y explorar redis-cache
Siguiendo todas las convenciones del proyecto, crea el manifiesto k8s/base/redis-cache-pod.yaml para un pod redis-cache en rutas-norte-dev con la imagen pública redis:7.2-alpine, el puerto 6379, las tres etiquetas del proyecto y requests de 50m de CPU y 64Mi de memoria. Después:
- Valídalo sin tocar el clúster y aplícalo.
- Comprueba que está
Runningy averigua su IP y su nodo. - Entra en el contenedor y comprueba que Redis responde ejecutando
redis-cli ping. - Muestra únicamente los pods de la plataforma Rutas Norte usando la etiqueta común.
Ejercicio 3: Demostrar la fragilidad y anticipar la solución
- Simula una caída de la aplicación matando el proceso principal del contenedor de
tienda-webconkubectl exec, y observa con--watchqué ocurre. ¿Vuelve el contenedor? ¿Cambia el contadorRESTARTS? ¿Cambia la IP del pod? - Ahora borra el pod entero con
kubectl delete pody observa de nuevo. ¿Vuelve? - Explica la diferencia entre los dos casos indicando qué componente actúa en cada uno.
- Escribe en dos frases qué objeto necesitarías para que el segundo caso también se recuperase solo.
Soluciones
Solución 1
| Componente | (a) Estado | (b) Tráfico entrante | (c) Objeto | (d) Si cae la máquina hoy |
|---|---|---|---|---|
tienda-web |
No | Público, desde internet | Deployment + Service + Ingress | La web deja de responder por completo |
api-reservas |
No | Público e interno | Deployment + Service + Ingress + HPA | No se pueden consultar ni crear reservas |
postgres-reservas |
Sí | Interno, restringido | StatefulSet + PVC | Parada total y riesgo de pérdida de datos si el disco no está replicado |
redis-cache |
Sí, prescindible | Interno | Deployment o StatefulSet + Service | Degradación de rendimiento; el servicio sigue con consultas directas a la base de datos |
worker-notificaciones |
No | Ninguno | Deployment (sin Service) | Los correos de confirmación se acumulan sin enviarse |
informes-ocupacion |
No, finito | Ninguno | CronJob | El informe nocturno no se genera |
redis-cache y postgres-reservas reciben tratamientos distintos porque el estado de Redis es reconstruible: si se pierde, se repuebla desde la base de datos y solo se degrada el rendimiento. El estado de PostgreSQL es la fuente de verdad del negocio: perderlo significa perder reservas y datos personales de clientes. Por eso PostgreSQL exige almacenamiento persistente, identidad estable, copias de seguridad y aislamiento de red, mientras que Redis solo necesita un límite de memoria y una política de expulsión.
Solución 2
# k8s/base/redis-cache-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis-cache
namespace: rutas-norte-dev
labels:
app: redis-cache
app.kubernetes.io/part-of: rutas-norte
entorno: dev
spec:
containers:
- name: redis
image: redis:7.2-alpine
ports:
- name: redis
containerPort: 6379
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "256Mi"kubectl apply -f k8s/base/redis-cache-pod.yaml --dry-run=client
kubectl apply -f k8s/base/redis-cache-pod.yaml
kubectl get pod redis-cache -o wide
kubectl exec -it redis-cache -- redis-cli ping
kubectl get pods -l app.kubernetes.io/part-of=rutas-norteSolución 3
El contenedor sí vuelve: RESTARTS pasa de 0 a 1 y la IP del pod no cambia, porque es el mismo pod. Quien lo recupera es el kubelet, aplicando la restartPolicy: Always que un pod tiene por defecto. El kubelet reinicia contenedores dentro de un pod existente.
El pod no vuelve. No hay ningún controlador que vigile su existencia.
-
La diferencia está en quién actúa y sobre qué: el kubelet vigila los contenedores dentro de los pods que tiene asignados y los reinicia si mueren, pero no puede recrear un pod que ya no existe como objeto en la API. Recrear pods es trabajo de un controlador del kube-controller-manager, y un pod suelto no tiene ninguno asociado: al borrarlo, borraste el propio estado deseado.
-
Necesitas un Deployment, que crea un ReplicaSet cuyo controlador mantiene permanentemente el número de réplicas declarado. Con él, el estado deseado deja de ser "que exista este pod concreto" y pasa a ser "que existan N pods con estas características", de modo que borrar uno provoca inmediatamente la creación de otro.
Conclusión
Ya conoces el proyecto que da sentido a todo el curso. Rutas Norte S.L. es una empresa con problemas absolutamente reales —caídas nocturnas sin respuesta, picos de demanda en puentes y vacaciones, despliegues con corte de servicio y datos personales de clientes mal protegidos— y su plataforma se compone de seis piezas con necesidades bien diferenciadas: dos frontales sin estado, una base de datos crítica con estado, una caché prescindible, un worker sin tráfico entrante y una tarea nocturna programada. Tienes la arquitectura objetivo, el mapa de qué módulo aporta qué, y las convenciones —namespaces por entorno, esquema de etiquetas, etiquetas de imagen inmutables y estructura k8s/ versionada en Git— que aplicaremos sin excepción.
Y, sobre todo, ya has desplegado tu primer componente: tienda-web corriendo en rutas-norte-dev, inspeccionado con get, describe y logs, y visible en tu navegador mediante port-forward. También has comprobado en primera persona su punto débil: un pod suelto que, al borrarse, no vuelve nunca.
Con esto cierras el módulo 1. Sabes qué es Kubernetes, cómo está construido, qué significa cada término, tienes un clúster funcionando, dominas kubectl y entiendes el modelo declarativo. El módulo 2, Componentes Principales de Kubernetes, arranca justo donde termina esta lección: convertiremos ese pod frágil en un Deployment con varias réplicas que se recupera solo, aprenderemos primero qué es exactamente un Pod por dentro y cómo un ReplicaSet lo mantiene con vida, y le daremos a tienda-web y a api-reservas una dirección estable con Servicios. La plataforma Rutas Norte empieza a tomar forma de verdad.
Curso de Kubernetes
Módulo 1: Introducción a Kubernetes
- ¿Qué es Kubernetes?
- Arquitectura de Kubernetes
- Conceptos y Terminología Clave
- Configuración de un Clúster de Kubernetes
- La CLI de Kubernetes: kubectl
- Objetos, Manifiestos YAML y el Modelo Declarativo
- El Proyecto del Curso: la Plataforma Rutas Norte
Módulo 2: Componentes Principales de Kubernetes
- Pods
- ReplicaSets
- Deployments
- Actualizaciones, Rollbacks y Estrategias de Despliegue
- Servicios
- Namespaces
- Etiquetas, Selectores y Anotaciones
Módulo 3: Gestión de Configuración y Secretos
- ConfigMaps
- Secrets
- Variables de Entorno
- Cuotas y Límites de Recursos
- LimitRanges y Clases de Calidad de Servicio (QoS)
- ServiceAccounts y Acceso a la API desde los Pods
Módulo 4: Redes en Kubernetes
- Redes de Clúster
- Tipos de Servicios
- DNS Interno y Descubrimiento de Servicios
- Controladores de Ingress
- TLS y Gestión de Certificados con cert-manager
- Políticas de Red
Módulo 5: Almacenamiento en Kubernetes
- Volúmenes
- Volúmenes Persistentes
- Reclamaciones de Volúmenes Persistentes
- Clases de Almacenamiento
- Aprovisionamiento Dinámico, Expansión y Snapshots
- Copias de Seguridad y Restauración de Datos
Módulo 6: Conceptos Avanzados de Kubernetes
- StatefulSets
- DaemonSets
- Trabajos y CronJobs
- Init Containers, Sidecars y Patrones Multi-Contenedor
- Planificación: Afinidad, Taints y Tolerations
- Definiciones de Recursos Personalizados (CRDs)
- Operadores y el Patrón Controlador
Módulo 7: Monitoreo y Registro
- Verificaciones de Salud y Sondas
- Servidor de Métricas y kubectl top
- Monitoreo con Prometheus
- Visualización y Alertas con Grafana y Alertmanager
- Registro Centralizado con Elasticsearch, Fluentd y Kibana (EFK)
- Depuración de Aplicaciones y Eventos del Clúster
Módulo 8: Seguridad en Kubernetes
- Control de Acceso Basado en Roles (RBAC)
- Contextos de Seguridad y Endurecimiento del Contenedor
- Políticas de Seguridad de Pods y Pod Security Standards
- Seguridad de Red
- Seguridad de Imágenes
- Auditoría, Escaneo y Gestión de Vulnerabilidades
Módulo 9: Escalado y Rendimiento
- Autoescalado Horizontal de Pods
- Autoescalado Vertical de Pods
- Autoescalado de Clúster
- Escalado por Eventos y Métricas Personalizadas con KEDA
- Alta Disponibilidad: PodDisruptionBudgets y Topología
- Ajuste de Rendimiento
Módulo 10: Ecosistema y Herramientas de Kubernetes
- Minikube y Entornos Locales con kind
- Kubeadm
- Helm
- Kustomize
- GitOps con Argo CD y Flux
- Kubernetes Gestionado: EKS, AKS y GKE
Módulo 11: Estudios de Caso y Aplicaciones del Mundo Real
- Despliegue de una Aplicación Web
- Ejecución de Aplicaciones con Estado
- CI/CD con Kubernetes
- Estrategias de Despliegue: Blue-Green y Canary
- Gestión Multi-Clúster
- Operación en Producción: Incidencias, Runbooks y Costes
