Service Discovery con Consul
Come i microservizi si trovano e comunicano tra loro usando Consul.
Cos'è Consul?
Consul è un tool di HashiCorp che fornisce:
- Service Discovery - I servizi si registrano e si trovano automaticamente
- Health Checking - Verifica che i servizi siano funzionanti
- Key/Value Store - Configurazione distribuita
- Service Mesh - Comunicazione sicura tra servizi (opzionale)
Registrazione Automatica
Quando definisci un service block nel job Nomad, il servizio viene registrato automaticamente in Consul:
service {
name = "auth"
port = "http"
tags = [
"api",
"v1"
]
check {
type = "http"
path = "/health"
interval = "10s"
timeout = "3s"
}
}
Verifica Registrazione
# Lista servizi registrati
consul catalog services
# Dettagli di un servizio
consul catalog nodes -service=auth
# Health status
consul health checks auth
Service Discovery nei Servizi
Usando DNS
Consul espone i servizi via DNS sulla porta 8600:
# Query DNS
dig @127.0.0.1 -p 8600 auth.service.consul
# Risultato
auth.service.consul. 0 IN A 10.0.1.15
auth.service.consul. 0 IN A 10.0.1.16
Configurazione DNS nel Container
task "devices" {
driver = "docker"
config {
image = "gcr.io/visla-gps/devices:latest"
# Usa Consul come DNS
dns_servers = ["${attr.unique.network.ip-address}"]
}
env {
# Usa nome DNS Consul
AUTH_SERVICE_URL = "http://auth.service.consul:8080"
}
}
Usando HTTP API
// Go example
package main
import (
"fmt"
consul "github.com/hashicorp/consul/api"
)
func main() {
client, _ := consul.NewClient(consul.DefaultConfig())
// Query per il servizio auth
services, _, _ := client.Health().Service("auth", "", true, nil)
for _, entry := range services {
fmt.Printf("Auth service at %s:%d\n",
entry.Service.Address,
entry.Service.Port)
}
}
Template Consul in Nomad
Nomad può generare configurazioni dinamiche usando i dati di Consul:
Service Discovery Template
task "api-gateway" {
template {
data = <<EOF
# upstream per nginx/envoy
{{ range service "auth" }}
upstream auth {
server {{ .Address }}:{{ .Port }};
}
{{ end }}
{{ range service "devices" }}
upstream devices {
server {{ .Address }}:{{ .Port }};
}
{{ end }}
EOF
destination = "local/upstream.conf"
change_mode = "signal"
change_signal = "SIGHUP"
}
}
Config dal KV Store
task "app" {
template {
data = <<EOF
{{ with secret "kv/data/app/config" }}
LOG_LEVEL={{ .Data.data.log_level }}
FEATURE_FLAGS={{ .Data.data.features }}
{{ end }}
EOF
destination = "secrets/config.env"
env = true
}
}
Health Checks
Tipi di Health Check
| Tipo | Uso | Esempio |
|---|---|---|
http | API REST | GET /health |
tcp | Porte TCP | Connessione riuscita |
script | Script custom | exit 0 = healthy |
grpc | gRPC health | Standard gRPC health |
Esempio Completo
service {
name = "auth"
port = "http"
# HTTP check - controlla endpoint /health
check {
name = "HTTP Health"
type = "http"
path = "/health"
interval = "10s"
timeout = "3s"
}
# TCP check - verifica che la porta sia aperta
check {
name = "TCP Port"
type = "tcp"
interval = "10s"
timeout = "2s"
}
# Script check - controllo custom
check {
name = "Database Connection"
type = "script"
command = "/app/check-db.sh"
interval = "30s"
timeout = "10s"
}
}
Health Check Endpoint Consigliato
// handlers/health.go
func HealthHandler(db *sql.DB, redis *redis.Client) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
status := map[string]string{
"status": "healthy",
}
// Check database
if err := db.Ping(); err != nil {
status["database"] = "unhealthy"
status["status"] = "unhealthy"
} else {
status["database"] = "healthy"
}
// Check Redis
if err := redis.Ping(r.Context()).Err(); err != nil {
status["redis"] = "unhealthy"
status["status"] = "unhealthy"
} else {
status["redis"] = "healthy"
}
if status["status"] == "unhealthy" {
w.WriteHeader(http.StatusServiceUnavailable)
}
json.NewEncoder(w).Encode(status)
}
}
Traefik + Consul Integration
Traefik legge automaticamente i servizi da Consul e configura il routing:
Tags per Traefik
service {
name = "auth"
port = "http"
tags = [
# Abilita Traefik per questo servizio
"traefik.enable=true",
# Routing rule
"traefik.http.routers.auth.rule=PathPrefix(`/api/auth`)",
# Entrypoint (http/https)
"traefik.http.routers.auth.entrypoints=websecure",
# TLS
"traefik.http.routers.auth.tls=true",
"traefik.http.routers.auth.tls.certresolver=letsencrypt",
# Middleware (opzionale)
"traefik.http.routers.auth.middlewares=strip-api",
"traefik.http.middlewares.strip-api.stripprefix.prefixes=/api"
]
}
Consul UI
Accedi all'UI Consul per visualizzare:
- Servizi registrati
- Health status
- Nodi del cluster
- Key/Value store
http://<consul-server>:8500/ui

Best Practices
-
Usa sempre health checks - Senza health checks, Consul non può rimuovere istanze non funzionanti
-
Timeout appropriati - Health check timeout < interval
-
Naming convention - Usa nomi consistenti per i servizi
-
Tags per versioning - Aggiungi tag come
v1,canaryper routing avanzato -
Monitor health - Configura alerting su servizi unhealthy
# Alert quando un servizio è unhealthy
consul watch -type=checks -state=critical /scripts/alert.sh