← Todos los temas
Día 8 · Cómo se publica y se conecta

Kubernetes

Qué es Kubernetes y cómo ayuda a que una app aguante picos de visitas.

Definición

Kubernetes (abreviado K8s: la K, ocho letras y la s) es una plataforma de código abierto para orquestar contenedores: automatiza su despliegue, su escalado y su recuperación ante fallos en un conjunto de máquinas. El nombre viene del griego kubernḗtēs, «timonel». Nació en Google, que lo liberó en 2014, y hoy lo mantiene la CNCF (Cloud Native Computing Foundation).

Su idea central es el estado deseado (desired state). No le das órdenes paso a paso («arranca este contenedor aquí»); le declaras el resultado que quieres («quiero 3 copias de esta aplicación siempre en marcha») en archivos YAML llamados manifiestos. Kubernetes compara continuamente lo que declaraste con lo que existe y actúa para igualarlos. Ese ciclo de observar, comparar y corregir se llama bucle de reconciliación y lo ejecutan programas llamados controladores.

Este artículo continúa el del día 5: allí se vio cómo empaquetar una aplicación en una imagen y ejecutarla como contenedor; aquí se ve quién se encarga de ejecutar muchos contenedores en muchas máquinas.

Arquitectura

Un clúster (cluster) es el conjunto de máquinas, llamadas nodos (nodes), que Kubernetes administra. Se divide en dos partes.

ComponenteDóndeFunción
kube-apiserverPlano de controlÚnica puerta de entrada: expone la API REST; kubectl y todos los demás componentes hablan con él
etcdPlano de controlBase de datos clave-valor con el estado del clúster
kube-schedulerPlano de controlDecide en qué nodo se ejecuta cada Pod nuevo según recursos y restricciones
kube-controller-managerPlano de controlEjecuta los controladores (Deployment, nodos, etc.) que reconcilian el estado
kubeletCada nodoAgente que arranca y vigila los contenedores asignados a su nodo
kube-proxyCada nodoMantiene las reglas de red que hacen funcionar los Services
Motor de contenedores (container runtime)Cada nodoEjecuta los contenedores (containerd, CRI-O)

El plano de control (control plane) toma las decisiones; los nodos trabajadores (worker nodes) ejecutan la carga. El usuario interactúa con kubectl, la herramienta de línea de comandos que traduce comandos y archivos en llamadas a la API.

Objetos fundamentales

Todo en Kubernetes es un objeto descrito por un manifiesto con cuatro campos de primer nivel: apiVersion, kind (tipo), metadata (nombre, etiquetas) y spec (el estado deseado).

ObjetoQué esIdea clave
PodLa unidad mínima desplegable: uno o varios contenedores que comparten red y almacenamientoEs efímero: nace, muere y no se «repara», se reemplaza
ReplicaSetMantiene un número fijo de Pods idénticosRara vez se usa directamente
DeploymentGestiona ReplicaSets: réplicas, actualizaciones y reversionesEs el objeto que se usa para aplicaciones sin estado
ServiceNombre y dirección estables que reparten tráfico entre PodsLos Pods cambian de IP; el Service no
IngressReglas de entrada HTTP/HTTPS desde fuera del clúster hacia ServicesRequiere un controlador de Ingress instalado
ConfigMap / SecretConfiguración y datos sensibles separados de la imagenUn Secret está codificado, no cifrado, salvo que se configure el cifrado en reposo
HorizontalPodAutoscalerAjusta el número de réplicas según métricasEs el autoescalado de esta guía
NamespacePartición lógica del clústerAísla equipos o entornos
StatefulSetComo Deployment, pero con identidad y almacenamiento establesPara bases de datos y servicios con estado

Dos mecanismos conectan los objetos entre sí: las etiquetas (labels, pares clave-valor como app: tienda-api) y los selectores (selectors), que eligen objetos por sus etiquetas. El Deployment elige sus Pods por etiqueta; el Service también.

Deployment: réplicas, actualizaciones y reversión

Un Deployment declara cuántas réplicas de un Pod deben existir y cómo cambiar de versión.

Manifiesto deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tienda-api
  labels:
    app: tienda-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tienda-api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    metadata:
      labels:
        app: tienda-api
    spec:
      containers:
        - name: api
          image: ghcr.io/ejemplo/tienda-api:1.0.0
          ports:
            - containerPort: 8000
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 256Mi
          readinessProbe:
            httpGet:
              path: /salud
              port: 8000
            initialDelaySeconds: 3
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /salud
              port: 8000
            initialDelaySeconds: 10
            periodSeconds: 10

Cómo leerlo:

  • replicas: 3: estado deseado, tres Pods. Si uno falla o su nodo desaparece, el controlador crea otro.
  • selector.matchLabels debe coincidir con template.metadata.labels: así el Deployment reconoce sus Pods.
  • strategy.RollingUpdate: al cambiar la imagen, reemplaza los Pods gradualmente. Con maxUnavailable: 0 nunca baja de las 3 réplicas disponibles; maxSurge: 1 permite un Pod extra durante la transición. Resultado: actualización sin interrupción.
  • image: se fija a una etiqueta de versión concreta (1.0.0), no a latest, para que el despliegue sea reproducible. La imagen es un ejemplo; no existe un repositorio real con ese nombre.
  • resources.requests es lo que el planificador reserva al elegir nodo; limits es el techo. 100m significa 100 milicores (0,1 de un núcleo de CPU). Superar el límite de memoria termina el contenedor (OOMKilled); superar el de CPU solo lo ralentiza.
  • readinessProbe (sonda de disponibilidad): mientras falla, el Pod no recibe tráfico. livenessProbe (sonda de vida): si falla repetidamente, el kubelet reinicia el contenedor. Ambas suponen que la aplicación expone un endpoint /salud.

Los comandos para actualizar y revertir:

kubectl set image deployment/tienda-api api=ghcr.io/ejemplo/tienda-api:1.1.0
kubectl rollout status deployment/tienda-api
kubectl rollout undo deployment/tienda-api

Service: una dirección estable

Los Pods son efímeros y cada uno tiene su propia IP, que cambia al reemplazarse. Un Service ofrece un punto de acceso estable y balancea la carga entre los Pods que coinciden con su selector.

Manifiesto service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: tienda-api
spec:
  type: ClusterIP
  selector:
    app: tienda-api
  ports:
    - name: http
      port: 80
      targetPort: 8000

port es el puerto del Service y targetPort el del contenedor. Dentro del clúster, otras aplicaciones lo alcanzan por el nombre DNS tienda-api. Solo reciben tráfico los Pods que están listos según la readinessProbe.

Tipo de ServiceAlcanceUso típico
ClusterIP (por defecto)Solo dentro del clústerComunicación entre servicios
NodePortUn puerto en cada nodoPruebas, o base de otros tipos
LoadBalancerBalanceador del proveedor de nubeExponer un servicio a Internet
ExternalNameAlias DNS a un nombre externoReferenciar un servicio fuera del clúster

Para HTTP/HTTPS con varias rutas o dominios sobre una sola IP pública se usa un Ingress (o su sucesor, la Gateway API).

Autoescalado: HorizontalPodAutoscaler

El HorizontalPodAutoscaler (HPA) cambia el número de réplicas de un Deployment según una métrica observada. Escalar horizontalmente es agregar más copias; escalar verticalmente sería dar más CPU o memoria a cada copia.

Manifiesto hpa.yaml:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: tienda-api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: tienda-api
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300

El HPA calcula periódicamente:

réplicas deseadas = ceil( réplicas actuales × métrica actual / métrica objetivo )

Ejemplo: con 3 réplicas al 140 % del CPU solicitado y objetivo de 70 %, resulta ceil(3 × 140 / 70) = 6 réplicas. Notas importantes:

  • Utilization se mide respecto a requests.cpu; por eso el Deployment debe declarar requests. Sin ellos, el HPA no puede calcular el porcentaje.
  • Requiere el Metrics Server instalado en el clúster para obtener las métricas.
  • stabilizationWindowSeconds: 300 evita «aleteo» (flapping): al bajar la demanda, espera 5 minutos antes de reducir réplicas.
  • minReplicas y maxReplicas acotan el comportamiento. Escalar réplicas solo sirve si hay nodos con espacio; el Cluster Autoscaler (o servicios equivalentes de la nube) agrega nodos, y es un componente distinto.
  • No escala a cero ni sirve para aplicaciones que no toleran varias copias en paralelo.

Autorreparación

Kubernetes aplica el bucle de reconciliación a varias situaciones sin intervención humana:

SituaciónQué hace
Un Pod se elimina o su nodo caeEl ReplicaSet crea un Pod de reemplazo en otro nodo
Un contenedor falla la livenessProbeEl kubelet lo reinicia
Un Pod no está listoEl Service deja de enviarle tráfico
Una versión nueva no pasa la readinessProbeLa actualización progresiva se detiene y las réplicas antiguas siguen sirviendo
Sube la demandaEl HPA aumenta las réplicas

Cuándo usar Kubernetes y cuándo no

SituaciónRecomendación
Una aplicación pequeña, un servidorNo: un contenedor con Docker Compose o un servicio gestionado basta
Pocos servicios, tráfico estableProbablemente no: el costo operativo supera el beneficio
Muchos servicios, varios equipos, tráfico variableSí: es el caso para el que se diseñó
Necesitas actualizaciones sin interrupción y recuperación automática a escalaSí
Quieres portabilidad entre proveedores de nubeSí, con la salvedad de que las partes específicas (balanceadores, almacenamiento) cambian

Kubernetes tiene una curva de aprendizaje alta y su operación (actualizaciones, seguridad, redes, costos) es un trabajo en sí misma. Existen servicios gestionados que operan el plano de control por ti (por ejemplo GKE, EKS, AKS); simplifican, pero no eliminan la necesidad de entender los objetos.

Buenas prácticas

  • Declara siempre requests y limits; sin ellos, el planificador y el HPA trabajan a ciegas.
  • Define readinessProbe y livenessProbe distintas en propósito: una decide si recibe tráfico, la otra si se reinicia.
  • Fija las imágenes a una versión concreta (o a su digest), nunca a latest.
  • Guarda los manifiestos en Git y aplícalos desde un pipeline (ver el artículo del día 7): es la base de GitOps.
  • Usa Namespaces y permisos mínimos (RBAC, control de acceso basado en roles).
  • No guardes datos importantes en el sistema de archivos de un Pod: se pierde al reemplazarse. Usa volúmenes persistentes o servicios externos.
  • Ejecuta los contenedores sin privilegios de administrador y con un sistema de archivos de solo lectura cuando sea posible.

Práctica: de los manifiestos a un clúster local

Importante sobre este artículo. Los tres manifiestos anteriores se validaron únicamente como YAML: un analizador confirmó que cada archivo es sintácticamente correcto y que declara Deployment (apps/v1), Service (v1) y HorizontalPodAutoscaler (autoscaling/v2). No se aplicaron a ningún clúster: en el entorno de redacción no había Kubernetes ni Docker instalados, así que su comportamiento real (arranque de Pods, escalado) no se ha observado. Los pasos siguientes siguen la documentación oficial; compruébalos tú en un clúster local de práctica.

Requisitos. Docker (o similar) y un clúster local. minikube o kind crean uno en tu equipo. Instalar Docker o minikube cambia tu sistema; hazlo siguiendo su documentación oficial.

1. Crea el clúster local.

minikube start
minikube addons enable metrics-server

El segundo comando instala el Metrics Server que necesita el HPA.

2. Sustituye la imagen de ejemplo por una real. Para probar solo el mecanismo, sirve cualquier imagen web (por ejemplo nginx) y ajusta containerPort, targetPort y las sondas (path: /, port: 80). Si usas la API del día 5, empaquétala y cárgala con minikube image load.

3. Aplica los manifiestos.

kubectl apply -f deployment.yaml -f service.yaml -f hpa.yaml

4. Observa el estado.

kubectl get deployments,pods,services,hpa
kubectl describe hpa tienda-api

Resultado esperado: 3 Pods en estado Running y READY 1/1.

5. Prueba la autorreparación. Elimina un Pod y observa cómo se crea otro:

kubectl delete pod <nombre-de-un-pod>
kubectl get pods --watch

6. Prueba el escalado. Genera carga contra el Service y observa cómo sube REPLICAS:

kubectl run carga --image=busybox:1.36 --restart=Never -- /bin/sh -c "while true; do wget -q -O- http://tienda-api; done"
kubectl get hpa --watch

Elimina el generador de carga al terminar: kubectl delete pod carga.

7. Actualiza y revierte con los comandos kubectl set image y kubectl rollout undo vistos arriba.

8. Limpia. kubectl delete -f deployment.yaml -f service.yaml -f hpa.yaml y minikube delete.

Preguntas frecuentes

¿Qué es Kubernetes en palabras simples?

Es un sistema que administra contenedores en un grupo de servidores (clúster): decide dónde corre cada uno, mantiene el número de copias que pediste y reemplaza las que fallan.

¿Qué diferencia hay entre Docker y Kubernetes?

Docker empaqueta y ejecuta contenedores. Kubernetes los orquesta a escala: reparte la carga entre servidores, escala el número de copias y hace actualizaciones sin cortar el servicio.

¿Qué es un pod en Kubernetes?

Es la unidad mínima que Kubernetes ejecuta: uno o varios contenedores que comparten red y almacenamiento. Normalmente se crean a través de un Deployment, no a mano.

¿Cuándo no conviene usar Kubernetes?

Cuando la aplicación es pequeña, el tráfico es estable o el equipo no puede dedicar tiempo a operarlo. En esos casos, un servicio administrado o un solo servidor con Docker suele bastar.

En el reel lo explicamos así

Piensa en el gerente de un restaurante. Tú le dices cuántos meseros quieres como mínimo; él se encarga del resto. Si el lugar se llena, llama a más meseros. Si uno falta, lo reemplaza sin que nadie se lo pida. Y cuando baja la clientela, deja ir a los que sobran. Kubernetes es ese gerente para tus contenedores: arranca los que faltan, reemplaza los que fallan y agrega más cuando sube la demanda.

Referencias

En el reel lo explicamos así

El gerente de un restaurante que llama a más meseros si se llena el lugar y reemplaza a quien falta, sin que nadie se lo pida.

Glosario: del inglés al español

Kubernetesdel griego «timonel»: dirige los contenedores
Clustergrupo de computadoras que trabajan juntas
Podla unidad mínima donde vive un contenedor
Autoscalingautoescalado