Reverse Proxy

Wdrożenie

Docker, Docker Compose, HTTPS termination, rozproszony rate limiting z Redis i graceful shutdown. Gotowe przepisy produkcyjne.

Docker#

bash
cd reverse-proxy
docker build -t reverse-proxy .
docker run --rm -p 8080:8080 -e ADMIN_TOKEN=change-me reverse-proxy

Obraz jest utwardzony: multi-stage, non-root, read-only FS, dropped capabilities.

Docker Compose#

bash
ADMIN_TOKEN=change-me docker compose up --build

Opcjonalne profile:

bash
docker compose --profile monitoring up -d
docker compose --profile redis up -d

Przykład: compose z monitoringiem i Redis#

yamldocker-compose.override.yml
services:
  proxy:
    environment:
      ADMIN_TOKEN: ${ADMIN_TOKEN}
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    volumes:
      - ./config/config.docker.yaml:/etc/proxy/config.yaml:ro
    ports:
      - "8080:8080"
    depends_on:
      - backend1
      - backend2
      - redis

HTTPS termination#

yaml
tls:
  cert_file: /etc/tls/server.crt
  key_file: /etc/tls/server.key
bash
docker run --rm -p 443:8080 \
  -e ADMIN_TOKEN=$ADMIN_TOKEN \
  -v /etc/tls:/etc/tls:ro \
  reverse-proxy
  • Oba pliki muszą istnieć, inaczej proxy nie wystartuje.
  • TLS 1.2+.
  • Jeśli TLS terminuje CDN/LB przed proxy, ustaw rate_limit.trusted_proxies na adresy tego LB.

Rozproszony rate limiting#

yaml
redis:
  addr: redis:6379
bash
export REDIS_PASSWORD=secret

Limity są atomowe (Lua token bucket), więc wiele instancji proxy współdzieli jeden budżet. Przy awarii Redis — fail-open.

Przykład: dwie instancje za LB#

yaml
backends:
  - url: http://api-1:8080
  - url: http://api-2:8080

redis:
  addr: redis:6379

rate_limit:
  requests_per_minute: 300
  burst: 100
  trusted_proxies:
    - 10.0.0.0/8

Każda instancja proxy ma ten sam redis.addr — klient dostaje jeden wspólny limit niezależnie od tego, która instancja go obsłuży.

Graceful shutdown#

yaml
timeouts:
  shutdown: 10s

Po SIGTERM proxy:

  1. Przestaje przyjmować nowe połączenia.
  2. Drainuje requesty w locie do shutdown budżetu.
  3. Zamyka idle connections.
  4. Kończy proces.
bash
docker stop --time 15 reverse-proxy

Connection pooling#

yaml
timeouts:
  backend_max_idle_conns: 256
  backend_max_idle_conns_per_host: 64
  backend_idle_conn: 90s

Upstream connection pooling z keep-alive i HTTP/2 dla TLS upstreamów. Dla bardzo dużej liczby backendów zwiększ backend_max_idle_conns.

Checklist wdrożenia#

  1. Zbuduj obraz: docker build -t reverse-proxy .
  2. Przygotuj config.yaml z backendami i strategią.
  3. Ustaw sekrety: ADMIN_TOKEN, opcjonalnie REDIS_PASSWORD.
  4. Zamontuj certyfikaty TLS read-only (jeśli termination na proxy).
  5. Uruchom z --read-only i non-root.
  6. Sprawdź /healthz i /admin/backends.
  7. Podłącz Prometheus do /metrics.
  8. Przetestuj failover: wyłącz jeden backend i obserwuj metryki.

Reverse proxy za nginx (TLS na brzegu)#

nginxnginx.conf
server {
  listen 443 ssl http2;
  server_name api.example.com;

  ssl_certificate /etc/tls/fullchain.pem;
  ssl_certificate_key /etc/tls/privkey.pem;

  location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Host $host;
  }
}

Wtedy w konfiguracji proxy:

yaml
rate_limit:
  trusted_proxies:
    - 127.0.0.1