Skip to content

Consul by HashiCorp

Consul es una herramienta de HashiCorp para descubrimiento de servicios, configuración y segmentación de redes. Permite que los servicios se registren y descubran entre sí, proporciona health checks, y facilita la comunicación segura en arquitecturas distribuidas.

Conceptos clave

  • Service Discovery: Registro y descubrimiento automático de servicios
  • Health Checking: Monitoreo del estado de servicios y nodos
  • KV Store: Almacenamiento clave-valor para configuración
  • Agent: Proceso que se ejecuta en cada nodo del cluster
  • Datacenter: Agrupación lógica de servicios y nodos
  • Service Mesh: Comunicación segura entre servicios con Consul Connect

Ventajas de Consul

  • Descubrimiento dinámico: Servicios se encuentran automáticamente
  • Health checks: Detección automática de servicios caídos
  • Multi-datacenter: Soporte nativo para múltiples datacenters
  • DNS y HTTP: Múltiples interfaces de consulta
  • Service Mesh: Comunicación segura con mTLS
  • KV Store: Configuración centralizada

Esta es una guía esencial para introducirse en el descubrimiento de servicios con Consul

Instalación y configuración de Consul

Instalación de Consul por Sistema Operativo

bash
# Linux/macOS
wget https://releases.hashicorp.com/consul/1.17.0/consul_1.17.0_linux_amd64.zip
unzip consul_1.17.0_linux_amd64.zip
sudo mv consul /usr/local/bin/

# macOS con Homebrew
brew install consul

# Verificar instalación
consul --version

Modo desarrollo (solo para pruebas)

bash
# Iniciar Consul en modo dev
consul agent -dev

# En otra terminal, verificar miembros
consul members

# Ver servicios registrados
consul catalog services

Docker

bash
docker run -d \
  --name consul \
  -p 8500:8500 \
  -p 8600:8600/udp \
  -e CONSUL_BIND_INTERFACE=eth0 \
  hashicorp/consul:latest agent -dev -ui -client=0.0.0.0

# Acceder a UI
open http://localhost:8500

Operaciones básicas

Registrar y consultar servicios

bash
# Registrar servicio manualmente
consul services register -name=web -port=8080

# Listar servicios
consul catalog services

# Consultar servicio específico
consul catalog nodes -service=web

# Consultar via DNS
dig @127.0.0.1 -p 8600 web.service.consul

# Consultar via HTTP API
curl http://localhost:8500/v1/catalog/service/web

# Deregistrar servicio
consul services deregister -id=web

Key-Value Store

bash
# Escribir valor
consul kv put config/app/db_host localhost
consul kv put config/app/db_port 5432

# Leer valor
consul kv get config/app/db_host

# Leer en formato JSON
consul kv get -detailed config/app/db_host

# Listar claves
consul kv get -recurse config/app/

# Eliminar clave
consul kv delete config/app/db_host

Ejemplo con JavaScript (Node.js)

Primero instalas el módulo consul (npm install consul) y como ejemplo esencial tendríamos un código como el siguiente:

javascript
const Consul = require('consul');

const consul = new Consul({
  host: 'localhost',
  port: 8500
});

async function consulEasy() {
  try {
    // Registrar servicio
    await consul.agent.service.register({
      name: 'api-service',
      port: 3000,
      check: {
        http: 'http://localhost:3000/health',
        interval: '10s'
      }
    });
    console.log('Service registered successfully');

    // Consultar servicios
    const services = await consul.catalog.service.nodes('api-service');
    console.log('\nRegistered services:');
    services.forEach(service => {
      console.log(`  - ${service.ServiceName} at ${service.ServiceAddress}:${service.ServicePort}`);
    });

    // Escribir en KV Store
    await consul.kv.set('config/api/timeout', '30');
    await consul.kv.set('config/api/retries', '3');
    console.log('\nConfiguration stored in KV');

    // Leer de KV Store
    const timeout = await consul.kv.get('config/api/timeout');
    const retries = await consul.kv.get('config/api/retries');
    
    console.log('\nConfiguration:');
    console.log(`  Timeout: ${timeout.Value}`);
    console.log(`  Retries: ${retries.Value}`);

    // Listar todas las claves
    const keys = await consul.kv.keys('config/api/');
    console.log('\nAvailable config keys:', keys);

  } catch (error) {
    console.error('Error:', error.message);
  }
}

consulEasy();

Ejemplo con Java (JBang)

Semejante al anterior ejemplo, para Java podemos usar JBang. Veamos el código:

java
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.orbitz.consul:consul-client:1.5.3
//DEPS com.google.guava:guava:32.1.3-jre
//JAVA 21

import com.orbitz.consul.Consul;
import com.orbitz.consul.KeyValueClient;
import com.orbitz.consul.model.health.ServiceHealth;
import com.orbitz.consul.AgentClient;
import com.orbitz.consul.model.agent.ImmutableRegistration;
import com.orbitz.consul.model.agent.Registration;
import java.util.List;

public class ConsulEasy {

    public static void main(String[] args) {
        System.out.println("Consul with Java");
        System.out.println("================\n");

        // Conectar a Consul
        Consul consul = Consul.builder()
            .withUrl("http://localhost:8500")
            .build();

        AgentClient agentClient = consul.agentClient();
        KeyValueClient kvClient = consul.keyValueClient();

        // Registrar servicio
        System.out.println("Registering service...");
        Registration service = ImmutableRegistration.builder()
            .id("java-service-1")
            .name("java-service")
            .port(8080)
            .address("localhost")
            .build();
        
        agentClient.register(service);
        System.out.println("Service registered successfully\n");

        // Consultar servicios
        System.out.println("Querying services...");
        List<ServiceHealth> services = consul.healthClient()
            .getHealthyServiceInstances("java-service")
            .getResponse();
        
        System.out.println("Healthy services:");
        services.forEach(s -> {
            System.out.println("  - " + s.getService().getService() + 
                " at " + s.getService().getAddress() + 
                ":" + s.getService().getPort());
        });

        // Escribir en KV Store
        System.out.println("\nWriting to KV store...");
        kvClient.putValue("config/java/max_connections", "100");
        kvClient.putValue("config/java/timeout", "5000");
        System.out.println("Configuration stored\n");

        // Leer de KV Store
        System.out.println("Reading from KV store...");
        String maxConn = kvClient.getValueAsString("config/java/max_connections").orElse("N/A");
        String timeout = kvClient.getValueAsString("config/java/timeout").orElse("N/A");
        
        System.out.println("Configuration:");
        System.out.println("  Max Connections: " + maxConn);
        System.out.println("  Timeout: " + timeout + "ms");

        // Listar claves
        System.out.println("\nListing keys...");
        List<String> keys = kvClient.getKeys("config/java/");
        System.out.println("Available keys:");
        keys.forEach(key -> System.out.println("  - " + key));
    }
}

Ejecutar:

bash
jbang ConsulEasy.java

Ejemplos para integración con aplicaciones

Definición de servicio con archivo JSON

json
{
  "service": {
    "name": "web",
    "port": 8080,
    "tags": ["production", "v1"],
    "check": {
      "http": "http://localhost:8080/health",
      "interval": "10s",
      "timeout": "1s"
    }
  }
}

Registrar:

bash
consul services register web-service.json

Docker Compose con Consul

yaml
version: '3.8'

services:
  consul:
    image: hashicorp/consul:latest
    ports:
      - "8500:8500"
      - "8600:8600/udp"
    command: agent -dev -ui -client=0.0.0.0

  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    depends_on:
      - consul
    environment:
      CONSUL_HTTP_ADDR: consul:8500

  registrator:
    image: gliderlabs/registrator:latest
    command: -internal consul://consul:8500
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock
    depends_on:
      - consul

Health Check avanzado

bash
# Registrar servicio con múltiples checks
consul services register - <<EOF
{
  "name": "database",
  "port": 5432,
  "checks": [
    {
      "tcp": "localhost:5432",
      "interval": "10s"
    },
    {
      "script": "/usr/local/bin/check_db.sh",
      "interval": "30s"
    }
  ]
}
EOF

Consul es fundamental para arquitecturas de microservicios, proporcionando descubrimiento de servicios, configuración centralizada y comunicación segura en entornos distribuidos.