Saltar al contenido principal
55 min de lectura1 de agosto de 2026
KubernetesDevOpsContenedoresPodsOrquestación

Requisitos previos

  • docker

Lo que aprenderás

  • Entender la arquitectura de Kubernetes
  • Crear y gestionar Pods y Deployments
  • Exponer servicios con ClusterIP, NodePort y LoadBalancer
  • Configurar ConfigMaps y Secrets
  • Usar Ingress y kubectl apply -f
Tutorial55 min

Kubernetes: orquestación de contenedores

Docker ejecuta contenedores; Kubernetes los orquesta: escala, balancea tráfico, reinicia fallos y despliega versiones sin tiempo de inactividad. Aprende la arquitectura, los objetos principales y el flujo de trabajo con kubectl.


¿Qué es Kubernetes?

Kubernetes (k8s) es un orquestador de contenedores open-source nacido de la experiencia de Google con Borg. En lugar de que tú decidas dónde ejecutar cada contenedor, le declaras el estado deseado: "3 réplicas de mi-api con la imagen v1.2" — y el sistema trabaja continuamente para que la realidad coincida con esa declaración.

De ahí lo de declarativo: no le dices "crea esto", "borra aquello", sino cómo quieres que sea el sistema al final. Si un contenedor muere, k8s crea otro sin que lo pidas. Si pides 5 réplicas, ajusta hasta tener 5.

La comparación rápida: Docker Compose orquesta contenedores en una sola máquina con un YAML simple. Kubernetes orquesta un clúster de muchas máquinas (nodes) con auto-reparación, escalado automático, rolling updates y balanceo de red. Compose para desarrollo local, k8s para producción.

Arquitectura

Un clúster tiene dos planos. El control plane es el cerebro: toma decisiones globales. Los nodes (o workers) son las máquinas que ejecutan tus aplicaciones. El control plane suele ser el doble de disponible que los workers porque su caída paraliza el clúster.

Componentes del control plane

kube-apiserver: única puerta de entrada al clúster. Toda comunicación (kubectl, otros componentes) pasa por esta API REST.

etcd: base de datos clave-valor distribuida que guarda todo el estado del clúster. Lo que no está en etcd no existe.

kube-scheduler: decide en qué node colocar cada nuevo pod según recursos disponibles, afinidades y restricciones.

kube-controller-manager: ejecuta los controladores que reconcilian el estado deseado (replicas, deployments, jobs...).

Componentes del node

kubelet: agente que vive en cada node. Garantiza que los pods asignados estén corriendo y reporta su estado al apiserver.

kube-proxy: gestiona las reglas de red que enrutan el tráfico a los pods.

Container runtime: el motor que ejecuta contenedores (containerd, CRI-O). Docker no corre pods directamente en k8s moderno, pero las imágenes que construyes con Docker son las mismas que corre el runtime.

Instalar minikube y kubectl

Para practicar localmente necesitas un clúster de una sola máquina (minikube o kind) y el cliente kubectl:

# macOS con Homebrew
brew install minikube kubectl

# Arrancar el clúster local (descarga una imagen de VM la primera vez)
minikube start

# Verificar que kubectl habla con el clúster
kubectl cluster-info
kubectl get nodes
# NAME           STATUS   ROLES           AGE   VERSION
# minikube       Ready    control-plane   ...   v1.30.0
kubectl no es el clúster: es solo el cliente. Sin un clúster al que apuntar (minikube en local, EKS, GKE o K3s en la nube), los comandos fallan con "Unable to connect to the server". minikube start lo levanta en tu máquina.

Pods

El pod es la unidad mínima de computación en k8s: uno o más contenedores que comparten red y almacenamiento. En la práctica, casi siempre es un único contenedor por pod. Los pods son efímeros — no los crees a mano en producción; se gestionan con workloads:

# Crear un pod directamente (solo para probar)
kubectl run nginx --image=nginx:alpine

# Estado de los pods
kubectl get pods
kubectl get pods -o wide     # con el node y la IP

# Detalle completo en YAML
kubectl describe pod nginx

# Logs e inspección
kubectl logs nginx
kubectl exec -it nginx -- sh

# Acceder a un puerto del pod desde tu máquina (debug)
kubectl port-forward pod/nginx 8080:80
curl http://localhost:8080

kubectl delete pod nginx

kubectl run es cómodo para probar, pero los pods así creados no se auto-reparan: si mueren, nadie los recrea. Para eso existen los controllers, empezando por el Deployment.

Deployments

Un Deployment gestiona un conjunto de réplicas de un pod, declarando el estado deseado. Es el objeto que usarás el 90% del tiempo para aplicaciones stateless:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-api
  labels:
    app: mi-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mi-api
  template:
    metadata:
      labels:
        app: mi-api
    spec:
      containers:
        - name: mi-api
          image: miusuario/mi-api:v1
          ports:
            - containerPort: 3000
kubectl apply -f deployment.yaml
kubectl get deployments
kubectl get pods
# 3 pods con prefijo mi-api-...

# Escalar
kubectl scale deployment mi-api --replicas=5

# Desplegar una nueva imagen (rolling update)
kubectl set image deployment/mi-api mi-api=miusuario/mi-api:v2

# Ver el rollout y volver atrás si algo falla
kubectl rollout status deployment/mi-api
kubectl rollout history deployment/mi-api
kubectl rollout undo deployment/mi-api

El Deployment crea un ReplicaSet por versión: durante un update, el ReplicaSet nuevo va subiendo réplicas mientras el viejo las baja (rolling update). Si una revisión falla, rollout undo restaura la anterior.

El selector con matchLabels es la clave de todo: k8s relaciona objetos (Deployment ↔ pods ↔ Service) por etiquetas, no por nombre. Mantén las labels coherentes entre el selector del Deployment y las del pod template.

Services

Los pods son efímeros y cambian de IP cada vez que se recrean. Un Service da una dirección estable y balancea el tráfico entre los pods que coinciden con su selector:

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mi-api
spec:
  selector:
    app: mi-api
  ports:
    - port: 80          # puerto del Service
      targetPort: 3000  # puerto de los pods
  type: ClusterIP

Los tres tipos principales:

ClusterIP (por defecto): dirección interna estable, solo accesible dentro del clúster. Ideal para comunicación entre servicios internos.

NodePort: expone el Service en un puerto de cada node (30000-32767). Práctico en local o para tests.

LoadBalancer: provisiona un balanceador del cloud provider (ELB, LB de GCP...) que reparte tráfico externo. El camino estándar en la nube.

kubectl apply -f service.yaml

# DNS interno: los pods acceden a "mi-api" por nombre
kubectl run test --rm -it --image=busybox -- wget -qO- http://mi-api

# En minikube, exponer el Service al host
minikube service mi-api

ConfigMaps y Secrets

Separa la configuración de la imagen: los ConfigMaps guardan valores no sensibles y los Secrets (base64, no cifrado por defecto) los sensibles. Ambos se montan como variables de entorno o como archivos:

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mi-api-config
data:
  LOG_LEVEL: debug
  FEATURES: "notificaciones,analytics"

# secret.yaml — el valor está en base64
apiVersion: v1
kind: Secret
metadata:
  name: mi-api-secrets
type: Opaque
data:
  API_KEY: c2VjcmV0bzEyMw==  # "secreto123" en base64

# Referenciarlos en el Deployment
envFrom:
  - configMapRef:
      name: mi-api-config
  - secretRef:
      name: mi-api-secrets
env:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: mi-api-secrets
        key: API_KEY
Los Secrets solo codifican en base64: cualquiera con acceso al clúster puede decodificarlos. Para datos realmente sensibles usa el cifrado en reposo de etcd, un secret manager externo (SOPS, Sealed Secrets, Vault) o las integraciones del cloud provider.

Ingress

Un LoadBalancer por Service multiplica costes y URLs. El Ingress es un punto de entrada único que enruta por host y por path hacia distintos Services, con TLS terminado en el borde:

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mi-ingress
spec:
  rules:
    - host: api.midominio.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: mi-api
                port:
                  number: 80
          - path: /web
            pathType: Prefix
            backend:
              service:
                name: frontend
                port:
                  number: 80
    - host: admin.midominio.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: admin-panel
                port:
                  number: 80

El Ingress requiere un Ingress Controller instalado en el clúster (nginx-ingress, Traefik, o el del cloud provider) que materialice esas reglas en un proxy real. En minikube:

minikube addons enable ingress
kubectl apply -f ingress.yaml
kubectl get ingress

Namespaces y apply -f

Los Namespaces particionan el clúster por equipos, entornos o aplicaciones, con límites de recursos y políticas de red propios. Trabajar siempre en default es una mala práctica que tarde o temprano pica:

kubectl create namespace produccion
kubectl get namespaces

# Operar sobre un namespace concreto
kubectl -n produccion get pods

# Aplicar todo un directorio de manifiestos
kubectl apply -f ./k8s/
kubectl apply -n produccion -f deployment.yaml

# Navegar el clúster
kubectl get all
kubectl get events --sort-by=.lastTimestamp
kubectl apply -f es idempotente: puedes ejecutarlo mil veces y el resultado es el mismo. Eso convierte los manifiestos YAML en tu única fuente de verdad y permite versionarlos en Git (GitOps): el clúster se describe en el repositorio, no se toca a mano.

Ejercicios


Kubernetes cambia la mentalidad: dejas de gestionar procesos y empiezas a declarar estados que el sistema reconcilia por sí solo. Con pods, deployments, services, configmaps e ingress ya tienes el vocabulario esencial para trabajar con cualquier clúster — el paso siguiente es GitOps: describir ese estado en Git y dejar que el clúster lo aplique.

Siguiente lección

Monitoreo y observabilidad

Implementa observabilidad completa: métricas con Prometheus, dashboards en Grafana, logging con Loki y trazas distribuidas con OpenTelemetry + Jaeger. Configura alerting con Alertmanager.