Kubernetes se ha convertido en el estándar de la industria para la orquestación de contenedores, impulsando desde pequeñas startups hasta las plataformas nativas de nube más grandes del planeta. Si estás gestionando contenedores con Docker o Docker Compose y has llegado al punto donde necesitas escalado automático, auto-reparación, actualizaciones progresivas y despliegues en múltiples nodos, Kubernetes es el siguiente paso. Esta guía te lleva desde cero hasta un clúster de Kubernetes funcional usando kubeadm en Ubuntu Server, cubriendo la arquitectura, instalación, primer despliegue, exposición de servicios y comandos de gestión diaria.
Requisitos Previos
Antes de comenzar, asegúrate de tener:
- Dos o más máquinas con Ubuntu Server 22.04 o 24.04 (físicas o virtuales)
- Cada máquina con al menos 2 GB de RAM y 2 CPUs
- Conectividad de red completa entre todas las máquinas (red privada recomendada)
- Hostname, dirección MAC y product_uuid únicos en cada nodo
- Acceso a terminal con privilegios sudo en todos los nodos
- Swap deshabilitado en todos los nodos
¿Qué es Kubernetes?
Kubernetes (frecuentemente abreviado como k8s) es una plataforma de orquestación de contenedores de código abierto, originalmente diseñada por Google y ahora mantenida por la Cloud Native Computing Foundation (CNCF). Automatiza el despliegue, escalado y gestión de aplicaciones en contenedores.
En esencia, Kubernetes resuelve un problema fundamental: cuando tienes decenas o cientos de contenedores ejecutándose en múltiples servidores, necesitas un sistema que decida dónde se ejecuta cada contenedor, reinicie los contenedores que fallan, los escale hacia arriba o abajo según la demanda, y gestione la red entre ellos. Kubernetes hace todo esto de forma declarativa — describes el estado deseado de tu aplicación, y Kubernetes trabaja continuamente para que el estado actual coincida.
Conceptos clave:
- Pod — la unidad desplegable más pequeña, envolviendo uno o más contenedores
- Service — un endpoint de red estable que enruta tráfico a un conjunto de Pods
- Deployment — una forma declarativa de gestionar Pods con réplicas y actualizaciones progresivas
- Namespace — una partición lógica para aislar recursos dentro de un clúster
- Node — una máquina física o virtual en el clúster
Arquitectura de Kubernetes
Un clúster de Kubernetes consta de dos tipos de nodos:
Plano de Control (Nodo Maestro)
El plano de control gestiona el estado general del clúster y toma decisiones de programación. Sus componentes incluyen:
- kube-apiserver — la puerta principal del clúster; toda la comunicación pasa por el servidor API
- etcd — un almacén distribuido de clave-valor que contiene toda la configuración y estado del clúster
- kube-scheduler — asigna Pods a nodos basándose en requisitos y restricciones de recursos
- kube-controller-manager — ejecuta controladores que manejan tareas rutinarias como mantener el número correcto de réplicas de Pods
- cloud-controller-manager — se integra con APIs de proveedores de nube (opcional, para despliegues en la nube)
Nodos Worker
Los nodos worker ejecutan tus contenedores de aplicación. Cada worker ejecuta:
- kubelet — el agente que se comunica con el plano de control y gestiona Pods en el nodo
- kube-proxy — maneja las reglas de red para que los Pods puedan comunicarse entre sí y con tráfico externo
- Runtime de contenedores — el software que realmente ejecuta los contenedores (containerd es el estándar)
┌─────────────────────────────────────────┐
│ Nodo del Plano de Control │
│ ┌───────────┐ ┌──────────────────┐ │
│ │ API Server│ │ Controller Mgr │ │
│ └───────────┘ └──────────────────┘ │
│ ┌───────────┐ ┌──────────────────┐ │
│ │ etcd │ │ Scheduler │ │
│ └───────────┘ └──────────────────┘ │
└─────────────────────────────────────────┘
│ │
┌────┴────┐ ┌───┴─────┐
│ Worker 1│ │ Worker 2│
│ kubelet │ │ kubelet │
│ kube- │ │ kube- │
│ proxy │ │ proxy │
│ runtime │ │ runtime │
└─────────┘ └─────────┘
Preparación de Todos los Nodos
Los siguientes pasos deben realizarse en cada nodo del clúster (plano de control y workers por igual).
Deshabilitar Swap
Kubernetes requiere que swap esté deshabilitado. El kubelet no arrancará si swap está activo:
# Deshabilitar swap inmediatamente
sudo swapoff -a
# Eliminar entradas de swap de fstab para persistir entre reinicios
sudo sed -i '/ swap / s/^/#/' /etc/fstab
# Verificar que swap está desactivado
free -h
Cargar Módulos del Kernel Requeridos
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
Configurar Parámetros sysctl
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# Aplicar sin reiniciar
sudo sysctl --system
Verifica que los ajustes se aplicaron:
sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward
Los tres valores deben devolver 1.
Instalación del Runtime de Contenedores (containerd)
Kubernetes necesita un runtime de contenedores que implemente la Interfaz de Runtime de Contenedores (CRI). containerd es el estándar de la industria y la opción recomendada:
# Instalar containerd
sudo apt update
sudo apt install -y containerd
# Generar la configuración predeterminada
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# Habilitar SystemdCgroup (requerido para kubeadm)
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# Reiniciar y habilitar containerd
sudo systemctl restart containerd
sudo systemctl enable containerd
Verifica que containerd está ejecutándose:
sudo systemctl status containerd
La salida debe mostrar active (running).
Instalación de kubeadm, kubelet y kubectl
Estas tres herramientas son la base de un clúster basado en kubeadm:
- kubeadm — inicializa el clúster
- kubelet — se ejecuta en cada nodo y gestiona Pods
- kubectl — la herramienta de línea de comandos para interactuar con el clúster
# Instalar paquetes requeridos
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl gpg
# Agregar la clave de firma del repositorio APT de Kubernetes
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# Agregar el repositorio APT de Kubernetes
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
# Instalar los paquetes
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
# Prevenir actualizaciones automáticas (crítico para la estabilidad del clúster)
sudo apt-mark hold kubelet kubeadm kubectl
Verifica la instalación:
kubeadm version
kubectl version --client
Inicialización del Plano de Control
Ejecuta el siguiente comando solo en el nodo del plano de control:
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
El flag --pod-network-cidr especifica el rango de IPs para los Pods. El valor 10.244.0.0/16 es el predeterminado para Flannel. Si planeas usar Calico, usa 192.168.0.0/16 en su lugar.
Después de que la inicialización se complete, verás una salida similar a:
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:abc123...
Guarda el comando kubeadm join — lo necesitarás para agregar nodos worker.
Configura kubectl para tu usuario:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Verifica que el plano de control está ejecutándose:
kubectl get nodes
La salida mostrará el nodo del plano de control con estado NotReady — esto es esperado hasta que instales un complemento de red para Pods.
Instalación de un Complemento de Red para Pods
Los Pods necesitan un plugin de red (CNI — Container Network Interface) para comunicarse entre sí a través de los nodos. Sin él, el clúster no es funcional.
Opción A: Instalar Flannel
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
Opción B: Instalar Calico
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
Espera a que todos los Pods del sistema estén listos:
kubectl get pods -n kube-system
Después de uno o dos minutos, todos los Pods deben mostrar estado Running. Ahora verifica el estado del nodo nuevamente:
kubectl get nodes
El nodo del plano de control ahora debe mostrar Ready.
Unir Nodos Worker
En cada nodo worker, ejecuta el comando kubeadm join que guardaste de la inicialización del plano de control:
sudo kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:abc123...
Si perdiste el comando de unión, puedes regenerarlo en el plano de control:
kubeadm token create --print-join-command
Después de unirse, verifica todos los nodos desde el plano de control:
kubectl get nodes
Salida esperada:
NAME STATUS ROLES AGE VERSION
control-plane Ready control-plane 10m v1.31.0
worker-01 Ready <none> 2m v1.31.0
worker-02 Ready <none> 1m v1.31.0
Para etiquetar tus nodos worker para mejor identificación:
kubectl label node worker-01 node-role.kubernetes.io/worker=worker
kubectl label node worker-02 node-role.kubernetes.io/worker=worker
Desplegando Tu Primera Aplicación
Ahora que el clúster está funcionando, despleguemos un servidor web nginx simple.
Crear un Deployment
kubectl create deployment nginx --image=nginx:latest --replicas=3
Esto le dice a Kubernetes: “Quiero 3 instancias de nginx ejecutándose en todo momento.” Kubernetes programará Pods a través de tus nodos worker.
Verifica el deployment:
kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 3/3 3 3 30s
Ver los Pods individuales:
kubectl get pods -o wide
El flag -o wide muestra en qué nodo se ejecuta cada Pod.
Usando un Manifiesto YAML
Para cargas de trabajo en producción, debes definir los recursos en archivos YAML. Crea un archivo llamado nginx-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Aplícalo:
kubectl apply -f nginx-deployment.yaml
Exposición de Servicios
Los Pods son efímeros y obtienen nuevas direcciones IP cuando se reinician. Los Servicios proporcionan un endpoint estable para alcanzar tu aplicación.
ClusterIP (Solo Interno)
El tipo de Service predeterminado. Accesible solo desde dentro del clúster:
kubectl expose deployment nginx-deployment --port=80 --target-port=80 --type=ClusterIP
kubectl get services
NodePort (Acceso Externo vía IP del Nodo)
Expone el Service en un puerto estático en la IP de cada nodo:
kubectl expose deployment nginx-deployment --port=80 --target-port=80 --type=NodePort --name=nginx-nodeport
kubectl get services nginx-nodeport
Ahora puedes acceder a la aplicación en http://<ip-de-cualquier-nodo>:31234.
LoadBalancer (Entornos en la Nube)
En entornos de nube (AWS, GCP, Azure), el tipo LoadBalancer aprovisiona un balanceador de carga externo automáticamente:
kubectl expose deployment nginx-deployment --port=80 --target-port=80 --type=LoadBalancer --name=nginx-lb
Para clústeres en bare-metal, puedes usar MetalLB para proporcionar funcionalidad de LoadBalancer.
Manifiesto YAML de Service
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080
type: NodePort
Escalado y Actualizaciones Progresivas
Escalar Réplicas
# Escalar a 5 réplicas
kubectl scale deployment nginx-deployment --replicas=5
# Reducir a 2 réplicas
kubectl scale deployment nginx-deployment --replicas=2
# Verificar
kubectl get deployment nginx-deployment
Actualizaciones Progresivas
Actualiza la imagen del contenedor sin tiempo de inactividad:
# Actualizar la imagen
kubectl set image deployment/nginx-deployment nginx=nginx:1.27-alpine
# Observar el progreso del despliegue
kubectl rollout status deployment/nginx-deployment
Kubernetes reemplazará gradualmente los Pods antiguos con nuevos, asegurando que la aplicación permanezca disponible durante toda la actualización.
Reversión
Si algo sale mal:
# Ver historial de despliegues
kubectl rollout history deployment/nginx-deployment
# Revertir a la versión anterior
kubectl rollout undo deployment/nginx-deployment
# Revertir a una revisión específica
kubectl rollout undo deployment/nginx-deployment --to-revision=2
Inspección y Depuración de Cargas de Trabajo
Ver Logs de Pods
# Logs de un Pod específico
kubectl logs nginx-deployment-abc123
# Seguir logs en tiempo real
kubectl logs -f nginx-deployment-abc123
# Logs de todos los Pods en un Deployment
kubectl logs -l app=nginx
Ejecutar Comandos en un Pod
# Abrir una shell dentro de un Pod en ejecución
kubectl exec -it nginx-deployment-abc123 -- /bin/bash
# Ejecutar un solo comando
kubectl exec nginx-deployment-abc123 -- cat /etc/nginx/nginx.conf
Describir Recursos
# Información detallada sobre un Pod
kubectl describe pod nginx-deployment-abc123
# Información detallada sobre un nodo
kubectl describe node worker-01
Solución de Problemas
Pods Atascados en Estado Pending
Esto típicamente significa que el scheduler no puede encontrar un nodo con recursos suficientes:
kubectl describe pod <nombre-del-pod>
Mira la sección Events al final. Causas comunes:
- CPU o memoria insuficiente en los nodos worker
- Taints de nodo previniendo la programación
- Reglas de afinidad de Pod que no se pueden satisfacer
Pods en CrashLoopBackOff
El contenedor inicia pero se cierra inmediatamente:
# Verificar los logs
kubectl logs <nombre-del-pod>
kubectl logs <nombre-del-pod> --previous
# Verificar eventos
kubectl describe pod <nombre-del-pod>
Causas comunes:
- Error de aplicación o mala configuración
- Variables de entorno o config maps faltantes
- Probes de readiness o liveness fallando
Nodos Muestran NotReady
# Verificar condiciones del nodo
kubectl describe node <nombre-del-nodo>
# En el nodo afectado, verificar logs del kubelet
sudo journalctl -u kubelet -f
Causas comunes:
- Plugin CNI no instalado o fallando
- kubelet no puede alcanzar el servidor API
- Presión de disco, presión de memoria o presión de PID
No Se Puede Alcanzar Servicios
# Verificar endpoints del Service
kubectl get endpoints <nombre-del-servicio>
# Probar conectividad desde dentro del clúster
kubectl run debug --image=busybox --rm -it -- wget -qO- http://<nombre-del-servicio>
Regenerar Tokens de Unión
Si el token de unión ha expirado (los tokens son válidos por 24 horas por defecto):
kubeadm token create --print-join-command
Referencia de Comandos kubectl
| Comando | Descripción |
|---|---|
kubectl get nodes | Listar todos los nodos del clúster |
kubectl get pods | Listar Pods en el namespace actual |
kubectl get pods -A | Listar Pods en todos los namespaces |
kubectl get services | Listar todos los Services |
kubectl get deployments | Listar todos los Deployments |
kubectl describe pod <nombre> | Mostrar información detallada del Pod |
kubectl logs <pod> | Ver logs del Pod |
kubectl exec -it <pod> -- /bin/bash | Abrir shell en un Pod |
kubectl apply -f <archivo> | Aplicar un manifiesto YAML |
kubectl delete -f <archivo> | Eliminar recursos de un manifiesto YAML |
kubectl scale deployment <nombre> --replicas=N | Escalar un Deployment |
kubectl rollout status deployment/<nombre> | Verificar progreso del despliegue |
kubectl rollout undo deployment/<nombre> | Revertir un Deployment |
kubectl top nodes | Mostrar uso de recursos de nodos |
kubectl top pods | Mostrar uso de recursos de Pods |
kubectl config get-contexts | Listar contextos de clúster disponibles |
kubectl cluster-info | Mostrar información del endpoint del clúster |
Resumen
Ahora tienes un clúster de Kubernetes funcional desplegado con kubeadm, completo con un plano de control, nodos worker, un plugin de red CNI y tu primera aplicación ejecutándose con servicios expuestos. Esta es la misma arquitectura fundamental utilizada en entornos de producción de Kubernetes en todo el mundo.
Puntos clave:
- Kubernetes orquesta contenedores a través de múltiples nodos con programación, escalado y auto-reparación automatizados
- El plano de control (API server, etcd, scheduler, controller manager) gestiona el estado del clúster mientras los nodos worker ejecutan tus cargas de trabajo
- kubeadm es la herramienta oficial para inicializar clústeres de grado de producción
- Los Deployments proporcionan gestión declarativa de Pods con actualizaciones progresivas y reversiones
- Los Services (ClusterIP, NodePort, LoadBalancer) dan a los Pods endpoints de red estables
- Siempre usa manifiestos YAML para cargas de trabajo en producción para habilitar control de versiones y reproducibilidad
Para fundamentos de contenedorización que complementan esta guía, consulta nuestros tutoriales sobre Cómo Instalar Docker en Ubuntu y Docker Compose: Guía Práctica para Administradores de Sistemas.