Saltar al contenido principal

Provisión Automatizada de Nodos

Este estándar técnico define el proceso de creación de infraestructura base para un clúster de Kubernetes v1.35 utilizando Infraestructura como Código (IaC) ligera sobre un hipervisor Linux (KVM).

1. Arquitectura de Despliegue

Utilizamos un modelo de "Imágenes Doradas" mediante archivos .qcow2 diferenciales y personalización dinámica en el primer arranque via Cloud-Init.

2. Definición de la Topología (nodes.csv)

La matriz de nodos define el estado deseado de recursos y direccionamiento IP estático.

HostnameRAMvCPUsIP (Static)Role
cluster1-master12048210.2.3.4Control Plane
cluster1-worker1-32048210.2.3.4XWorker Nodes

3. Lógica de Aprovisionamiento (Scripting)

El script vms_provision.sh orquesta el ciclo de vida de la creación de VMs, gestionando discos diferenciales para optimizar el almacenamiento y utilizando virt-customize para inyectar configuraciones de red antes del arranque.

vms_provision.sh
#!/bin/bash

# 1. PATHS ABSOLUTOS
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
readonly FILE_NODES="${SCRIPT_DIR}/nodes.csv"
readonly USER_DATA="${SCRIPT_DIR}/user-data.yaml"

# 2. VARIABLES KVM
readonly POOL_NAME='kvm-images'
readonly BASE_IMAGE='noble-server-cloudimg-amd64.img'
readonly GW='172.17.17.17'
readonly DNS1='172.17.17.17'

# --- Function: Metadata Generation (Efímero) ---
function generate_temp_metadata() {
local hostname=$1
local temp_file=$(mktemp /tmp/metadata.${hostname}.XXXXXX.yaml)
echo -e "instance-id: ${hostname}\nlocal-hostname: ${hostname}" > "$temp_file"
echo "$temp_file"
}

# --- Main Provisioning Loop ---
tail -n +2 "${FILE_NODES}" | while IFS=',' read -r hostname ram vcpus ip; do
echo "--- Aprovisionando: ${hostname} (${ip}) ---"

current_metadata=$(generate_temp_metadata "$hostname")

# 1. Crear disco diferencial
virsh vol-create-as ${POOL_NAME} ${hostname}.qcow2 20G \
--format qcow2 --backing-vol ${BASE_IMAGE} --backing-vol-format qcow2 > /dev/null

DISK_PATH=$(virsh vol-path --pool ${POOL_NAME} ${hostname}.qcow2)

# 2. INYECCIÓN DIRECTA (Network & Fixes)
sudo virt-customize -a "$DISK_PATH" \
--hostname "$hostname" \
--write "/etc/netplan/01-netcfg.yaml:
network:
version: 2
ethernets:
enp1s0:
dhcp4: false
addresses: [$ip/24]
routes: [{to: default, via: $GW}]
nameservers: {addresses: [$DNS1, 8.8.8.8]}" \
--run-command "rm -f /etc/netplan/50-cloud-init.yaml" \
--write "/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg:network: {config: disabled}"

# 3. LANZAMIENTO
virt-install \
--name "${hostname}" --ram "${ram}" --vcpus "${vcpus}" \
--os-variant ubuntu24.04 --disk path="$DISK_PATH" \
--network bridge=br0 --graphics none --import \
--cloud-init user-data="${USER_DATA}",meta-data="${current_metadata}" \
--noautoconsole

rm -f "$current_metadata"
done

4. Configuración del Runtime vía Cloud-Init

Para garantizar la inmutabilidad, el archivo user-data.yaml automatiza el Hardening del OS, la instalación del runtime containerd y la optimización de parámetros del Kernel (sysctl) necesarios para Kubernetes.

Gestión de Secretos

En este ejemplo, la contraseña se inyecta mediante un hash SHA-512. En entornos productivos, se recomienda el uso de SSH Keys exclusivamente y la inyección de secretos vía Vault.

user-data.yaml.example
#cloud-config
users:
- name: candidate
# Conf. de sudo sin passwd
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
lock_passwd: false
passwd: "...PASSWORD_HASH..."
ssh_authorized_keys:
- ssh-rsa ...MY_CLAVE_PUBLICA...
- ...
# 1. INSTALACIÓN DE PAQUETES (Base + Runtime)
package_update: true
packages:
- curl
- gnupg
- vim
- apt-transport-https
- ca-certificates
- socat # Instalación automática del runtime (Requisito Kubeadm)
- conntrack # (Requisito Kubeadm)
- containerd # El Runtime (Requisitor de Kubeadm)
- nfs-common # Útil para volúmenes persistentes futuros

# 2. CONFIGURACIÓN DE ARCHIVOS (Módulos y Sysctl)
write_files:
- path: /etc/modules-load.d/k8s.conf
content: |
overlay
br_netfilter
- path: /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

# 3. EJECUCIÓN DE COMANDOS (El Ritual de Containerd)
runcmd:
# Desactivar Swap permanentemente
- [ swapoff, -a ]
- [ sed, -i, '/swap/d', /etc/fstab ]

# sysctl - Cargar módulos y aplicar red
- [ modprobe, overlay ]
- [ modprobe, br_netfilter ]
# Aplicar cambios de red sin reiniciar
- [ sysctl, --system ]

# Configuración de Containerd (Driver Systemd)
- mkdir -p /etc/containerd
- sh -c "containerd config default > /etc/containerd/config.toml"
- sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# Configuración inicial de containerd (se completará manualmente para practicar)
- [ systemctl, enable, --now, containerd ]
# Configurar el socket de containerd para crictl
- [ crictl, config, runtime-endpoint, unix:///run/containerd/containerd.sock ]
# Reiniciar para aplicar cambios
- systemctl restart containerd
- systemctl enable containerd

Documentación Relacionada: