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:

  1. CPU o memoria insuficiente en los nodos worker
  2. Taints de nodo previniendo la programación
  3. 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:

  1. Error de aplicación o mala configuración
  2. Variables de entorno o config maps faltantes
  3. 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:

  1. Plugin CNI no instalado o fallando
  2. kubelet no puede alcanzar el servidor API
  3. 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

ComandoDescripción
kubectl get nodesListar todos los nodos del clúster
kubectl get podsListar Pods en el namespace actual
kubectl get pods -AListar Pods en todos los namespaces
kubectl get servicesListar todos los Services
kubectl get deploymentsListar 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/bashAbrir 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=NEscalar un Deployment
kubectl rollout status deployment/<nombre>Verificar progreso del despliegue
kubectl rollout undo deployment/<nombre>Revertir un Deployment
kubectl top nodesMostrar uso de recursos de nodos
kubectl top podsMostrar uso de recursos de Pods
kubectl config get-contextsListar contextos de clúster disponibles
kubectl cluster-infoMostrar 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.