Kubernetes asusta. Tiene una curva de aprendizaje pronunciada, una terminologia propia y una cantidad de conceptos nuevos que pueden abrumar a cualquiera. Pero si ya tienes Docker bajo control y quieres dar el salto al despliegue de aplicaciones en produccion a escala, Kubernetes es el estandar de facto. Esta guia te explica Kubernetes desde la perspectiva de un desarrollador web, sin rodeos y con ejemplos reales.
Que es Kubernetes y cuando lo necesitas?
Kubernetes (tambien llamado K8s) es un sistema de orquestacion de contenedores de codigo abierto. Automatiza el despliegue, el escalado y la gestion de aplicaciones en contenedores. Si tienes una aplicacion en Docker funcionando en un servidor, Kubernetes te permite llevarla al siguiente nivel: multiples instancias de tu aplicacion ejecutandose en varios servidores, con balanceo de carga automatico, recuperacion ante fallos, actualizaciones sin downtime y escalado automatico.
La pregunta honesta es: cuando lo necesitas? Si tu aplicacion sirve a miles o millones de usuarios y necesitas alta disponibilidad, escalado automatico y despliegues sin downtime, Kubernetes tiene sentido. Para proyectos pequeños o medianos, un servidor con Docker Compose o un PaaS como Railway, Render o Fly.io es suficiente y mucho mas simple. No uses Kubernetes por moda.
Los conceptos clave de Kubernetes
Antes de escribir una sola linea de configuracion, necesitas entender los objetos fundamentales de Kubernetes. Todo en K8s se describe en archivos YAML y se gestiona a traves de la API.
Un Cluster es el conjunto de maquinas (nodos) que ejecutan tus aplicaciones. Hay nodos de control (control plane) que gestionan el cluster y nodos worker donde se ejecutan las aplicaciones. Un Pod es la unidad minima de despliegue en Kubernetes: uno o mas contenedores que comparten red y almacenamiento. Un Deployment gestiona un conjunto de pods identicos, asegurando que siempre haya el numero deseado en ejecucion. Un Service expone un grupo de pods como un endpoint de red estable. Un Ingress gestiona el acceso HTTP externo al cluster.
Instalar kubectl y configurar un cluster local
# Instalar kubectl (CLI de Kubernetes)
# Linux
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl && sudo mv kubectl /usr/local/bin/
# macOS
brew install kubectl
# Verificar
kubectl version --client
# Cluster local para desarrollo: kind (Kubernetes in Docker)
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.26.0/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/
# Crear cluster de desarrollo
kind create cluster --name mi-cluster
# O usar minikube
minikube start --driver=docker --cpus=4 --memory=4g
# Verificar
kubectl cluster-info
kubectl get nodes
Tu primera aplicacion en Kubernetes: el Deployment
Vamos a desplegar una API Node.js real. El Deployment es el objeto mas importante: describe cuantas replicas quieres, que imagen usar, los recursos de CPU/memoria, las health checks y la estrategia de actualizacion.
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mi-api
spec:
replicas: 3
selector:
matchLabels:
app: mi-api
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
app: mi-api
spec:
containers:
- name: mi-api
image: mi-usuario/mi-api:v1.0.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DB_URL
valueFrom:
secretKeyRef:
name: mi-api-secrets
key: database-url
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
Service, ConfigMap y Secret
# service.yaml - Expone los pods internamente en el cluster
apiVersion: v1
kind: Service
metadata:
name: mi-api-service
spec:
selector:
app: mi-api
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: ClusterIP
---
# configmap.yaml - Variables de entorno no sensibles
apiVersion: v1
kind: ConfigMap
metadata:
name: mi-api-config
data:
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
---
# secret.yaml - Datos sensibles en base64
apiVersion: v1
kind: Secret
metadata:
name: mi-api-secrets
type: Opaque
data:
database-url: cG9zdGdyZXNxbDovL3VzdWFyaW86cGFzc3dvcmRAZGI6NTQzMi9taWJk
# Comandos esenciales de kubectl
kubectl apply -f ./k8s/ # Aplicar todos los manifiestos del directorio
kubectl get pods # Ver estado de los pods
kubectl get pods -w # Watch: actualizacion en tiempo real
kubectl logs mi-api-abc123 -f # Logs en tiempo real
kubectl exec -it mi-api-abc123 -- /bin/sh # Entrar al contenedor
kubectl describe pod mi-api-abc123 # Detalles y eventos del pod
kubectl rollout status deployment/mi-api # Estado del despliegue
kubectl rollout undo deployment/mi-api # Rollback al anterior
Ingress: exponer la aplicacion a Internet
Un Service ClusterIP solo es accesible dentro del cluster. Para exponer tu aplicacion a Internet con un dominio y HTTPS, necesitas un Ingress Controller (generalmente NGINX) y un objeto Ingress.
# Instalar NGINX Ingress Controller con Helm
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx --create-namespace
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mi-api-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.miapp.com
secretName: mi-api-tls
rules:
- host: api.miapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: mi-api-service
port:
number: 80
Escalado automatico con HPA
El Horizontal Pod Autoscaler (HPA) escala automaticamente el numero de pods segun la carga de CPU o memoria. Es una de las caracteristicas mas potentes de Kubernetes para manejar picos de trafico.
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: mi-api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mi-api
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
# Ver estado
kubectl get hpa
kubectl describe hpa mi-api-hpa
Helm: el gestor de paquetes de Kubernetes
Helm te permite instalar aplicaciones complejas en Kubernetes con un solo comando, como si fuera un apt o npm pero para clusters. Es especialmente util para instalar componentes de infraestructura como bases de datos, message queues o monitoring.
# Instalar Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Instalar PostgreSQL en el cluster
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install mi-postgres bitnami/postgresql \
--set auth.postgresPassword=mipassword \
--set auth.database=miapp \
--set primary.persistence.size=20Gi \
-n produccion
# Instalar Redis
helm install mi-redis bitnami/redis \
--set auth.password=redispassword \
-n produccion
# Ver releases
helm list -A
# Actualizar
helm upgrade mi-postgres bitnami/postgresql -n produccion
# Desinstalar
helm uninstall mi-postgres -n produccion
Namespaces y organizacion del cluster
# Crear namespaces para separar entornos
kubectl create namespace produccion
kubectl create namespace staging
kubectl create namespace monitoreo
# Desplegar en namespace especifico
kubectl apply -f deployment.yaml -n produccion
kubectl get pods -n produccion
kubectl get pods --all-namespaces
# Configurar namespace por defecto
kubectl config set-context --current --namespace=produccion
# ResourceQuota: limitar recursos por namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: produccion-quota
namespace: produccion
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
pods: "50"
CI/CD con Kubernetes: despliegues automaticos desde GitHub Actions
# .github/workflows/deploy.yml
name: Build y Deploy a K8s
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build y push imagen Docker
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USER }}" --password-stdin
docker build -t ${{ secrets.DOCKER_USER }}/mi-api:${{ github.sha }} .
docker push ${{ secrets.DOCKER_USER }}/mi-api:${{ github.sha }}
- name: Configurar kubectl
uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Actualizar imagen
run: |
kubectl set image deployment/mi-api \
mi-api=${{ secrets.DOCKER_USER }}/mi-api:${{ github.sha }} \
-n produccion
kubectl rollout status deployment/mi-api -n produccion --timeout=5m
- name: Rollback si falla
if: failure()
run: kubectl rollout undo deployment/mi-api -n produccion
Monitoreo y observabilidad
Un cluster de Kubernetes sin monitoreo es un cluster ciego. El stack mas popular para monitoreo es Prometheus + Grafana, instalable con el kube-prometheus-stack de Helm. Para logs, Loki es la solucion mas ligera y bien integrada con Grafana.
# Instalar Prometheus + Grafana
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install kube-prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoreo \
--create-namespace
# Acceder a Grafana localmente
kubectl port-forward svc/kube-prometheus-grafana 3000:80 -n monitoreo
# Usuario: admin, Password: prom-operator
# Ver metricas de pods
kubectl top pods
kubectl top nodes
Kubernetes es una tecnologia que requiere inversion real en aprendizaje, pero que ofrece una plataforma de despliegue increiblemente robusta. El mejor consejo para aprenderlo es usar kind o minikube localmente y desplegar tus propios proyectos en el. La teoria sin practica no lleva a ninguna parte con K8s. Empieza con un Deployment, un Service y un Ingress, y ve añadiendo complejidad poco a poco.