Este documento describe paso a paso cómo monté un cluster Kubernetes (k3s) sobre Proxmox, con:
- 1 nodo master (
k8s-master) - 2 nodos worker (
k8s-worker1,k8s-worker2) - Primer despliegue: Nginx expuesto con NodePort
Distribución usada en las VMs: Ubuntu Server / Debian (basado en
systemdynetplan).
- Hipervisor: Proxmox VE
- 3 Máquinas virtuales:
k8s-masterk8s-worker1k8s-worker2
- Todas conectadas al mismo bridge de Proxmox (
vmbr0), en la misma red172.25.205.0/24.
| Rol | Hostname | IP |
|---|---|---|
| Master | k8s-master |
172.25.205.116 |
| Worker | k8s-worker1 |
172.25.205.117 |
| Worker | k8s-worker2 |
172.25.205.118 |
Ajustar las IPs según la red de tu laboratorio.
Estos valores funcionaron bien para laboratorio con k3s:
- vCPU: 2
- RAM: 2–4 GB
- Disco: 15–20 GB
- SO: Ubuntu Server / Debian
- Red: bridge a LAN (
vmbr0)
- vCPU: 2
- RAM: 2 GB
- Disco: 10 GB
- SO: Ubuntu Server / Debian
- Red: bridge a LAN (
vmbr0)
Todos los pasos de esta sección se realizan en cada VM (master y workers), cambiando lo necesario en IPs y hostnames.
Ejemplo de usuario: masteradmin (puede ser cualquier nombre).
# Crear usuario
sudo adduser masteradmin
# Darle permisos de sudo
sudo usermod -aG sudo masteradminIniciar sesión con ese usuario:
su - masteradminEn cada VM:
# En el master
sudo hostnamectl set-hostname k8s-master
# En el worker 1
sudo hostnamectl set-hostname k8s-worker1
# En el worker 2
sudo hostnamectl set-hostname k8s-worker2Cerrar sesión y volver a entrar para ver el cambio en el prompt.
Archivo: /etc/netplan/90-default.yaml
sudo nano /etc/netplan/90-default.yamlContenido para el master (k8s-master):
network:
version: 2
renderer: networkd
ethernets:
ens18:
dhcp4: no
dhcp6: no
addresses:
- 172.25.205.116/24
gateway4: 172.25.205.2
nameservers:
addresses: [1.1.1.1, 8.8.8.8]Notas:
- La interfaz puede ser
ens18,enp0s18, etc. Ver conip a. gateway4debe coincidir con la puerta de enlace de tu red (ip route | grep default).
Aplicar cambios:
sudo netplan apply
ip a # Verificar IPPara los workers, se repite el proceso cambiando solo la IP:
k8s-worker1→172.25.205.117/24k8s-worker2→172.25.205.118/24
sudo nano /etc/hostsContenido:
127.0.0.1 localhost
127.0.1.1 k8s-master # En el master
# 127.0.1.1 k8s-worker1 # En worker1, adaptarlo
# 127.0.1.1 k8s-worker2 # En worker2, adaptarlo
172.25.205.116 k8s-master
172.25.205.117 k8s-worker1
172.25.205.118 k8s-worker2
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
En cada nodo, ajustar la línea 127.0.1.1 al hostname local correspondiente.
En cada nodo:
# Desactivar swap en la sesión actual
sudo swapoff -a
# Comentar la línea de swap en /etc/fstab
sudo sed -i '/ swap / s/^/#/' /etc/fstab
# Verificar
free -h # La columna Swap debe estar en 0En k8s-master:
curl -sfL https://get.k3s.io | sh -Verificar servicio:
sudo systemctl status k3sVer nodos (de momento solo el master):
sudo kubectl get nodesEn el master:
sudo cat /var/lib/rancher/k3s/server/node-tokenGuardar ese token: se usará para unir los workers.
Ejemplo de formato (NO usar este literal):
K10a1b2c3d4e5f6g7h8i9j0k.lmnopqrstuvwxyz
En cada worker (k8s-worker1 y k8s-worker2), ejecutar:
curl -sfL https://get.k3s.io | \
K3S_URL=https://172.25.205.116:6443 \
K3S_TOKEN=K10a1b2c3d4e5f6g7h8i9j0k.lmnopqrstuvwxyz \
sh -Verificar que el agente está arriba:
sudo systemctl status k3s-agentEn k8s-master:
sudo kubectl get nodesSalida esperada (similar a):
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane,master XXm v1.33.5+k3s1
k8s-worker1 Ready <none> YYm v1.33.5+k3s1
k8s-worker2 Ready <none> ZZm v1.33.5+k3s1
En el master, con el usuario masteradmin:
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/configsudo chmod 644 /etc/rancher/k3s/k3s.yamlkubectl get nodesPor defecto, los workers aparecen sin rol.
Desde el master:
kubectl label node k8s-worker1 node-role.kubernetes.io/worker=worker
kubectl label node k8s-worker2 node-role.kubernetes.io/worker=workerVer etiquetas:
kubectl get nodes --show-labelskubectl create namespace demo-web
kubectl get namespaceskubectl create deployment nginx-demo \
--image=nginx \
--replicas=3 \
-n demo-webVer pods y en qué nodo corren:
kubectl get pods -n demo-web -o wideSalida ejemplo:
NAME READY STATUS RESTARTS AGE IP NODE
nginx-demo-xxxxx-qqzxp 1/1 Running 0 2m 10.42.1.3 k8s-worker1
nginx-demo-xxxxx-jx8rj 1/1 Running 0 2m 10.42.2.3 k8s-worker2
nginx-demo-xxxxx-nfzwd 1/1 Running 0 2m 10.42.2.4 k8s-worker2
kubectl expose deployment nginx-demo \
--type=NodePort \
--port=80 \
-n demo-webVer servicios:
kubectl get svc -n demo-webEjemplo de salida:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-demo NodePort 10.43.29.57 <none> 80:31969/TCP 7m
Obtener IPs de los nodos:
kubectl get nodes -o wideDesde cualquier máquina en la misma red que las VMs (por ejemplo, el equipo físico):
http://172.25.205.117:31969 # k8s-worker1
http://172.25.205.118:31969 # k8s-worker2
Debería mostrarse la página por defecto de Nginx ("Welcome to nginx!").
kubectl get all -n demo-webSubir de 3 a 5 réplicas:
kubectl scale deployment/nginx-demo --replicas=5 -n demo-web
kubectl get pods -n demo-web -o wideBajar a 2 réplicas:
kubectl scale deployment/nginx-demo --replicas=2 -n demo-web
kubectl get pods -n demo-web -o wideBorrar solo el Service (NodePort):
kubectl delete svc nginx-demo -n demo-webBorrar el Deployment (y sus pods):
kubectl delete deployment nginx-demo -n demo-webBorrar todo el namespace de pruebas:
kubectl delete namespace demo-webYa tienes un cluster k3s funcional con:
- ✅ 1 master y 2 workers
- ✅ Nginx desplegado y expuesto
- ✅ Operaciones básicas de escalado
¡Happy clustering! 🚀






