TL;DR — Resumen Rápido

Domina HashiCorp Consul para service discovery y service mesh: arquitectura, health checks, ACLs, proxies Envoy y federación multi-datacenter.

HashiCorp Consul es una plataforma de red de servicios distribuida y de alta disponibilidad que resuelve uno de los problemas más difíciles de los microservicios: ¿cómo se encuentran los servicios entre sí, verifican que el otro está sano y se comunican de forma segura en una infraestructura dinámica donde las IPs cambian constantemente? Consul responde las tres preguntas mediante un plano de control unificado que combina service discovery, health checking, almacén KV y un service mesh completo con aplicación de TLS mutuo. Esta guía cubre todas las capacidades principales de Consul, desde la configuración de un clúster de tres nodos hasta los proxies sidecar Envoy y la federación multi-datacenter.

Prerrequisitos

Antes de continuar, asegúrate de tener disponible lo siguiente:

  • Host Linux o macOS — Consul funciona en cualquier sistema tipo Unix y Windows; los ejemplos usan Linux.
  • Docker 24+ — para el ejemplo de producción con Docker Compose al final de esta guía.
  • curl y dig — para probar las respuestas DNS y de la API HTTP.
  • Comprensión básica de microservicios — familiaridad con conceptos como balanceo de carga, health checks y TLS.
  • Binario Consul 1.17+ — instala vía el repositorio APT/YUM de HashiCorp o descarga desde releases.hashicorp.com.

Instalación en Ubuntu/Debian:

wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install consul
consul version
# Consul v1.20.2

Arquitectura de Consul

Agentes: Servidores vs Clientes

Cada nodo en un clúster Consul ejecuta un agente Consul en uno de dos modos:

Los agentes servidor forman el plano de control. Participan en el algoritmo de consenso Raft para elegir un líder, almacenan el estado autorizado del clúster (servicios, resultados de health checks, datos KV, políticas ACL) y replican ese estado. Debes ejecutar un número impar de servidores: 3 servidores toleran 1 fallo; 5 servidores toleran 2 fallos.

Los agentes cliente se ejecutan en cada nodo no servidor (tus hosts de aplicación). Registran servicios y health checks locales, reenvían consultas a los servidores y participan en la red gossip. Los clientes son livianos — no almacenan estado del clúster.

Protocolo Gossip (Serf)

Consul usa Serf, una implementación del protocolo gossip SWIM, para la membresía del clúster y la detección de fallos. Todos los agentes participan en dos pools de gossip:

  • Pool gossip LAN — agentes dentro del mismo datacenter. Usado para el descubrimiento de miembros, propagación de estado de salud y difusión de eventos.
  • Pool gossip WAN — servidores entre datacenters. Habilita la federación multi-datacenter.

Consenso Raft

Raft se usa solo entre servidores Consul para la consistencia fuerte del estado del clúster. El líder acepta todas las operaciones de escritura y replica las entradas de log en los seguidores antes de confirmar.

Modelo de Datacenter

Un datacenter Consul es una unidad aislada: un conjunto de servidores que comparten un pool gossip LAN. Múltiples datacenters se comunican vía gossip WAN o mesh gateways.

Métodos de Instalación

Instalación del Binario

CONSUL_VERSION="1.20.2"
wget "https://releases.hashicorp.com/consul/${CONSUL_VERSION}/consul_${CONSUL_VERSION}_linux_amd64.zip"
unzip consul_${CONSUL_VERSION}_linux_amd64.zip
sudo install consul /usr/local/bin/
consul version

Docker

docker run -d \
  --name consul-dev \
  -p 8500:8500 \
  -p 8600:8600/udp \
  hashicorp/consul:1.20 agent -dev -client=0.0.0.0

Helm Chart para Kubernetes

helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
helm install consul hashicorp/consul \
  --namespace consul \
  --create-namespace \
  --values consul-values.yaml

consul-values.yaml mínimo:

global:
  name: consul
  datacenter: dc1
  tls:
    enabled: true
  acls:
    manageSystemACLs: true
server:
  replicas: 3
  bootstrapExpect: 3
connectInject:
  enabled: true

Configuración del Clúster de Servidores

Crea /etc/consul.d/consul.hcl en cada nodo servidor:

datacenter  = "dc1"
data_dir    = "/opt/consul"
log_level   = "INFO"
node_name   = "consul-server-01"
server      = true
bootstrap_expect = 3

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

encrypt = "CLAVE_GOSSIP_BASE64_AQUI"

ui_config {
  enabled = true
}
client_addr    = "0.0.0.0"
bind_addr      = "{{ GetPrivateIP }}"
advertise_addr = "{{ GetPrivateIP }}"

Genera la clave gossip una vez y úsala en todos los nodos:

consul keygen
# Salida: j5Y3fIFjQKs2b9s9rG2ufA==

Inicia el clúster:

sudo systemctl enable consul && sudo systemctl start consul
consul members
consul operator raft list-peers

Registro de Servicios

Archivos de Definición de Servicio

Crea un archivo JSON en /etc/consul.d/ en el agente cliente:

{
  "service": {
    "id":   "web-01",
    "name": "web",
    "port": 8080,
    "tags": ["v2", "primary"],
    "check": {
      "http":     "http://localhost:8080/health",
      "interval": "10s",
      "timeout":  "3s",
      "deregister_critical_service_after": "90s"
    }
  }
}

Registro vía API HTTP

curl -s -X PUT http://localhost:8500/v1/agent/service/register \
  -H "Content-Type: application/json" \
  -d '{
    "ID":   "api-01",
    "Name": "api",
    "Port": 3000,
    "Check": {
      "HTTP":     "http://localhost:3000/healthz",
      "Interval": "15s",
      "Timeout":  "5s"
    }
  }'

Catálogo vs Agente

La API de agente (/v1/agent/service/*) gestiona los servicios registrados en el agente local. La API de catálogo (/v1/catalog/service/*) consulta el registro global agregado de todos los agentes:

# Vista del agente — solo nodo local
curl http://localhost:8500/v1/agent/services | jq .

# Vista del catálogo — todos los nodos
curl http://localhost:8500/v1/catalog/service/web | jq '[.[] | {nodo: .Node, dir: .ServiceAddress, puerto: .ServicePort}]'

Health Checks

Consul soporta seis tipos de health checks, todos con parámetros interval, timeout y deregister_critical_service_after.

HTTP — Consul realiza un GET HTTP; 2xx = passing, 429 = warning, cualquier otro = critical.

TCP — Abre una conexión TCP al host:puerto especificado.

Script — Ejecuta un comando de shell; código de salida 0 = passing, 1 = warning, otro = critical.

TTL — La aplicación misma debe llamar a la API de Consul para actualizar su estado de salud.

gRPC — Para servicios que implementan el protocolo de salud gRPC.

Ejemplo de check TTL:

# Reportar passing
curl -X PUT http://localhost:8500/v1/agent/check/pass/service:worker-01 \
  -d "Procesando cola normalmente"

Establece siempre deregister_critical_service_after para eliminar automáticamente servicios con fallos continuos:

"deregister_critical_service_after": "90s"

Interfaz DNS

Consul ejecuta un servidor DNS en el puerto 8600. Los servicios se resuelven en <servicio>.service.<datacenter>.consul.

# Resolver todas las instancias sanas de "web"
dig @127.0.0.1 -p 8600 web.service.consul A

# Con registro SRV (incluye puerto)
dig @127.0.0.1 -p 8600 web.service.consul SRV

# Por etiqueta
dig @127.0.0.1 -p 8600 primary.web.service.consul A

Reenvío DNS con systemd-resolved

# /etc/systemd/resolved.conf.d/consul.conf
[Resolve]
DNS=127.0.0.1:8600
Domains=~consul
sudo systemctl restart systemd-resolved
# Ahora puedes usar: curl http://web.service.consul:8080/

Prepared Queries con Failover

curl -X POST http://localhost:8500/v1/query \
  -d '{
    "Name": "web",
    "Service": {
      "Service":     "web",
      "OnlyPassing": true,
      "Failover": {
        "NearestN":    2,
        "Datacenters": ["dc2", "dc3"]
      }
    }
  }'

dig @127.0.0.1 -p 8600 web.query.consul A

Service Mesh / Connect

Consul Connect usa Envoy como proxy sidecar y emite certificados TLS de corta duración para TLS mutuo entre servicios.

Registro del Sidecar

{
  "service": {
    "name": "web",
    "port": 8080,
    "connect": {
      "sidecar_service": {
        "port": 21000,
        "proxy": {
          "upstreams": [
            {
              "destination_name": "api",
              "local_bind_port":  9191
            }
          ]
        }
      }
    }
  }
}
consul connect envoy -sidecar-for web &

Intenciones

# Permitir que web llegue a api
consul intention create web api

# Denegar una ruta específica
consul intention create -deny payments logging

# Listar todas las intenciones
consul intention list

Almacén KV

# Escribir un valor
consul kv put config/app/log_level info

# Leer un valor
consul kv get config/app/log_level

# Listar claves bajo un prefijo
consul kv list config/app/

# Eliminar un árbol
consul kv delete -recurse config/app/

consul-template

# Instalar
wget https://releases.hashicorp.com/consul-template/0.39.0/consul-template_0.39.0_linux_amd64.zip
unzip consul-template_0.39.0_linux_amd64.zip
sudo install consul-template /usr/local/bin/

Plantilla nginx.conf.ctmpl:

upstream web_backend {
  {{ range service "web" }}
  server {{ .Address }}:{{ .Port }};
  {{ end }}
}
consul-template \
  -template "nginx.conf.ctmpl:/etc/nginx/conf.d/web.conf:nginx -s reload" \
  -once

Sistema ACL

# Habilitar ACLs en consul.hcl
cat >> /etc/consul.d/consul.hcl <<EOF
acl {
  enabled                  = true
  default_policy           = "deny"
  enable_token_persistence = true
}
EOF

# Inicializar — hazlo una sola vez y guarda la salida
consul acl bootstrap
# SecretID: db14c...   ← este es tu token bootstrap (root)

export CONSUL_HTTP_TOKEN="db14c..."

# Crear una política
consul acl policy create \
  -name "lectura-servicio-web" \
  -rules 'service "web" { policy = "read" }
node_prefix "" { policy = "read" }'

# Crear un token con esta política
consul acl token create \
  -description "Token lectura servicio web" \
  -policy-name "lectura-servicio-web"

IU de Consul

La IU web de Consul está disponible en http://localhost:8500/ui cuando ui_config { enabled = true } está configurado. Secciones principales:

  • Servicios — todos los servicios registrados con estado de salud, etiquetas y conteo de instancias.
  • Nodos — todos los agentes con su estado de salud y servicios registrados.
  • Key/Value — navegador del almacén KV con soporte para crear, editar y eliminar.
  • Intenciones — visualiza y gestiona las reglas de autorización entre servicios.
  • Controles de acceso — gestiona tokens, políticas y roles (visible solo cuando los ACLs están habilitados).

Federación Multi-Datacenter

Gossip WAN

# En los servidores de dc2, unirse al pool WAN de dc1
consul join -wan 10.0.1.11

# Consultar el servicio web en dc2
dig @127.0.0.1 -p 8600 web.service.dc2.consul A
curl "http://localhost:8500/v1/catalog/service/web?dc=dc2"

Mesh Gateways

Los mesh gateways son el enfoque moderno — hacen proxy del tráfico entre datacenters sin exponer los servicios internos a la WAN:

consul connect envoy -gateway=mesh -register -address $(hostname -I | awk '{print $1}'):8443

Comparación: Consul vs Alternativas

CaracterísticaConsuletcdZooKeeperEurekaIstioLinkerd
Service DiscoveryDNS + API HTTPSolo APISolo APIAPI HTTPVía KubernetesVía Kubernetes
Health ChecksIntegrado (6 tipos)Sin nativeSin nativeHeartbeat clienteProbes KubernetesProbes Kubernetes
Service MeshSí (Connect)NoNoNoSí (Envoy)Sí (proxy2)
Almacén KVSí (uso primario)NoNoNo
Sistema ACLSí (rico)BásicoSASL/ACLNoRBACRBAC
Multi-datacenterNativoNoNoPeers replicadosNoNo
ConsensoRaftRaftZABEventualN/AN/A
Sin KubernetesNoNo

Despliegue en Producción con Docker Compose

name: consul-cluster

services:
  consul-server-1:
    image: hashicorp/consul:1.20
    command: >
      agent -server
      -node=server-1
      -bootstrap-expect=3
      -datacenter=dc1
      -data-dir=/consul/data
      -bind=0.0.0.0
      -client=0.0.0.0
      -retry-join=consul-server-2
      -retry-join=consul-server-3
      -ui
    volumes:
      - consul-server-1-data:/consul/data
      - ./consul-config:/consul/config:ro
    ports:
      - "8500:8500"
      - "8600:8600/udp"
    networks:
      - consul-net
    restart: unless-stopped

  consul-server-2:
    image: hashicorp/consul:1.20
    command: >
      agent -server
      -node=server-2
      -bootstrap-expect=3
      -datacenter=dc1
      -data-dir=/consul/data
      -bind=0.0.0.0
      -client=0.0.0.0
      -retry-join=consul-server-1
      -retry-join=consul-server-3
    volumes:
      - consul-server-2-data:/consul/data
      - ./consul-config:/consul/config:ro
    networks:
      - consul-net
    restart: unless-stopped

  consul-server-3:
    image: hashicorp/consul:1.20
    command: >
      agent -server
      -node=server-3
      -bootstrap-expect=3
      -datacenter=dc1
      -data-dir=/consul/data
      -bind=0.0.0.0
      -client=0.0.0.0
      -retry-join=consul-server-1
      -retry-join=consul-server-2
    volumes:
      - consul-server-3-data:/consul/data
      - ./consul-config:/consul/config:ro
    networks:
      - consul-net
    restart: unless-stopped

  consul-client:
    image: hashicorp/consul:1.20
    command: >
      agent
      -node=client-1
      -datacenter=dc1
      -data-dir=/consul/data
      -bind=0.0.0.0
      -client=0.0.0.0
      -retry-join=consul-server-1
    volumes:
      - consul-client-data:/consul/data
      - ./consul-config:/consul/config:ro
      - ./services:/consul/services:ro
    networks:
      - consul-net
    restart: unless-stopped

volumes:
  consul-server-1-data:
  consul-server-2-data:
  consul-server-3-data:
  consul-client-data:

networks:
  consul-net:
    driver: bridge
GOSSIP_KEY=$(docker run --rm hashicorp/consul:1.20 keygen)
echo "encrypt = \"${GOSSIP_KEY}\"" > consul-config/encryption.hcl
docker compose up -d
sleep 5
docker compose exec consul-server-1 consul members

Problemas Comunes y Casos Límite

  • bootstrap_expect inconsistente — todos los servidores deben tener el mismo valor; una discrepancia impide la formación del clúster.
  • Rotación de clave gossip — usa consul keyring para rotar; todos los nodos deben completar la rotación antes de eliminar la clave anterior.
  • Distribución de tokens ACL — evita tokens en archivos de configuración; usa el motor de secretos Consul de Vault o Kubernetes secrets.
  • Compatibilidad de versiones Envoy — siempre alinea la versión de Envoy con la matriz de compatibilidad de Consul; una versión no soportada causa fallos silenciosos en el proxy.
  • Timeouts en health checks — establece timeout menor que interval para evitar ejecuciones de checks superpuestas.

Resumen

  • Tres o cinco servidores con consenso Raft forman el plano de control; usa un número impar para tolerancia de quórum.
  • Gossip (Serf) maneja la membresía del clúster y la detección de fallos a escala O(log N) en LAN y WAN.
  • Las definiciones de servicio registran servicios con health checks ricos que eliminan automáticamente instancias no saludables del DNS.
  • La interfaz DNS en el puerto 8600 permite resolver servicio.service.consul sin cambios de código.
  • Consul Connect agrega mTLS entre servicios vía sidecars Envoy; las intenciones aplican autorización sin tocar el código de la aplicación.
  • Las prepared queries proporcionan failover a nivel DNS entre datacenters para alta disponibilidad.
  • El almacén KV combinado con consul-template permite gestión dinámica de configuración.
  • Los ACLs con default_policy = deny bloquean el clúster; usa auth methods para emisión de tokens sin secretos.

Artículos Relacionados