Instalación y Uso de HashiCorp Nomad
HashiCorp Nomad es un orquestador de cargas de trabajo flexible que puede ejecutar aplicaciones en contenedores Docker, binarios nativos y máquinas virtuales en un clúster multi-nodo, con soporte para despliegues en múltiples regiones y centros de datos. A diferencia de Kubernetes, Nomad tiene una curva de aprendizaje más gradual y puede orquestar cargas de trabajo no contenedorizadas, lo que lo hace ideal para migraciones progresivas. Esta guía cubre la instalación de un clúster Nomad, definición de jobs y uso práctico.
Requisitos Previos
- Servidores Linux (Ubuntu 22.04/Debian 12 o CentOS 9/Rocky 9)
- 3 nodos para clúster de producción (1+ servidores, 2+ clientes)
- Docker instalado en nodos cliente (para el driver Docker)
- Opcional: HashiCorp Consul para descubrimiento de servicios
- Puertos 4646 (HTTP API), 4647 (RPC) y 4648 (Serf) entre nodos
Instalación de Nomad
# Añadir repositorio HashiCorp (Ubuntu/Debian)
wget -O- https://apt.releases.hashicorp.com/gpg | \
gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
tee /etc/apt/sources.list.d/hashicorp.list
apt-get update && apt-get install -y nomad
# CentOS/Rocky Linux
dnf config-manager --add-repo \
https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
dnf install -y nomad
# Verificar la instalación
nomad version
# Crear directorios de datos y configuración
mkdir -p /opt/nomad/data /etc/nomad.d
chmod 700 /opt/nomad/data
Configuración del Clúster
Configura los nodos servidor (ejecutar en los nodos designados como servidores):
# Configuración del nodo servidor Nomad
cat > /etc/nomad.d/server.hcl << 'EOF'
# Identificador del datacenter
datacenter = "dc1"
region = "global"
# Directorio de datos persistentes
data_dir = "/opt/nomad/data"
# Clave de encriptación para comunicaciones Gossip
encrypt = "cg8StVXbQJ0gPvMd9o7yrg==" # Generar con: nomad operator keygen
# Logging
log_level = "INFO"
log_file = "/var/log/nomad/nomad.log"
# Servidor del clúster
server {
enabled = true
bootstrap_expect = 3 # Número de servidores para formar quórum
# Tiempo de retención de jobs y allocations
job_gc_threshold = "4h"
deployment_gc_threshold = "1h"
eval_gc_threshold = "1h"
node_gc_threshold = "24h"
}
# Telemetría y métricas (Prometheus)
telemetry {
prometheus_metrics = true
publish_allocation_metrics = true
publish_node_metrics = true
}
EOF
Configura los nodos cliente (donde se ejecutarán los jobs):
# Configuración del nodo cliente Nomad
cat > /etc/nomad.d/client.hcl << 'EOF'
datacenter = "dc1"
region = "global"
data_dir = "/opt/nomad/data"
encrypt = "cg8StVXbQJ0gPvMd9o7yrg=="
client {
enabled = true
# Servidores a los que conectarse
servers = ["10.0.1.10:4647", "10.0.1.11:4647", "10.0.1.12:4647"]
# Metadatos del nodo para selección de jobs
meta {
"env" = "production"
"role" = "worker"
}
# Recursos disponibles para Nomad (dejar algo para el SO)
reserved {
cpu = 500 # MHz reservados para el SO
memory = 512 # MB reservados para el SO
disk = 1024 # MB reservados para el SO
}
}
# Habilitar driver Docker
plugin "docker" {
config {
allow_privileged = false
volumes {
enabled = true
}
}
}
EOF
Crea el servicio systemd:
cat > /etc/systemd/system/nomad.service << 'EOF'
[Unit]
Description=HashiCorp Nomad - Workload Orchestrator
Documentation=https://www.nomadproject.io/docs
Wants=network-online.target
After=network-online.target
[Service]
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/bin/nomad agent -config /etc/nomad.d
KillMode=process
KillSignal=SIGINT
LimitNOFILE=65536
LimitNPROC=infinity
Restart=on-failure
RestartSec=2
TasksMax=infinity
[Install]
WantedBy=multi-user.target
EOF
mkdir -p /var/log/nomad
systemctl daemon-reload
systemctl enable --now nomad
# Verificar el estado del clúster
nomad server members
nomad node status
Definición de Jobs
Un Job en Nomad es la unidad de despliegue. Define qué ejecutar, cuántas instancias y con qué recursos:
# Crear un job de ejemplo para una aplicación web
cat > /tmp/webapp.nomad.hcl << 'EOF'
job "webapp" {
# Datacenter y región
datacenters = ["dc1"]
region = "global"
# Tipo de scheduler: service (long-running), batch, system
type = "service"
# Grupo de tareas
group "web" {
count = 3 # Número de instancias
# Política de actualización progresiva
update {
max_parallel = 1 # Actualizar una instancia a la vez
min_healthy_time = "30s" # Tiempo mínimo sano antes de continuar
healthy_deadline = "3m" # Tiempo máximo para que una instancia sea sana
auto_revert = true # Revertir si el despliegue falla
}
# Reiniciar la tarea si falla
restart {
attempts = 3
interval = "5m"
delay = "15s"
mode = "fail"
}
# Red del grupo
network {
port "http" {
to = 8080 # Puerto dentro del contenedor
# static = 8080 # Puerto fijo en el host (opcional)
}
}
# Definición de la tarea
task "server" {
driver = "docker"
config {
image = "nginx:1.25-alpine"
ports = ["http"]
}
# Recursos asignados a esta tarea
resources {
cpu = 500 # MHz
memory = 256 # MB
}
# Comprobación de salud
service {
name = "webapp"
port = "http"
tags = ["production", "web"]
check {
type = "http"
path = "/health"
interval = "10s"
timeout = "3s"
}
}
}
}
}
EOF
# Planificar el job (muestra el plan sin ejecutar)
nomad job plan /tmp/webapp.nomad.hcl
# Ejecutar el job
nomad job run /tmp/webapp.nomad.hcl
# Ver el estado del job
nomad job status webapp
nomad alloc status <alloc-id>
Driver Docker
# Job con configuración avanzada de Docker
cat > /tmp/redis.nomad.hcl << 'EOF'
job "redis" {
datacenters = ["dc1"]
type = "service"
group "cache" {
count = 1
network {
port "redis" { static = 6379 }
}
# Volumen persistente
volume "redis-data" {
type = "host"
source = "redis-data"
}
task "redis" {
driver = "docker"
config {
image = "redis:7-alpine"
ports = ["redis"]
command = "redis-server"
args = ["--appendonly", "yes", "--maxmemory", "256mb"]
}
volume_mount {
volume = "redis-data"
destination = "/data"
}
resources {
cpu = 500
memory = 384
}
}
}
}
EOF
# Primero crear el volumen host en el cliente
mkdir -p /opt/nomad/volumes/redis-data
# Añadir en client.hcl:
# host_volume "redis-data" {
# path = "/opt/nomad/volumes/redis-data"
# read_only = false
# }
nomad job run /tmp/redis.nomad.hcl
Driver Exec (Aplicaciones Nativas)
# Job para ejecutar un binario directamente (sin contenedor)
cat > /tmp/api-server.nomad.hcl << 'EOF'
job "api-server" {
datacenters = ["dc1"]
type = "service"
group "api" {
count = 2
network {
port "api" { to = 3000 }
}
task "server" {
driver = "exec"
config {
command = "/opt/api/bin/servidor"
args = ["--port", "${NOMAD_PORT_api}", "--env", "production"]
}
# Variables de entorno para la tarea
env {
DB_HOST = "10.0.3.10"
DB_PORT = "5432"
LOG_LEVEL = "info"
}
# Artefacto a descargar antes de ejecutar
artifact {
source = "https://releases.empresa.com/api/latest/servidor-linux-amd64"
destination = "local/servidor"
mode = "file"
}
resources {
cpu = 1000
memory = 512
}
}
}
}
EOF
Descubrimiento de Servicios con Consul
# Cuando Consul y Nomad están integrados, los servicios se registran automáticamente
# Verificar que Nomad puede comunicarse con Consul
nomad agent-info | grep consul
# Los servicios registrados por Nomad aparecen en Consul con el prefijo "nomad-"
consul catalog services | grep nomad
# Consultar los servicios de Nomad via DNS de Consul
dig @127.0.0.1 -p 8600 webapp.service.consul
Despliegue Multi-región
# Job configurado para múltiples regiones
cat > /tmp/global-app.nomad.hcl << 'EOF'
job "global-webapp" {
# Múltiples regiones
region = "global"
datacenters = ["dc1", "dc2"]
# Configuración multi-región
multiregion {
strategy {
max_parallel = 1
on_failure = "fail_all"
}
region "eu-west" {
datacenters = ["dc1"]
count = 3
}
region "us-east" {
datacenters = ["dc2"]
count = 2
}
}
group "web" {
task "server" {
driver = "docker"
config {
image = "myapp:latest"
}
resources {
cpu = 500
memory = 256
}
}
}
}
EOF
Solución de Problemas
# Ver logs del agente Nomad
journalctl -u nomad -f
# Ver eventos del clúster
nomad operator debug -duration=2m -log-level=DEBUG
# Estado de un job fallido
nomad job status webapp
nomad alloc status <alloc-id>
# Logs de una allocation específica
nomad alloc logs <alloc-id>
nomad alloc logs -stderr <alloc-id>
# Acceder a una tarea en ejecución
nomad alloc exec <alloc-id> /bin/sh
# Ver el plan antes de ejecutar para verificar cambios
nomad job plan /tmp/webapp.nomad.hcl
# Forzar un reschedule de una allocation fallida
nomad alloc restart <alloc-id>
# Ver el estado del raft en servidores
nomad operator raft list-peers
Conclusión
HashiCorp Nomad ofrece una experiencia de orquestación simple pero potente, capaz de gestionar cargas de trabajo heterogéneas desde contenedores Docker hasta binarios nativos y trabajos batch en el mismo clúster. Su integración nativa con el ecosistema HashiCorp (Consul, Vault) y su menor complejidad operativa respecto a Kubernetes lo convierten en una excelente opción para equipos que necesitan orquestación sin la sobrecarga administrativa de Kubernetes.


