La mayoría de los tutoriales de Kubernetes comienzan con un clúster gestionado en la nube. Aquí es diferente: vamos a montar el clúster en VMs, en tu red, para que veas la plomería. Esto duele un poco — y es exactamente por eso que enseña.
El laboratorio de este curso tiene 3 masters y 3 workers. En el capítulo 05 vamos a poner el control plane en HA; por ahora, un master ya es suficiente para aprender.
Sello del curso: probado en Ubuntu Server 24.04, Kubernetes 1.35 y containerd 2.x. Las IPs a continuación son ficticias (
192.168.56.x).
Topología
| Nombre | IP | vCPU | RAM | Disco | Rol |
|---|---|---|---|---|---|
| k8s-master-01 | 192.168.56.10 | 2 | 4 GB | 50 GB | control plane |
| k8s-master-02 | 192.168.56.11 | 2 | 4 GB | 50 GB | control plane (HA, cap. 05) |
| k8s-master-03 | 192.168.56.12 | 2 | 4 GB | 50 GB | control plane (HA, cap. 05) |
| k8s-worker-01 | 192.168.56.21 | 4 | 16 GB | 100 GB | worker |
| k8s-worker-02 | 192.168.56.22 | 4 | 16 GB | 100 GB | worker |
| k8s-worker-03 | 192.168.56.23 | 4 | 16 GB | 100 GB | worker |
¿Por qué 16 GB en los workers? Porque vamos a ejecutar base de datos, mensajería, object storage y observabilidad. En laboratorio, 8 GB funciona hasta que comienzas a probar carga y failover — y ahí aparece el OOM killer. Aprendí esto de la manera difícil.
Si no tienes 6 VMs, comienza con 1 master + 1 worker. Lo importante es el proceso.
1. Preparando las VMs
Crea las VMs en Proxmox (o VirtualBox/UTM) con Ubuntu Server 24.04. Durante la instalación:
- Usuario con sudo.
- OpenSSH habilitado.
- Nada de swap (vamos a deshabilitarlo de todas formas).
Después, en todas las VMs:
sudo hostnamectl set-hostname k8s-master-01 # ajuste por VM
sudo timedatectl set-timezone America/Sao_Paulo
sudo apt update && sudo apt upgrade -y
Configura /etc/hosts en todas (o usa DNS interno):
192.168.56.10 k8s-master-01
192.168.56.11 k8s-master-02
192.168.56.12 k8s-master-03
192.168.56.21 k8s-worker-01
192.168.56.22 k8s-worker-02
192.168.56.23 k8s-worker-03
Prueba la resolución y el reloj. Un reloj incorrecto rompe TLS y el join:
getent hosts k8s-master-01
timedatectl
2. Prerrequisitos del nodo
En todas las VMs:
# swap off (agora e no boot)
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab
# módulos de kernel
sudo tee /etc/modules-load.d/k8s.conf >/dev/null <<'EOF'
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# sysctl de rede
sudo tee /etc/sysctl.d/k8s.conf >/dev/null <<'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
Verifica:
free -h # swap deve ser 0
lsmod | grep -E 'overlay|br_netfilter'
sysctl net.ipv4.ip_forward
No te saltes el swap. El kubelet se niega a iniciar con swap activo por defecto. Es el error nº 1 de quien monta el primer clúster.
3. containerd
Kubernetes no habla con Docker directamente; habla con un runtime vía CRI. Vamos a usar containerd.
sudo apt install -y containerd
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
Edita /etc/containerd/config.toml y asegúrate de que:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
Reinicia:
sudo systemctl restart containerd
sudo systemctl enable containerd
systemctl status containerd --no-pager
4. kubeadm, kubelet y kubectl
sudo apt install -y apt-transport-https ca-certificates curl gpg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key \
| sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /' \
| sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
Comprueba:
kubeadm version
kubelet --version
kubectl version --client
5. kubeadm init
En el master-01:
sudo kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=192.168.56.10 \
--control-plane-endpoint=192.168.56.10
Al final, imprime el comando de join. Guárdalo. Configura el kubeconfig de 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
kubectl get nodes
El master aparecerá como NotReady hasta que instalemos la CNI. Es normal.
6. CNI (flannel)
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
kubectl -n kube-flannel get pods -o wide
kubectl get nodes
Cuando flannel esté Running en todos los nodos, el master queda Ready.
Aviso: flannel resuelve la red, pero no aplica NetworkPolicy. Si necesitas segmentación, el capítulo 06 explica Cilium/Calico. Lo descubrí de la peor forma: la política existía, pero no hacía efecto.
7. Join de los workers
En el master, genera un nuevo token si lo necesitas:
kubeadm token create --print-join-command
Ejecuta el comando en los workers (como root):
sudo kubeadm join 192.168.56.10:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
Valida:
kubectl get nodes -o wide
kubectl get pods -A
Todos los nodos deben estar Ready.
8. El primer “no subió”
| Síntoma | Causa probable | Cómo resolver |
|---|---|---|
| kubelet no sube | swap activo | swapoff -a + /etc/fstab |
Node NotReady |
CNI ausente o pod flannel con error | kubectl -n kube-flannel logs <pod> |
join falla por TLS |
reloj desincronizado | timedatectl, NTP |
connection refused en el 6443 |
firewall o apiserver fuera | ss -lntp, crictl ps |
flannel sin subnet.env después de reboot |
/run limpio y flannel subió antes |
reinicia el DaemonSet/espera; verifica los logs de flannel |
pods en Pending |
nodo con taint o pocos recursos | kubectl describe node |
9. Limpieza (cuando necesites rehacer)
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d /var/lib/cni /var/lib/kubelet
sudo systemctl restart containerd kubelet
Haz esto en todos los nodos y comienza de nuevo. El laboratorio es para eso.
Checklist
- [ ] 6 VMs con hostname, IP fijo y
/etc/hostscorrectos. - [ ] Swap desactivado y módulos cargados en todos los nodos.
- [ ] containerd con
SystemdCgroup = true. - [ ] kubeadm/kubelet/kubectl en la misma versión.
- [ ]
kubeadm initcompletado y kubeconfig copiado. - [ ] CNI aplicada y master
Ready. - [ ] Workers con
joincompletado yReady. - [ ]
kubectl get pods -Asin pods rotos.
Glosario
- kubeadm: herramienta de bootstrap del clúster.
- CRI: interfaz entre kubelet y runtime.
- CNI: plugin de red de los pods.
- taint/toleration: regla de afinidad/repulsión entre pods y nodos.
- join: comando que agrega un nodo al clúster.
Próximo
Con el clúster en funcionamiento, vamos a crear el primer Deployment, exponer un Service y aprender a diagnosticar cuando el pod no sube.
Próximo: Curso Kubernetes 03 — El primer pod y el primer deploy
Seguir leyendo
Lea también
TutoriaisManual LSI / Broadcom MegaRAID — MegaCli y StorCLI
Con esta guía, su equipo gana autonomía para diagnosticar rebuilds, resolver foreign configurations, gestionar BBU y CacheCade — con escenarios reales de producción documentados.
20 min de leitura
Nginx Cheatsheet
Guía de bolsillo con ejemplos listos para configurar Nginx en producción: desde instalación y virtual hosts hasta ajuste de rendimiento y resolución de problemas.
12 min de leitura
KubernetesCurso Kubernetes 27 — SLO/SLI, on-call y postmortem sin culpa
Qué prometer, cómo medir y qué hacer cuando el error se dispara: SLI, SLO, presupuesto de error, alertas que despiertan a alguien y postmortem sin caza de brujas.
5 min de leitura
Comentarios
Aún sin comentarios
Sea el primero en compartir su opinión sobre este artículo.

Deje un comentario