HashiCorp Consul es una herramienta de descubrimiento de servicios: cada servicio se registra en el catálogo de Consul con su dirección, su puerto y una comprobación de salud, y el resto de la infraestructura lo encuentra por DNS o por la API HTTP. Además incluye un almacén clave-valor (KV) para configuración compartida. En este tutorial montarás un clúster de tres servidores Consul en Ubuntu 24.04, registrarás un servicio web con un health check y lo resolverás por DNS desde el propio sistema.

Requisitos previos

Para seguir esta guía necesitas:

  • Tres servidores con Ubuntu 24.04 LTS, por ejemplo tres VPS de CubePath, con al menos 2 GB de RAM cada uno.
  • Un usuario no root con privilegios sudo en cada servidor.
  • Una red privada entre los tres servidores. En los ejemplos se usan estas direcciones, sustitúyelas por las tuyas:
NombreIP privada
consul-0110.0.1.10
consul-0210.0.1.11
consul-0310.0.1.12
  • Relojes sincronizados (Ubuntu 24.04 trae systemd-timesyncd activo por defecto; compruébalo con timedatectl).

Salvo que se indique lo contrario, ejecuta cada paso en los tres servidores.

Paso 1: Instalar Consul desde el repositorio de HashiCorp

HashiCorp publica paquetes firmados para Ubuntu. Instala las herramientas necesarias y descarga la clave del repositorio 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

Añade el repositorio:

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 Consul:

sudo apt update
sudo apt install -y consul

El paquete crea el usuario de sistema consul, el directorio de datos /opt/consul, la configuración en /etc/consul.d/ y la unidad systemd consul.service, así que no tienes que crear nada de eso a mano. Comprueba la versión instalada:

consul version
Consul v1.x.x
Revision ...

Paso 2: Generar la clave de cifrado gossip

Los agentes Consul se comunican entre sí con un protocolo gossip que conviene cifrar. Genera la clave solo una vez, en consul-01:

consul keygen
pUqJrVyVRj5jsiYEkM/tFQYfWyJIv4s3XkvDwy7Cu5s=

Copia el valor que obtengas: lo usarás en el archivo de configuración de los tres servidores. Trátalo como un secreto.

Paso 3: Configurar los servidores Consul

Abre el archivo de configuración que instaló el paquete:

sudo nano /etc/consul.d/consul.hcl

Sustituye su contenido por el siguiente bloque. Cambia node_name y bind_addr en cada servidor, y pon en encrypt la clave del paso anterior:

datacenter = "dc1"
node_name  = "consul-01"
data_dir   = "/opt/consul"

server           = true
bootstrap_expect = 3

bind_addr   = "10.0.1.10"
client_addr = "127.0.0.1"

retry_join = ["10.0.1.10", "10.0.1.11", "10.0.1.12"]

encrypt = "pUqJrVyVRj5jsiYEkM/tFQYfWyJIv4s3XkvDwy7Cu5s="

ui_config {
  enabled = true
}

log_level = "INFO"

Qué hace cada ajuste:

  • server y bootstrap_expect = 3: el nodo actúa como servidor y el clúster no elige líder hasta que haya tres servidores unidos.
  • bind_addr: la IP privada por la que el nodo habla con el resto del clúster.
  • client_addr = "127.0.0.1": la API HTTP (puerto 8500), la interfaz web y el DNS (puerto 8600) solo escuchan en local. Sin ACLs activadas, exponer la API a otras máquinas permite a cualquiera modificar el catálogo.
  • retry_join: lista de servidores a los que el nodo intenta unirse hasta conseguirlo, sin importar el orden de arranque.

Valida la sintaxis antes de arrancar:

sudo consul validate /etc/consul.d/
Configuration is valid!

Paso 4: Abrir los puertos en el firewall

Consul usa estos puertos entre servidores: 8300/tcp (RPC), 8301/tcp y udp (gossip LAN) y 8302/tcp y udp (gossip WAN). Permite el tráfico solo desde la red privada. Si UFW aún no está activo, permite antes SSH para no perder el acceso:

sudo ufw allow OpenSSH
sudo ufw allow from 10.0.1.0/24 to any port 8300:8302 proto tcp
sudo ufw allow from 10.0.1.0/24 to any port 8301:8302 proto udp
sudo ufw enable

Comprueba las reglas:

sudo ufw status

Paso 5: Arrancar el clúster

Habilita e inicia el servicio en los tres servidores:

sudo systemctl enable --now consul

Comprueba que el servicio está activo:

sudo systemctl status consul

Cuando los tres nodos estén en marcha, lista los miembros del clúster desde cualquiera de ellos:

consul members
Node       Address          Status  Type    Build   Protocol  DC   Partition  Segment
consul-01  10.0.1.10:8301   alive   server  1.x.x   2         dc1  default    <all>
consul-02  10.0.1.11:8301   alive   server  1.x.x   2         dc1  default    <all>
consul-03  10.0.1.12:8301   alive   server  1.x.x   2         dc1  default    <all>

Comprueba que se ha elegido un líder de Raft:

consul operator raft list-peers
Node       ID        Address         State     Voter  RaftProtocol
consul-01  8f3a...   10.0.1.10:8300  leader    true   3
consul-02  1c7e...   10.0.1.11:8300  follower  true   3
consul-03  a42d...   10.0.1.12:8300  follower  true   3

Si un nodo no aparece, revisa sus logs con sudo journalctl -u consul -e.

Paso 6: Registrar un servicio con health check

Como ejemplo, registrarás un servidor web Nginx en consul-01. Instálalo:

sudo apt install -y nginx

Crea el archivo de definición del servicio:

sudo nano /etc/consul.d/web.hcl
service {
  name = "web"
  port = 80
  tags = ["nginx"]

  check {
    id       = "web-http"
    name     = "HTTP en el puerto 80"
    http     = "http://127.0.0.1:80/"
    interval = "10s"
    timeout  = "2s"
  }
}

El health check hace una petición HTTP cada 10 segundos. Mientras responda con un código 2xx el servicio figura como sano; si falla, Consul deja de devolverlo en las consultas DNS.

Ajusta el propietario del archivo y recarga la configuración del agente sin reiniciarlo:

sudo chown consul:consul /etc/consul.d/web.hcl
consul reload
Configuration reload triggered

Comprueba que el servicio aparece en el catálogo y qué nodos lo ofrecen:

consul catalog services
consul catalog nodes -service=web
consul
web
Node       ID        Address    DC
consul-01  8f3a...   10.0.1.10  dc1

Consulta el estado del health check a través de la API HTTP. El parámetro passing filtra solo las instancias sanas:

curl -s "http://127.0.0.1:8500/v1/health/service/web?passing" | python3 -m json.tool | grep -E '"(Status|ServiceName)"'
            "Status": "passing",
            "ServiceName": "web",

Para registrar el mismo servicio en otras máquinas, repite este paso en cada una. En producción, las máquinas que ejecutan aplicaciones no son servidores Consul, sino agentes cliente (misma instalación con server = false y sin bootstrap_expect).

Paso 7: Resolver servicios por DNS

Consul responde consultas DNS en el puerto 8600 con el formato <servicio>.service.consul. Pruébalo con dig:

dig @127.0.0.1 -p 8600 web.service.consul +short
10.0.1.10

Los registros SRV incluyen también el puerto:

dig @127.0.0.1 -p 8600 web.service.consul SRV +short
1 1 80 consul-01.node.dc1.consul.

Para que cualquier programa resuelva nombres .consul sin indicar el puerto, reenvía ese dominio a Consul desde systemd-resolved. Crea el directorio de configuración adicional y el archivo:

sudo mkdir -p /etc/systemd/resolved.conf.d
sudo nano /etc/systemd/resolved.conf.d/consul.conf
[Resolve]
DNS=127.0.0.1:8600
Domains=~consul

Domains=~consul hace que solo las consultas del dominio consul vayan a ese servidor; el resto sigue usando tus DNS habituales. Reinicia el servicio y prueba la resolución normal:

sudo systemctl restart systemd-resolved
resolvectl query web.service.consul
web.service.consul: 10.0.1.10

Ahora cualquier aplicación del servidor puede conectarse a web.service.consul y recibirá solo instancias sanas.

Paso 8: Guardar configuración en el almacén KV

El almacén clave-valor sirve para configuración que comparten varios servicios. Escribe un par de claves:

consul kv put config/web/max_connections 100
consul kv put config/web/log_level info
Success! Data written to: config/web/max_connections
Success! Data written to: config/web/log_level

Léelas de forma individual o por prefijo:

consul kv get config/web/max_connections
consul kv get -recurse config/web/
100
config/web/log_level:info
config/web/max_connections:100

Los datos se replican a los tres servidores, así que puedes leerlos desde cualquiera. Para una copia de seguridad de un prefijo en JSON:

consul kv export config/ > consul-kv-config.json

Para una copia completa del estado del clúster (catálogo, KV y demás), usa un snapshot:

consul snapshot save consul-$(date +%F).snap

Paso 9: Acceder a la interfaz web

La interfaz web escucha en 127.0.0.1:8500, así que no está expuesta a Internet. Accede a ella con un túnel SSH desde tu equipo:

ssh -L 8500:127.0.0.1:8500 your_user@your_server_ip

Con el túnel abierto, visita http://localhost:8500/ui en tu navegador. Verás los servicios, los nodos, el estado de los health checks y el almacén KV.

Solución de problemas

  • consul members solo muestra un nodo: los servidores no se ven entre sí. Comprueba que el puerto 8301 (tcp y udp) está permitido en UFW y que bind_addr es la IP privada correcta. Prueba la conectividad con nc -zv 10.0.1.11 8301.
  • Errores No cluster leader: todavía no hay tres servidores unidos, porque bootstrap_expect = 3. Revisa que los tres servicios están activos.
  • Errores de gossip con encrypt: la clave no es la misma en todos los nodos. Copia exactamente el mismo valor en los tres y reinicia con sudo systemctl restart consul.
  • El servicio no aparece en DNS: el health check está fallando. Mira el estado con la consulta a /v1/health/service/web sin ?passing y revisa que la URL del check responde con curl -I http://127.0.0.1/.
  • Nodo caído que sigue en la lista: si un servidor se ha eliminado definitivamente, sácalo del clúster con consul force-leave consul-03.

Conclusión

Tienes un clúster Consul de tres servidores con cifrado gossip, un servicio registrado con health check, resolución DNS integrada en systemd-resolved y un almacén KV replicado. Como siguientes pasos, activa el sistema de ACLs para proteger la API antes de exponerla fuera de 127.0.0.1, instala agentes cliente en las máquinas que ejecutan tus aplicaciones y usa consul-template para generar configuraciones de Nginx o HAProxy a partir del catálogo.