Para escalar el clúster de forma eficiente, hemos implementado Infraestructura como Código (IaC) mediante Ansible. Esto nos permite gestionar la configuración de todos los nodos de forma simultánea y centralizada desde un único “Nodo de Control”, garantizando la idempotencia en todo el entorno.
El servidor de control es el cerebro de la automatización. Para su despliegue, hemos clonado la plantilla base de Ubuntu 24.04 creada anteriormente en Proxmox.
Pasos en Proxmox:
ubuntu-2404-template -> Clone (Full Clone).Identidad: Nombre de VM: ansible-server |
VM ID: 115. |
192.168.1.115 mediante Netplan.| Parámetro | Valor Real | |
|---|---|---|
| Hostname | ansible-server |
|
| IP Estática | 192.168.1.115 |
|
| Máscara / Gateway | /24 |
192.168.1.1 |
| Recursos | 1 Core / 1 GB RAM / 40 GB Disco |
Instalación del motor de automatización:
sudo apt update && sudo apt install ansible -y
Ansible se conecta a los nodos mediante SSH. Para que el proceso sea automático, el servidor debe poder entrar en los nodos sin pedir contraseña en cada ejecución.
1. Generar par de llaves en ansible-server:
ssh-keygen -t rsa -b 4096
# Pulsar ENTER en todas las opciones (dejar la "passphrase" vacía)
2. Copiar la llave pública a todos los nodos: Es fundamental documentar que, al ejecutar estos comandos por primera vez, el sistema pedirá confirmar la autenticidad del nodo escribiendo yes y luego la contraseña del usuario asir de forma manual:
ssh-copy-id asir@192.168.1.110 # Nodo Master
ssh-copy-id asir@192.168.1.111 # Nodo Worker 01
ssh-copy-id asir@192.168.1.112 # Nodo Worker 02
ssh-copy-id asir@192.168.1.116 # Nodo Servidor NFS
[!TIP] Una vez completado, puedes verificar el acceso con
ssh asir@192.168.1.110. Si entras directamente al terminal del nodo sin que te pida contraseña, el intercambio ha sido exitoso.
Para mantener un estándar profesional, el proyecto se organiza en subcarpetas específicas. Esto facilita el crecimiento del clúster y la gestión de diferentes configuraciones.
Recomendado para entender la lógica de grupos en Ansible.
mkdir -p ~/ansible/inventory ~/ansible/playbooks ~/ansible/roles
nano ~/ansible/inventory/hosts.ini
Contenido del archivo hosts.ini:
[master]
k8s-master ansible_host=192.168.1.110
[workers]
k8s-worker-01 ansible_host=192.168.1.111
k8s-worker-02 ansible_host=192.168.1.112
[nfs]
nfs-server ansible_host=192.168.1.116
[k8s_cluster:children]
master
workers
Ideal para replicar el entorno exactamente como está en el repositorio de GitHub.
# 1. Crear carpetas base
mkdir -p ~/ansible/inventory ~/ansible/playbooks
# 2. Descargar archivos reales
wget -O ~/ansible/inventory/hosts.ini https://raw.githubusercontent.com/jobopaK/ProyectoIntegradoASIR/main/ansible/inventory/hosts.ini
wget -O ~/ansible/playbooks/pre-requisitos.yml https://raw.githubusercontent.com/jobopaK/ProyectoIntegradoASIR/main/ansible/playbooks/pre-requisitos.yml
Al ejecutar tareas de administración (como instalar paquetes), Ansible requiere privilegios de root mediante el uso de become: yes. Por defecto, Ubuntu solicita la contraseña de usuario para cada acción de sudo, lo que detiene la automatización con el error Missing sudo password.
Solución Implementada:
Para lograr una automatización fluida, hemos configurado los nodos para que el usuario asir pueda ejecutar comandos administrativos sin necesidad de introducir la contraseña.
Comando aplicado en cada nodo:
echo "asir ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/asir
sudo chmod 0440 /etc/sudoers.d/asir
[!IMPORTANT] Decisión de Diseño: Esta configuración se ha aplicado directamente en la Plantilla Base de Proxmox. De esta forma, cualquier nodo futuro (como un posible Worker-03) nacerá ya preparado para ser gestionado por Ansible de forma 100% automática, eliminando la intervención manual en el escalado del clúster.
[!NOTE] Consideración de Seguridad: Se ha optado por la directiva
NOPASSWDexclusivamente para el usuarioasiry limitada al entorno de administración automatizada. Esta decisión de diseño prioriza la fluidez del despliegue continuo (CI/CD) y la escalabilidad del clúster sobre la re-autenticación manual, manteniendo un control de acceso estricto mediante llaves SSH.
Con la confianza SSH establecida y los privilegios de sudo configurados, procedemos a realizar las pruebas finales de control.
Primero, validamos que el servidor de control puede comunicarse con todos los nodos del inventario y ejecutar Python en ellos:
ansible all -i ~/ansible/inventory/hosts.ini -m ping

[!NOTE] Si el resultado muestra SUCCESS en verde para todos los nodos, significa que la infraestructura está lista para ser gestionada por código.
Lanzamos el script pre-requisitos.yml para automatizar la instalación de componentes esenciales (cliente NFS y módulos del kernel) en todo el clúster de forma simultánea:
ansible-playbook -i ~/ansible/inventory/hosts.ini ~/ansible/playbooks/pre-requisitos.yml

Análisis de los resultados:
Una de las mayores ventajas de esta arquitectura es la facilidad para añadir nuevos nodos. Para el k8s-worker-03, seguimos un proceso híbrido que combina la rapidez de la plantilla de Proxmox con la potencia de Ansible.
Debido a que Ansible necesita una IP para comunicarse, realizamos un mínimo de pasos manuales:
ubuntu-2404-template en Proxmox (VM ID 113).192.168.1.113.~/ansible/inventory/hosts.ini dentro de la VM ansible-server:[workers]
k8s-worker-01 ansible_host=192.168.1.111
k8s-worker-02 ansible_host=192.168.1.112
k8s-worker-03 ansible_host=192.168.1.113
Para que el Nodo de Control pueda gestionar el nuevo worker, existen dos vías:
ssh-copy-id asir@192.168.1.113 desde el servidor Ansible.~/.ssh/authorized_keys. De esta forma, cualquier clon futuro tendrá acceso automático. Ejecutando ssh-copy-id asir@192.168.1.XXX desde el servidor Ansible a la IP asignada de la plantilla.Para automatizar la conversión de una VM vacía en un nodo de Kubernetes, hemos desarrollado el playbook install-k8s-node.yml. Este script garantiza que todos los nodos tengan exactamente la misma configuración y versiones de software y parámetros de red, eliminando el error humano.
Recomendado si se desea editar o revisar el código antes de su ejecución.
nano ~/ansible/playbooks/install-k8s-node.yml
Código completo del Playbook:
---
- name: Instalación completa de Kubernetes en Nodos
hosts: workers
become: yes
tasks:
- name: 0. Instalar cliente NFS (Añadido para persistencia WP)
apt:
name: nfs-common
state: present
update_cache: yes
- name: 1. Desactivar SWAP (Requisito K8s)
shell: |
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
- name: 2. Configurar módulos del Kernel para Red
copy:
dest: /etc/modules-load.d/k8s.conf
content: |
overlay
br_netfilter
- name: 3. Cargar módulos ahora mismo
shell: |
modprobe overlay
modprobe br_netfilter
- name: 4. Configurar parámetros sysctl para IP Forwarding
copy:
dest: /etc/sysctl.d/k8s.conf
content: |
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
- name: 5. Aplicar cambios de sysctl
command: sysctl --system
- name: 6. Instalar dependencias base
apt:
name: [apt-transport-https, ca-certificates, curl, gpg]
state: present
update_cache: yes
- name: 7. Añadir repositorio y clave de Containerd (Docker)
shell: |
install -m 0755 -d /etc/apt/keyrings
curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) | gpg --dearmor -o /etc/apt/keyrings/docker.gpg --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
- name: 8. Instalar Containerd
apt:
name: containerd.io
state: present
update_cache: yes
- name: 9. Configurar Containerd (SystemdCgroup)
shell: |
mkdir -p /etc/containerd
containerd config default | tee /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
notify: Reiniciar Containerd
- name: 10. Añadir repositorio de Kubernetes (v1.30)
shell: |
curl -fsSL [https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key](https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key) | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg --yes
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] [https://pkgs.k8s.io/core:/stable:/v1.30/deb/](https://pkgs.k8s.io/core:/stable:/v1.30/deb/) /' | tee /etc/apt/sources.list.d/kubernetes.list
- name: 11. Instalar Kubelet, Kubeadm y Kubectl
apt:
name: [kubelet, kubeadm, kubectl]
state: present
update_cache: yes
- name: 12. Bloquear versiones para evitar actualizaciones automáticas
shell: apt-mark hold kubelet kubeadm kubectl
- name: 13. Cambiar el nombre del host (Hostname)
hostname:
name: ""
- name: 14. Leer el comando join del archivo txt
set_fact:
join_command: ""
- name: 15. Unirse al clúster (si no está ya unido)
shell: ""
args:
creates: /etc/kubernetes/kubelet.conf
handlers:
- name: Reiniciar Containerd
service:
name: containerd
state: restarted
Ideal para asegurar que se utiliza la versión testeada del proyecto alojada en GitHub.
wget -O ~/ansible/playbooks/install-k8s-node.yml https://raw.githubusercontent.com/jobopaK/ProyectoIntegradoASIR/main/ansible/playbooks/install-k8s-node.yml
[!TIP] Eficiencia en el diseño: El uso de la variable mágica `` (Tarea 13) es una de las mayores potencias de este Playbook. Permite que el código sea genérico: Ansible toma el nombre definido en el archivo de inventario (
hosts.ini) y lo aplica dinámicamente al sistema operativo, eliminando la necesidad de crear configuraciones específicas para cada nuevo nodo.
Durante el despliegue del Worker 03, nos enfrentamos a un problema común: el comando de unión fallaba a pesar de que Ansible no reportaba errores críticos.
Por seguridad, los tokens de unión generados por Kubernetes tienen una validez de 24 horas. Al intentar unir un nodo 9 días después de la creación del clúster, el token almacenado en nuestro archivo .txt era inválido.
Para solucionar esto, ejecutamos el siguiente comando en el Nodo Master:
sudo kubeadm token create --print-join-command
Este comando genera una nueva cadena de unión que debemos actualizar en nuestro servidor de control ansible-server:
nano ~/ansible/token_k8s.txt
Ya solo queda reemplazar el contenido por el nuevo comando generado.
Si un nodo intenta unirse y falla, deja residuos de configuración que impiden un segundo intento. Para limpiar el estado del nodo antes de reintentar el Playbook, usamos:
sudo kubeadm reset -f
Con el token actualizado y el inventario listo, lanzamos el despliegue limitando la ejecución únicamente al nuevo nodo para optimizar tiempo:
ansible-playbook -i ~/ansible/inventory/hosts.ini ~/ansible/playbooks/install-k8s-node.yml --limit k8s-worker-03
Figura 20: Proceso de configuración de dependencias y sistema en el nuevo nodo.
Figura 21: Finalización del despliegue indicando el éxito de la unión al clúster.
Antes de validar la unión en el clúster, podemos demostrar el poder de gestión centralizada de Ansible verificando el estado del servicio de Kubernetes en todos los nodos del grupo workers a la vez:
ansible workers -i ~/ansible/inventory/hosts.ini -m shell -a "systemctl status kubelet | grep Active"
Este comando confirma en una sola salida que el demonio kubelet está corriendo correctamente en toda la infraestructura.
Una vez finalizado el Playbook (RECAP: failed=0), verificamos que el nodo aparece en el clúster desde el k8s-master:
kubectl get nodes
[!NOTE] El nodo puede aparecer inicialmente como
NotReadymientras se descargan e inician los pods de red (Calico/Flannel). Tras un par de minutos, el estado cambiará aReady, confirmando el éxito de la automatización.
Al añadir un nodo mediante kubeadm join, Kubernetes no le asigna automáticamente el rol de “worker” en la columna ROLES. Aunque esto no afecta al funcionamiento técnico, es una buena práctica para la organización del clúster.
Para asignar el rol al nuevo nodo desde el k8s-master, ejecutamos:
kubectl label node k8s-worker-03 node-role.kubernetes.io/worker=worker
Siguiente Paso: Fase 8: Despliegue de Aplicación Real
Proyecto Integrado de Grado Superior ASIR
© 2026 - jobopaK