Ejemplos
Recetario de fragmentos válidos contra el esquema, con la referencia de claves en Configuración. Los que incluyen [listener.web] y sus backends se pueden validar tal cual; los demás se añaden a una configuración existente.
Proxy TLS a dos backends con health check
schema_version = 1
[listener.web]
listen = "0.0.0.0:443"
mode = "http"
backend = "api"
[listener.web.tls]
cert = "certs/fullchain.pem"
key = "certs/privkey.pem"
alpn = ["h2", "http/1.1"]
min_version = "1.2"
[backend.api]
servers = [
{ addr = "10.0.0.11:9000", weight = 3 },
{ addr = "10.0.0.12:9000", weight = 1 },
]
[backend.api.health_check]
type = "http"
interval = "2s"
timeout = "1s"
fall = 3
rise = 2
http_path = "/healthz"
expect_status = "200-299"
Tres sondas fallidas sacan al servidor del reparto; dos éxitos lo devuelven. Los pesos 3:1 reparten tres de cada cuatro peticiones al primero.
Balanceo least-conn
[backend.api]
balance = "leastconn"
servers = [
{ addr = "10.0.0.11:9000", weight = 2 },
{ addr = "10.0.0.12:9000", weight = 1 },
{ addr = "10.0.0.13:9000", weight = 1, backup = true },
]
slow_start = "30s"
leastconn elige por conexiones activas divididas por peso; slow_start sube el peso de un servidor recuperado durante 30 s, y el backup solo entra cuando no queda ningún primario elegible.
Límites de conexiones
[listener.web]
listen = "0.0.0.0:8080"
mode = "http"
backend = "api"
maxconn = 2048
[listener.web.limits]
max_head_bytes = 16384
max_headers = 100
max_body_bytes = 1048576
[backend.api]
servers = [
{ addr = "10.0.0.11:9000", maxconn = 256 },
{ addr = "10.0.0.12:9000", maxconn = 256 },
]
maxqueue = 128
timeout_queue = "500ms"
Al tope del listener se rechaza al recién llegado; al tope de un servidor, el balanceador deja de elegirlo. Si todos lo alcanzan, se espera en la cola y, agotado timeout_queue, se responde 503.
Caché de respuestas
[listener.web.cache]
default_ttl = "60s"
max_memory_bytes = 67108864
max_entry_bytes = 1048576
disk_path = "cache/web"
Se guardan GET con estados 200, 301, 308, 404 y 410 y sin Set-Cookie; las peticiones con Cookie, Authorization, Range o no-store ni se consultan ni se guardan. La respuesta lleva X-Cache: HIT, MISS o REVALIDATED.
Rate limit por regla
[[listener.web.rules]]
name = "api"
host = "api.example.com"
path_prefix = "/api/"
use_backend = "api"
rate_limit = { rate = 10, per = "1s", burst = 20 }
rate = 10 con per = "1s" son 10 peticiones por segundo; con per = "60s", 10 por minuto. Sin burst, el cubo tiene capacidad rate. Con varias instancias, el límite es por instancia.
Acceso por ruta a distintos backends
[listener.web]
listen = "0.0.0.0:8080"
mode = "http"
backend = "web"
[[listener.web.rules]]
name = "api-v1"
path_prefix = "/api/v1/"
use_backend = "api"
[listener.web.rules.rewrite]
path_prefix_rewrite = "/api/"
[[listener.web.rules]]
name = "admin-writes"
path_prefix = "/admin/"
methods = ["POST", "PUT", "DELETE"]
use_backend = "admin"
[[listener.web.rules]]
name = "healthz"
path = "/healthz"
[listener.web.rules.direct_response]
status = 200
content_type = "text/plain"
body = "ok"
[backend.web]
server = "10.0.0.21:8080"
[backend.api]
server = "10.0.0.22:9000"
[backend.admin]
server = "10.0.0.23:9000"
/api/v1/users llega a api como /api/users; /healthz se contesta localmente; lo demás cae en web. El orden importa, y path_prefix no normaliza: /api/ casa también con rutas más largas.
Despliegue Docker con Compose
services:
eqproxy:
image: ghcr.io/exyonq/eqproxy:0.9.1
command: ["run", "--config", "/etc/eqproxy/eqproxy.toml"]
ports: ["8080:8080", "127.0.0.1:9001:9001"]
volumes:
- ./eqproxy.toml:/etc/eqproxy/eqproxy.toml:ro
- ./admin.token:/etc/eqproxy/admin.token:ro
- ./certs:/certs:ro
- eqproxy-state:/var/lib/eqproxy
depends_on: [app1, app2]
restart: unless-stopped
app1:
image: ghcr.io/acme/storefront:1.0
expose: ["9000"]
app2:
image: ghcr.io/acme/storefront:1.0
expose: ["9000"]
volumes:
eqproxy-state:
schema_version = 1
[admin]
listen = "127.0.0.1:9001"
token_file = "/etc/eqproxy/admin.token"
[listener.web]
listen = "0.0.0.0:8080"
mode = "http"
backend = "app"
[backend.app]
servers = [
{ addr = "app1:9000", weight = 1 },
{ addr = "app2:9000", weight = 1 },
]
[backend.app.health_check]
type = "tcp"
interval = "2s"
fall = 3
rise = 2
Los nombres de servicio se resuelven por DNS en segundo plano, sin bloquear los listeners. El fichero montado tiene que ser legible por el uid 65532, y el volumen de estado conserva el historial de config apply. Para recargar en caliente, edita el TOML y manda la señal: docker kill --signal=HUP eqproxy.