HashiCorp Nomad es un orquestador de cargas de trabajo: tú describes en un archivo job qué quieres ejecutar, cuántas copias y con qué recursos, y Nomad decide en qué nodos colocarlas y las mantiene en marcha. Es más sencillo de operar que Kubernetes (un único binario) y, además de contenedores Docker, puede ejecutar binarios nativos. En este tutorial montarás en Ubuntu 24.04 un clúster Nomad con un servidor y dos clientes Docker, desplegarás un servicio web con health checks y lo actualizarás de forma progresiva.
Requisitos previos
Para seguir esta guía necesitas:
- Tres servidores con Ubuntu 24.04 LTS, por ejemplo tres VPS de CubePath, con al menos 1 GB de RAM el servidor y 2 GB cada cliente.
- Un usuario no root con privilegios
sudoen cada uno. - Una red privada entre los tres. En los ejemplos se usan estas direcciones, sustitúyelas por las tuyas:
| Nombre | Rol | IP privada |
|---|---|---|
| nomad-server | Servidor | 10.0.1.10 |
| nomad-client-01 | Cliente | 10.0.1.21 |
| nomad-client-02 | Cliente | 10.0.1.22 |
Los servidores Nomad guardan el estado del clúster y toman las decisiones de planificación; los clientes ejecutan las tareas. Esta guía usa un solo servidor para simplificar. En producción se usan tres servidores (con bootstrap_expect = 3) para que el clúster siga funcionando si cae uno.
Paso 1: Instalar Nomad
Ejecuta este paso en los tres nodos. Añade el repositorio oficial de HashiCorp con su clave en /etc/apt/keyrings:
sudo apt update
sudo apt install -y wget gpg lsb-release
sudo install -m 0755 -d /etc/apt/keyrings
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
Instala Nomad:
sudo apt update
sudo apt install -y nomad
nomad version
Nomad v1.x.x
BuildDate ...
Revision ...
El paquete instala la configuración en /etc/nomad.d/nomad.hcl, el directorio de datos /opt/nomad/data y la unidad nomad.service. El agente se ejecuta como root porque los clientes necesitan gestionar contenedores, cgroups y redes.
Paso 2: Instalar Docker en los clientes
El driver docker de Nomad necesita Docker Engine en cada nodo cliente. En nomad-client-01 y nomad-client-02, instala el paquete de Ubuntu (si prefieres el repositorio oficial de Docker, el resultado para Nomad es el mismo):
sudo apt install -y docker.io
sudo systemctl enable --now docker
Comprueba que funciona:
sudo docker run --rm hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
Paso 3: Generar la clave de cifrado gossip
Los servidores Nomad se comunican entre sí con un protocolo gossip que conviene cifrar. Genera una clave en nomad-server:
nomad operator gossip keyring generate
cg8StVXbQJ0gPvMd9o7yrg==
Guarda el valor, lo usarás en la configuración del servidor. Con varios servidores, todos deben tener la misma clave.
Paso 4: Configurar el servidor
En nomad-server, abre el archivo de configuración:
sudo nano /etc/nomad.d/nomad.hcl
Sustituye su contenido por el siguiente, con tu IP privada y la clave del paso anterior:
datacenter = "dc1"
data_dir = "/opt/nomad/data"
bind_addr = "0.0.0.0"
advertise {
http = "10.0.1.10"
rpc = "10.0.1.10"
serf = "10.0.1.10"
}
server {
enabled = true
bootstrap_expect = 1
encrypt = "cg8StVXbQJ0gPvMd9o7yrg=="
}
client {
enabled = false
}
bind_addr = "0.0.0.0" hace que Nomad escuche en todas las interfaces, y el bloque advertise indica qué IP deben usar el resto de nodos para llegar a este. La API HTTP (puerto 4646) no tiene autenticación mientras no actives las ACLs, así que en el paso 6 la limitarás con el firewall.
Valida la configuración, arranca el servicio y comprueba que el servidor se ha elegido líder:
sudo nomad config validate /etc/nomad.d/
sudo systemctl enable --now nomad
nomad server members
Name Address Port Status Leader Raft Version Build Datacenter Region
nomad-server.global 10.0.1.10 4648 alive true 3 1.x.x dc1 global
Paso 5: Configurar los clientes
En cada cliente, abre el mismo archivo:
sudo nano /etc/nomad.d/nomad.hcl
Sustituye su contenido por el siguiente. Cambia las IP del bloque advertise por la del cliente en el que estés:
datacenter = "dc1"
data_dir = "/opt/nomad/data"
bind_addr = "0.0.0.0"
advertise {
http = "10.0.1.21"
rpc = "10.0.1.21"
serf = "10.0.1.21"
}
client {
enabled = true
servers = ["10.0.1.10:4647"]
meta {
env = "production"
}
}
plugin "docker" {
config {
allow_privileged = false
}
}
serversindica a qué servidor debe registrarse el cliente, por el puerto RPC 4647.metaañade etiquetas libres al nodo que después puedes usar en los jobs conconstraint.allow_privileged = falseimpide que un job lance contenedores privilegiados.
Arranca Nomad en los dos clientes:
sudo systemctl enable --now nomad
Desde cualquier nodo, comprueba que los clientes aparecen como ready y que el driver Docker está detectado:
nomad node status
ID Node Pool DC Name Class Drain Eligibility Status
5b1e2c3a default dc1 nomad-client-02 <none> false eligible ready
a7f09d41 default dc1 nomad-client-01 <none> false eligible ready
nomad node status -self -verbose | grep -i 'driver.docker '
Ejecútalo en un cliente: debe mostrar driver.docker = 1. Si no aparece, revisa que Docker está en marcha y consulta sudo journalctl -u nomad -e.
Paso 6: Proteger los puertos con UFW
Nomad usa el puerto 4646 para la API HTTP y la interfaz web, 4647 para RPC y 4648 para gossip entre servidores. Permite el tráfico solo desde la red privada en los tres nodos:
sudo ufw allow OpenSSH
sudo ufw allow from 10.0.1.0/24 to any port 4646:4648 proto tcp
sudo ufw allow from 10.0.1.0/24 to any port 4648 proto udp
sudo ufw enable
AdvertenciaDocker publica los puertos de los contenedores con sus propias reglas de iptables, que se evalúan antes que las de UFW. Los puertos dinámicos que Nomad asigna a los contenedores (rango 20000-32000 por defecto) serán accesibles desde fuera aunque UFW no los permita. Expón los servicios a Internet a través de un balanceador o proxy inverso, y filtra ese rango en el firewall de red si lo necesitas.
Paso 7: Desplegar tu primer job
Un job describe grupos de tareas (group) que Nomad coloca juntos en un mismo cliente. Crea el archivo del job en el servidor, en tu directorio personal:
nano ~/web.nomad.hcl
job "web" {
datacenters = ["dc1"]
type = "service"
group "web" {
count = 2
network {
port "http" {
to = 80
}
}
update {
max_parallel = 1
min_healthy_time = "10s"
healthy_deadline = "2m"
auto_revert = true
}
service {
name = "web"
port = "http"
provider = "nomad"
check {
type = "http"
path = "/"
interval = "10s"
timeout = "2s"
}
}
task "nginx" {
driver = "docker"
config {
image = "nginx:1.26-alpine"
ports = ["http"]
}
resources {
cpu = 200
memory = 128
}
}
}
}
Qué define este job:
count = 2: dos instancias del grupo, que Nomad reparte entre los clientes disponibles.port "http" { to = 80 }: Nomad asigna un puerto dinámico en el host y lo redirige al puerto 80 del contenedor.serviceconprovider = "nomad": registra cada instancia en el catálogo de servicios integrado de Nomad, con un health check HTTP. No necesitas Consul para esto.update: en cada despliegue actualiza las instancias de una en una y vuelve a la versión anterior si la nueva no supera los health checks.resources: CPU en MHz y memoria en MB reservados para la tarea.
Revisa qué haría Nomad sin aplicar nada:
nomad job plan ~/web.nomad.hcl
+ Job: "web"
+ Task Group: "web" (2 create)
+ Task: "nginx" (forces create)
Scheduler dry-run:
- All tasks successfully allocated.
Lanza el job:
nomad job run ~/web.nomad.hcl
Nomad muestra el progreso del despliegue y termina cuando las dos instancias están sanas. Consulta el estado del job:
nomad job status web
Allocations
ID Node ID Task Group Version Desired Status Created Modified
3f0a91c2 a7f09d41 web 0 run running 45s ago 30s ago
b82d7e55 5b1e2c3a web 0 run running 45s ago 31s ago
Cada fila es una allocation, es decir, una instancia del grupo colocada en un cliente concreto.
Paso 8: Descubrir el servicio y ver los logs
Consulta en qué direcciones y puertos está disponible el servicio:
nomad service info web
Job ID Address Tags Node ID Alloc ID
web 10.0.1.21:24561 [] a7f09d41 3f0a91c2
web 10.0.1.22:28734 [] 5b1e2c3a b82d7e55
Pruébalo con una petición HTTP a una de esas direcciones:
curl -I http://10.0.1.21:24561
HTTP/1.1 200 OK
Server: nginx/1.26.x
Otras aplicaciones del clúster pueden leer estas direcciones con plantillas en el bloque template de sus jobs (función nomadService), de modo que un proxy inverso se reconfigura solo cuando las instancias cambian.
Para ver los logs de una instancia, usa el ID de su allocation:
nomad alloc logs 3f0a91c2 nginx
Y si necesitas entrar en el contenedor:
nomad alloc exec -task nginx 3f0a91c2 /bin/sh
Paso 9: Escalar y actualizar el job
Para cambiar el número de instancias o la imagen, edita el archivo del job y vuelve a ejecutarlo. Cambia count = 2 por count = 3 y la imagen por nginx:1.27-alpine:
nano ~/web.nomad.hcl
Revisa el plan: verás una instancia nueva y las existentes marcadas para actualizarse.
nomad job plan ~/web.nomad.hcl
nomad job run ~/web.nomad.hcl
Gracias al bloque update, Nomad sustituye las instancias de una en una y espera a que cada una pase su health check antes de seguir. Consulta el despliegue:
nomad job status web
Si algo sale mal y auto_revert no lo ha resuelto por sí mismo, vuelve a la versión anterior del job. Consulta el historial y revierte a la versión 0:
nomad job history web
nomad job revert web 0
Para eliminar el job y detener todas sus instancias:
nomad job stop -purge web
Paso 10: Acceder a la interfaz web
La interfaz web de Nomad está en el puerto 4646 del servidor, que solo es accesible desde la red privada. Ábrela desde tu equipo con un túnel SSH:
ssh -L 4646:10.0.1.10:4646 your_user@your_server_ip
Con el túnel abierto, visita http://localhost:4646/ui. Verás los jobs, las allocations, los clientes y el uso de recursos de cada uno.
Solución de problemas
- Un cliente no aparece en
nomad node status: comprueba que llega al servidor connc -zv 10.0.1.10 4647y que la IP deserverses correcta. Revisasudo journalctl -u nomad -een el cliente. Constraint "missing drivers" filtered 2 nodes: Docker no está instalado o no está en marcha en los clientes. Arráncalo consudo systemctl start dockery reinicia Nomad.Dimension "memory" exhausted: los clientes no tienen memoria libre para las instancias pedidas. Reducememoryenresourceso añade clientes.- El despliegue no termina y se revierte: el health check falla. Mira el motivo con
nomad alloc status <alloc-id>y los logs de la tarea connomad alloc logs.
Conclusión
Tienes un clúster Nomad con un servidor y dos clientes Docker, y un servicio web desplegado con health checks, descubrimiento de servicios integrado y actualizaciones progresivas con vuelta atrás automática. Como siguientes pasos, activa las ACLs con nomad acl bootstrap antes de dar acceso a otras personas, amplía el clúster a tres servidores para tener alta disponibilidad y pon delante de tus servicios un proxy inverso que lea el catálogo con nomadService.
