Saltar al contenido

Pods y Deployments de Kubernetes: Una introducción práctica

Una guía práctica sobre Pods, Deployments y Services de Kubernetes que cubre las abstracciones básicas necesarias para ejecutar cargas de trabajo en producción.

5 min de lectura
Diagrama de un clúster de Kubernetes que muestra pods, deployments y services

La documentación de Kubernetes es inmensa. La superficie de su API es enorme. Los ingenieros que recién empiezan se quedan mirando un muro de YAML sin saber por dónde empezar. La respuesta son tres primitivas: Pods, Deployments y Services. Estas tres abstracciones cubren el 90 % de lo que necesitas para ejecutar una aplicación web en producción.

Esta guía explica qué hace cada primitiva, cómo interactúan entre sí y qué configuraciones realmente importan en cargas de trabajo reales.

Pods: la unidad más pequeña

Un Pod es uno o más contenedores que se ejecutan juntos en el mismo nodo, compartiendo red y almacenamiento. La mayoría de los Pods ejecutan un único contenedor. El patrón multicontenedor se usa para sidecars: un agente de logging, un proxy o un init container que realiza tareas de configuración antes de que arranque el proceso principal.

ymlyaml
# A minimal Pod definition — rarely used directly in production
apiVersion: v1
kind: Pod
metadata:
  name: api-server
  labels:
    app: api
    version: v1
spec:
  containers:
    - name: api
      image: myregistry/api-server:1.4.2
      ports:
        - containerPort: 3000
      env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
      resources:
        requests:
          memory: "128Mi"
          cpu: "100m"
        limits:
          memory: "256Mi"
          cpu: "500m"

Casi nunca se crean Pods directamente. Si un Pod muere, nada lo reinicia. Los Deployments gestionan el ciclo de vida de los Pods, por eso son la verdadera unidad de trabajo en producción.

Deployments: gestión del ciclo de vida de los Pods

Un Deployment declara el estado deseado: qué imagen de contenedor usar, cuántas réplicas mantener y cómo desplegar las actualizaciones. El controlador del Deployment reconcilia continuamente la realidad con ese estado deseado.

ymlyaml
# ❌ Running pods directly — no restart, no scaling, no rollback
apiVersion: v1
kind: Pod
metadata:
  name: api-server-1
spec:
  containers:
    - name: api
      image: myregistry/api-server:1.4.2
---
apiVersion: v1
kind: Pod
metadata:
  name: api-server-2
spec:
  containers:
    - name: api
      image: myregistry/api-server:1.4.2
ymlyaml
# ✅ A Deployment manages pods with automatic restart, scaling, and rollback
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
  labels:
    app: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: api
        version: v1
    spec:
      containers:
        - name: api
          image: myregistry/api-server:1.4.2
          ports:
            - containerPort: 3000
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 20

Campos clave de este Deployment:

  • replicas: 3: ejecuta tres instancias del Pod
  • maxSurge: 1: durante las actualizaciones, permite un Pod adicional por encima de la cantidad deseada
  • maxUnavailable: 0: nunca cae por debajo de la cantidad de réplicas deseada durante las actualizaciones
  • readinessProbe: indica a Kubernetes cuándo el Pod puede aceptar tráfico
  • livenessProbe: indica a Kubernetes cuándo el Pod está bloqueado y debe reiniciarse

Actualizaciones progresivas y reversiones

Los Deployments gestionan las actualizaciones reemplazando gradualmente los Pods antiguos por otros nuevos. Los parámetros maxSurge y maxUnavailable controlan la velocidad de la actualización y la disponibilidad durante el proceso.

shbash
# Update the image — triggers a rolling update
kubectl set image deployment/api-server \
  api=myregistry/api-server:1.5.0
 
# Watch the rollout progress
kubectl rollout status deployment/api-server
 
# Check rollout history
kubectl rollout history deployment/api-server
 
# Rollback to the previous version
kubectl rollout undo deployment/api-server
 
# Rollback to a specific revision
kubectl rollout undo deployment/api-server --to-revision=3

El proceso de actualización progresiva con maxSurge: 1, maxUnavailable: 0 y 3 réplicas funciona así:

Step 1: [v1] [v1] [v1] [v2]     ← new Pod created (4 total)
Step 2: [v1] [v1] [v2] [v2]     ← old Pod removed, new created
Step 3: [v1] [v2] [v2] [v2]     ← continuing replacement
Step 4: [v2] [v2] [v2]          ← rollout complete (3 total)

El tráfico solo se enruta hacia los Pods que superan la readiness probe, de modo que los usuarios nunca llegan a un Pod que todavía está iniciando.

Services: cómo exponer los Pods

Los Pods reciben direcciones IP efímeras que cambian con cada reinicio. Un Service proporciona una identidad de red estable: un nombre DNS y una IP fijos que enrutan el tráfico hacia los Pods saludables que coinciden con un selector de etiquetas.

ymlyaml
# ClusterIP Service — internal access only
apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  type: ClusterIP
  selector:
    app: api
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
---
# Other pods can now reach the API at http://api-service:80
# Kubernetes DNS resolves api-service to the ClusterIP
ymlyaml
# LoadBalancer Service — external access via cloud load balancer
apiVersion: v1
kind: Service
metadata:
  name: api-external
spec:
  type: LoadBalancer
  selector:
    app: api
  ports:
    - protocol: TCP
      port: 443
      targetPort: 3000

Los distintos tipos de Service cumplen propósitos diferentes:

  • ClusterIP: solo acceso interno. Para que los microservicios se comuniquen entre sí.
  • NodePort: expone el servicio en la IP de cada nodo a través de un puerto estático. Útil para desarrollo.
  • LoadBalancer: aprovisiona un balanceador de carga en la nube. El estándar para servicios de cara al público.

Requests y limits de recursos

Sin una configuración de recursos, un solo Pod puede consumir todos los recursos del nodo y dejar sin recursos a las demás cargas de trabajo. Los requests garantizan una asignación mínima. Los limits establecen el máximo permitido.

ymlyaml
resources:
  # Requests: minimum guaranteed resources
  # Used by scheduler to place pods on nodes
  requests:
    memory: "128Mi"
    cpu: "100m"    # 100 millicores = 0.1 CPU cores
 
  # Limits: maximum allowed resources
  # Pod is killed (OOMKill) if it exceeds memory limit
  # Pod is throttled if it exceeds CPU limit
  limits:
    memory: "256Mi"
    cpu: "500m"    # 500 millicores = 0.5 CPU cores
shbash
# Check actual resource usage vs. requests/limits
kubectl top pods -n default
 
# Example output:
# NAME                          CPU(cores)   MEMORY(bytes)
# api-server-7d5f8b6c4-abc12   45m          98Mi
# api-server-7d5f8b6c4-def34   52m          102Mi
# api-server-7d5f8b6c4-ghi56   38m          95Mi

Configurar requests demasiado altos desperdicia recursos del clúster. Configurarlos demasiado bajos provoca fallos de scheduling cuando los nodos parecen estar llenos. Empieza con requests basados en el uso observado (con kubectl top) y limits equivalentes al doble del request.

Health checks que realmente funcionan

Las readiness probes y las liveness probes son las configuraciones que más se configuran mal. Una mala configuración de los probes provoca fallos en cascada.

ymlyaml
# ❌ Common mistake: same endpoint, same timing for both probes
readinessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 5
livenessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 5
ymlyaml
# ✅ Different timing, different thresholds
readinessProbe:
  httpGet:
    path: /health/ready
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10
  failureThreshold: 3
  # Pod removed from Service after 3 consecutive failures (30s)
 
livenessProbe:
  httpGet:
    path: /health/live
    port: 3000
  initialDelaySeconds: 30
  periodSeconds: 20
  failureThreshold: 5
  # Pod restarted after 5 consecutive failures (100s)
 
startupProbe:
  httpGet:
    path: /health/live
    port: 3000
  failureThreshold: 30
  periodSeconds: 10
  # Allows up to 300s for slow-starting apps

La liveness probe debería tener un initialDelaySeconds más largo y un failureThreshold más alto que la readiness probe. Una liveness probe demasiado agresiva reinicia Pods que solo están sobrecargados temporalmente, lo que genera un bucle de reinicios que empeora la interrupción.

El endpoint /health/ready de la readiness probe debería verificar las dependencias externas (conexión a la base de datos, disponibilidad de la caché). El endpoint /health/live de la liveness probe debería ser una simple comprobación de que "el proceso sigue vivo": nunca incluyas verificaciones de dependencias en las liveness probes.

ConfigMaps y Secrets

La configuración y los secretos deben vivir fuera de la imagen del contenedor, gestionados como recursos de Kubernetes.

ymlyaml
# ConfigMap for non-sensitive configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  LOG_LEVEL: "info"
  CACHE_TTL: "300"
  FEATURE_NEW_UI: "true"
---
# Secret for sensitive values (base64 encoded)
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  url: cG9zdGdyZXM6Ly91c2VyOnBhc3NAZGIuZXhhbXBsZS5jb206NTQzMi9teWRi
  password: c3VwZXJzZWNyZXQ=
ymlyaml
# Reference in Deployment
spec:
  containers:
    - name: api
      image: myregistry/api-server:1.4.2
      envFrom:
        - configMapRef:
            name: api-config
      env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url

Los ConfigMaps permiten actualizar la configuración sin reconstruir las imágenes del contenedor. Modificar un ConfigMap y reiniciar los Pods aplica un cambio de configuración en cuestión de segundos.

Puntos clave

  1. Nunca ejecutes Pods sueltos: usa siempre Deployments para el reinicio automático y la gestión de despliegues
  2. Define los resource requests según el uso observado: kubectl top da cifras reales, no estimaciones
  3. Separa las readiness probes de las liveness probes: endpoints distintos, umbrales distintos, propósitos distintos
  4. Usa actualizaciones progresivas con maxUnavailable: 0: mantén la capacidad completa durante los despliegues
  5. Empieza con services de tipo ClusterIP: expón externamente solo lo que realmente necesita acceso externo
  6. Guarda los secretos en Secrets y la configuración en ConfigMaps: nunca incrustes valores específicos de cada entorno en las imágenes
Wilfredo Rujel

Wilfredo Rujel

Ingeniero de Software Full Stack

Compartir esta publicaciónX