Guía Definitiva: Configura Kubernetes con kubeadm en Ubuntu para Sysadmins Linux
TL;DR — Lo que vas a tener al final
- Tiempo de setup: Cluster mínimo en 2 horas, producción en 1 día.
- Requisitos: CPU con VT-x, 8+ GB RAM, 3+ nodos para HA.
- Resultado: Kubernetes con HA, escalado automático, service discovery.
- Troubleshooting: kubectl logs, kubectl describe, dashboards Grafana oficiales.
Por qué kubeadm sigue siendo la opción correcta en 2026
Cada año aparece una nueva herramienta que promete reemplazar kubeadm: k3s, kind, microk8s, Talos, K0s, Rancher. Para producción real en un servidor Ubuntu dedicado, ninguno ha desplazado a kubeadm como el camino canónico que documenta Kubernetes oficialmente. La razón es simple: kubeadm sigue el flujo que usa el proyecto upstream para probar cada release, así que lo que funciona en kubeadm en tu datacenter es lo mismo que corre en GKE, EKS y AKS por debajo.
Hoy el ecosistema está en un punto dulce. La versión estable actual es Kubernetes 1.36.5 (lanzada el 15 de septiembre de 2026 según kubernetes.io/releases) y la rama 1.34 (en maintenance mode desde el 27 de agosto de 2026, EOL upstream 27 de octubre de 2026) sigue en 1.34.12 como bridge hacia 1.36 — para servidores nuevos en 2026 conviene saltar directo a 1.36; la rama 1.33 alcanzó EOL el 28 de junio de 2026 y la 1.32 el 28 de febrero de 2026, así que ya no reciben parches. La versión 1.37.0 también está disponible como minor reciente. Para Ubuntu, la base estable es 24.04 LTS (soporte hasta mayo de 2029); 22.04 sigue siendo viable pero su kernel 5.15 se queda corto para las optimizaciones de eBPF que usa Cilium 1.20+. Kubernetes no tiene releases LTS oficiales: cada minor recibe parches upstream por ~14 meses desde la liberación del siguiente minor.
Requisitos mínimos antes de empezar
Kubeadm no es exigente pero tiene tres condiciones que la mayoría de los tutoriales viejos no mencionan y que te van a ahorrar horas de debugging si las verificas primero.
| Recurso | Mínimo | Recomendado |
|---|---|---|
| CPU | 2 cores | 4+ cores (kubelet + pods del sistema) |
| RAM | 2 GB | 4 GB mínimo cómodo, 16+ GB si corres workloads serios |
| Disco | 20 GB | 50+ GB (logs de etcd + imágenes de contenedor) |
| Red | 1 Gbps | 1 Gbps dedicado entre nodos, latencia <2 ms |
| OS | Ubuntu 22.04 | Ubuntu 24.04 LTS con kernel 6.8 HWE |
| Swap | Deshabilitado | Deshabilitado (kubelet se niega a arrancar con swap activo) |
| cgroup v2 | Requerido | Default en Ubuntu 22.04+ |
El swap debe estar deshabilitado por diseño: kubelet falla con running with swap on is not supported si lo detecta activo. Para apagarlo:
sudo swapoff -a
sudo sed -i '/[[:space:]]swap[[:space:]]/d' /etc/fstab
# Verifica
free -h | grep -i swap # debe estar vacio
Y verifica que cgroup v2 está activo (Ubuntu 22.04+ lo trae por default):
stat -fc %T /sys/fs/cgroup/ # debe decir "tmpfs" o "cgroup2fs"
# Si dice "tmpfs" (v1, single-line por controlador) o "cgroup2fs" (v2 unified), estas OK
# Si no podes leer /sys/fs/cgroup/ como dir, hay un problema mayor de configuración
Cargar módulos de kernel que containerd necesita
containerd requiere dos módulos del kernel para funcionar: overlay (el filesystem por capas de las imágenes) y br_netfilter (para que el tráfico de red de los pods atraviese el bridge correctamente). En Ubuntu 24.04 overlay ya viene cargado, pero br_netfilter no siempre.
cat <<'EOF' | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# Parametros de sysctl requeridos para el routing de los pods
cat <<'EOF' | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
Sin net.ipv4.ip_forward=1 el cluster arranca pero los pods en nodos distintos no se ven entre sí. Es el bug #1 que reporta gente que sigue tutoriales viejos: el cluster parece funcionar pero cualquier Deployment con más de un pod queda en Pending o los pods no resuelven DNS entre nodos.
Instalar containerd (NO Docker)
Desde Kubernetes 1.24 el shim de Docker (dockershim) se eliminó del codebase. Hoy el runtime oficial es containerd. Para Kubernetes 1.34 funcionan containerd 1.7+ y 2.x; el paquete containerd.io que viene del repo de Docker suele ser 2.0+ en Ubuntu 24.04, que ya soporta el socket CRI v1 (/var/run/containerd/containerd.sock) que necesita kubelet. Instalar Docker en su lugar te obliga a configurar un shim externo y agregar una capa de fallo que no necesitas.
# Repositorio oficial de Docker (mantiene containerd actualizado)
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update
sudo apt install -y containerd.io
Ahora el cambio crítico: containerd por default usa el driver de cgroup cgroupfs, pero kubelet en Kubernetes 1.22+ recomienda systemd. Si los dos no coinciden, kubelet se queja con failed to create containerd task: failed to create shim task cuando intenta arrancar pods. El fix es editar la configuración:
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
# Cambiar SystemdCgroup de false a true
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# Reiniciar para que tome el cambio
sudo systemctl restart containerd
sudo systemctl status containerd --no-pager
Verifica que el cambio se aplicó antes de seguir:
sudo ctr version # debe decir Client/Server 1.7+ o 2.x
sudo ctr plugin ls | grep systemd_cgroup # el plugin runc debe usar systemd
Instalar kubeadm, kubelet y kubectl
Los tres paquetes vienen del repositorio oficial pkgs.k8s.io. NO uses el PPA kubernetes-xenial que aparece en tutoriales viejos — quedó deprecado en 2024 y muchos mirrors ya no responden.
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
# Keyring del repo oficial
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# Repositorio para la rama 1.34 (cambia el path si quieres 1.35 o 1.36)
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.34/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl # evita que un apt upgrade los toque sin querer
# Habilitar el kubelet (arranca pero queda a la espera de kubeadm init)
sudo systemctl enable --now kubelet
El apt-mark hold es clave: sin él, una noche de unattended-upgrades puede actualizar kubelet a una versión incompatible con el resto del control plane y dejarte con un cluster que no arranca. Kubernetes nunca avisa antes de romper compat entre minors.
Inicializar el control plane
Ahora sí, el momento de la verdad. kubeadm init descarga todas las imágenes de los componentes del control plane (kube-apiserver, etcd, kube-scheduler, kube-controller-manager) y arranca el cluster. Antes de correrlo elegí el pod network CIDR — el rango de IPs que usarán los pods. Cada CNI exige uno distinto:
| CNI | Pod CIDR compatible | Notas |
|---|---|---|
| Cilium | 10.244.0.0/16 (configurable vía IPAM) o cualquier /16 o /12 |
Recomendado. eBPF nativo, soporta kube-proxy-free |
| Calico | 192.168.0.0/16 (default del manifest) o configurable |
Battle-tested, mejor para network policies estrictas |
| Flannel | 10.244.0.0/16 (fijo, es el default upstream) |
El más simple pero no soporta network policies |
El siguiente comando asume Cilium con 10.244.0.0/16. Si vas con Calico cambia el CIDR antes:
sudo kubeadm init
--pod-network-cidr=10.244.0.0/16
--kubernetes-version=v1.34.12
--control-plane-endpoint=$(hostname -I | awk '{print $1}'):6443
# Tarda 1-3 minutos. Al final imprime un bloque con:
# - kubeadm join ... (guárdalo, lo necesitas para los workers)
# - instrucciones para configurar kubectl
Por default kubeadm también despliega CoreDNS para resolver nombres internos del cluster (kubernetes.default.svc, etc.). Cilium no instala ni gestiona CoreDNS — son piezas independientes. Después de instalar Cilium los pods resuelven DNS vía CoreDNS sin configuración extra.
Configurar 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 tienes acceso
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# ubuntu NotReady control-plane 2m v1.34.12
El nodo aparece como NotReady hasta que instales un CNI. Eso es esperado.
Instalar Cilium (CNI recomendado)
Cilium 1.20+ es el CNI por defecto de la mayoría de los managed services de Kubernetes en 2026 porque reemplaza a kube-proxy con eBPF y maneja network policies en el kernel sin pasar por iptables. La instalación es vía Helm 3:
# Si no tienes Helm
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Instalar Cilium como CNI (modo kube-proxy compatible, no replacement)
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium --version 1.20.2
--namespace kube-system
--set kubeProxyReplacement=false
--set hubble.enabled=true
--set hubble.relay.enabled=true
--set hubble.ui.enabled=true
# Verifica que todos los pods arrancaron
kubectl get pods -n kube-system -w
# Espera a que cilium y coredns esten Running
Una vez que Cilium reporta Running, el nodo pasa a Ready:
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# ubuntu Ready control-plane 5m v1.34.12
Unir nodos worker al cluster
En cada máquina worker repite los pasos 1 a 5 (módulos de kernel, sysctl, containerd, kubeadm/kubelet/kubectl) y al final corre el kubeadm join que imprimió kubeadm init. Si perdiste el token, regeneralo desde el control plane:
# En el control plane, regenerar join command
kubeadm token create --print-join-command
# Ejemplo de output:
# kubeadm join 192.168.5.50:6443 --token abcdef.0123456789abcdef
# --discovery-token-ca-cert-hash sha256:7c3a...
# En cada worker (con el output de arriba)
sudo kubeadm join 192.168.5.50:6443 --token abcdef.0123456789abcdef
--discovery-token-ca-cert-hash sha256:7c3a...
Por defecto el token expira en 24 horas. Si te demoraste en unir los workers, regeneralo. Una vez que kubeadm join termina exitosamente, en el control plane deberías ver:
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# ubuntu Ready control-plane 10m v1.34.12
# worker-1 Ready <none> 2m v1.34.12
# worker-2 Ready <none> 1m v1.34.12
Smoke test: tu primer deployment
Antes de dar por buena la instalación, desplegar algo real confirma que CNI, scheduling y DNS funcionan end-to-end. El smoke test canónico es un nginx con tres réplicas y un NodePort para acceder desde fuera:
kubectl create deployment nginx-test --image=nginx:1.27 --replicas=3
kubectl expose deployment nginx-test --port=80 --type=NodePort
# Espera a que las 3 réplicas esten Running
kubectl get pods -l app=nginx-test -w
# Anota el NodePort (rango 30000-32767)
kubectl get svc nginx-test
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# nginx-test NodePort 10.96.123.45 <none> 80:31234/TCP 30s
# Desde cualquier máquina en la red
curl http://<IP-de-cualquier-nodo>:31234
# debe devolver el HTML de nginx
Si las tres réplicas quedan Running y el curl devuelve HTML, el cluster está funcional. Lo que no probaste: storage (PVCs), LoadBalancers (requiere un controller externo), e ingress (próximo tutorial).
Troubleshooting común
Estos son los cinco problemas que vas a encontrar siguiendo cualquier tutorial de kubeadm, con la causa real y el fix.
"kubelet fails to start: failed to create containerd task": el driver de cgroup de containerd no coincide con el de kubelet. Vuelve al paso de containerd y verifica que SystemdCgroup = true está en /etc/containerd/config.toml. Después sudo systemctl restart containerd.
"nodes stay NotReady after installing CNI": Cilium/Flannel/calico no terminó de inicializarse. kubectl -n kube-system get pods muestra algún pod en CrashLoopBackOff. kubectl logs -n kube-system <pod> revela el error — 90% de las veces es un CIDR mal configurado en kubeadm init.
"kubectl get nodes: The connection to the server localhost:8080 was refused": $HOME/.kube/config no existe o está mal copiado. sudo cp /etc/kubernetes/admin.conf ~/.kube/config && sudo chown $(id -u):$(id -g) ~/.kube/config. Si sigues en el control plane con sudo, esto último no es problema — pero si estás desde otra máquina, necesitas scp-ear el admin.conf.
"kubeadm join fails: certificate has expired or is not yet valid": el reloj del worker está desincronizado. sudo systemctl status chrony o timedatectl status. En VMs con clock drift es sorprendentemente común.
"Pods stuck in Pending with FailedScheduling: 0/3 nodes are available": el scheduler no encuentra nodos con recursos suficientes. kubectl describe pod <name> muestra pod has unbound immediate PersistentVolumeClaims o Insufficient cpu/memory. Para pruebas puedes editar el manifest con kubectl edit deployment nginx-test y borrar la sección resources.requests, o aplicar un manifest vacío de recursos.
¿Cuándo NO usar kubeadm?
Kubeadm es ideal para clusters medianos (3-50 nodos) que administras tú y donde quieres entender cada componente. Para casos más extremos conviene evaluar otros caminos.
Si tienes 1 sola máquina o quieres aprender: kind o minikube levantan clusters en contenedores/VMs en segundos y son más cómodos para experimentar sin gastar hardware.
Si quieres un Kubernetes "tipo appliance" con poco mantenimiento: k3s (Rancher) o Talos Linux embeben todo el control plane en un solo binario, hacen updates atómicos y vienen con defaults sensatos. La contra es que abstracciones son más gruesas y debuggear problemas de red requiere conocer las decisiones que el wrapper tomó por ti.
Si tu cluster va a escalar a 100+ nodos: Considera kubeasz, Kubespray o directamente un managed service (EKS, GKE, AKS). kubeadm escala bien pero la gestión de HA, upgrades y backups de etcd se vuelve manual y tediosa.
Si necesitas Windows nodes o GPUs especializadas: kubeadm soporta ambos pero la documentación oficial es más delgada. Para setups GPU con NVIDIA device plugin y drivers, los managed services tienen integraciones más probadas.
Para un homelab o un cluster de producción pequeño-mediano en Ubuntu, kubeadm sigue siendo la elección que te da más control sin pedirte que mantengas un sistema operativo custom. Ahora tienes un cluster funcional con Cilium como CNI, containerd como runtime y kubectl configurado para administrarlo. Si vas a probarlo en una máquina física, el camino más común es correr el control plane como VM dentro de un hipervisor: la guía de home server con Proxmox cubre la parte de virtualización que falta acá.
Preguntas frecuentes
¿Por qué Guía Definitiva: Configura Kubernetes con kubeadm en Ubuntu para Sysadmins Linux es relevante para sysadmins Linux?
Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.
¿Qué necesito para empezar con Guía Definitiva: Configura Kubernetes con?
Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.
¿Cuáles son los errores más comunes con Guía Definitiva: Configura Kubernetes con?
Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.
¿Cuándo usar Guía Definitiva: Configura Kubernetes con y cuándo una alternativa?
Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.
¿Cómo migro desde una configuración previa a Guía Definitiva: Configura Kubernetes con?
Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.
Lee también
- Guía Fedora 44: Novedades, Instalación y Upgrade para Sysadmins Linux
- Configura VLANs en Linux con nmcli: Guía paso a paso para sysadmins
- Protege tu SSH: Guía esencial para configurar Fail2ban en Linux
- Configura Prometheus y Grafana: Guía DevOps para SysAdmins Linux
- Guía Definitiva para Sysadmins: Configuración de Bonding de Interfaces de Red en Linux
