El proxy inverso Traefik, características y funcionalidades que ofrece

Escrito por picodotdev el .
gnu-linux planeta-codigo software-libre
Enlace permanente Comentarios

Un proxy inverso oculta la complejidad de los servicios para los que hace de intermediario. Al situarse como intermediario el proxy inverso es capaz de proporcionar funcionalidades adicionales como balanceo de carga, limitar el número de peticiones por unidad de tiempo, duplicar peticiones o realizar la autenticación entre otras funciones. Traefik es un proxy inverso diseñado para los entornos cloud dinámicos soportando autoconfigurarción a partir de varios proveedores de registro y descubrimiento de servicios como Consul o Docker, también se integra con herramientas para la observabilidad como Prometheus para métricas y Zipkin y Elastic para trazabilidad y registro de trazas.

La tendencia de desarrollo es utilizar microservicios, contenedores y computación en la nube. Una característica de este tipo de aplicaciones es que se componen de múltiples elementos muy dinámicos que se crean, eliminan o cambia el número de instancias. La configuración de las herramientas nativas para la nube ha de ser dinámica en vez de estática.

Una herramienta específica para hacer de proxy inverso adaptada a la nube es Traefik.

El proxy inverso Traefik

Traefik es un proxy inverso y balanceador de carga adaptado a la computación en la nube mediante microservicios. Traefik se integra con los principales componentes de infraestructura configurándose a sí mismo automáticamente y de forma dinámica. Traefik es simple de operar pero capaz de manejar sistemas complejos y grandes en entornos diversos y diferentes capas de la pila de red como HTTP, TCP o UDP. Proporciona funcionalidades de intermediario que aumenta sus capacidades para realizar balanceo de carga o servir como gateway API.

Como proxy inverso intercepta las peticiones entrantes y las redirige a los servicios adecuados. A diferencia de proxys inversos configurados de forma estática, Traefik usa descubrimiento de servicios para configurarse a sí mismo según los servicios en ejecución. Puede hacer de proxy en diferentes capas de la pila de red proporcionando funcionalidades como balanceo de carga, limitación de peticiones, circuit breaker, duplicación o mirroring, autenticación y más. También soporta terminación de SSL y puede usar un proveedor ACME como Let’s Encrypt para la generación automática de certificados.

El proxy inverso Traefik y arquitectura

El ecosistema de Traefik permite integrarse con otras herramientas destacadas soportando de forma nativa observabilidad con trazabilidad distribuida y con varios proveedores de métricas.

Traefik posee dos versiones una open source gratuita con varias de las funcionalidades básicas, para los casos de uso empresariales proporciona funcionalidades adicionales como autenticación OAuth2 o integración con Vault, en la tabla que compara ambas versiones se especifica cuales están incluídas en cada una.

Al iniciar Traefik se integra un panel de información o dashboard en el que observar las rutas, servicios y middelwares configurados, dado el entorno dinámico de la computación en la nube y microservicios permite ver el estado del sistema.

El panel de información o dashboard integrado de Traefik

Casos de uso y características

Los principales casos de uso deTraefik son:

  • Balanceador de carga: permite distribuir la carga de peticiones con un enrutamiento flexible en la capa 4 (TCP y UDP) y 7 (HTTP, aplicación) junto con un conjunto amplio de midleware que permite escalado dinámico, despliegues blue-green y canary sin caídas, duplicación de peticiones y más.
  • API gateway: usar Traefik como proxy inverso delante de los servicios permite delegar a Traefik aspectos funcionales transversales incluyendo autenticación, limitación de peticiones y terminación de SSL. Estas capacidades son expandibles mediante complementos o plugins.
  • Kubernetes Ingress: Traefik se puede usar como controlador Kubernetes Ingress para añadir la flexibilidad y facilidad de uso a los despliegues de Kubernetes asi como al resto de su infraestructura de red.
  • Gestión de certificados: Traefik soporta de forma nativa gestión de certificados de proveedores ACME como Let’s Encrypt así como certificados actualizables de forma dinámica definidos por el usuario.

Sus características son:

  • Enrutado y balanceo de carga: capa de enrutado flexible en la capa 4 y 7, soporta los protocolos HTTP, HTTP/2, TCP, UDP, Websockets, gRPC, despliegues blue-green y canary, fijación de sesión o session stickness y comprobaciones de salud.
  • Seguridad: automatización de HTTP, soporte para Let’s Encrypt, certificados personalizados y autenticación.
  • Configuración dinámica: a través de descubrimiento de servicios (Kubernetes, Docker Swarm, Red Hat OpenShift, Rancher, Amazon ECS, key-value stores) y funcionales de intermediario o middelware (circuit breakers, reintentos, buffering, compresión de la respuesta, cabeceras o limitación de peticiones).
  • Observabilidad: posee un panel informativo de forma nativa, trazabilidad distribuida (Jaeger, Open Tracing, Zipkin) y métricas en tiempo real (Datadog, Grafana, InfluxDB, Prometheus, StatsD).

Estas funcionalidades son extensibles mediante plugins.

Conceptos

Como proxy inverso la funcionalidad de Traefik es redirigir las peticiones entrantes a los servicios de backend para los que hace de proxy. Traefik emplea los siguientes conceptos fundamentales para su configuración:

  • Proveedores: permiten descubrir los servicios disponibles proporcionando su ubicación y salud y al mismo tiempo realizar la configuración de Traefik de forma dinámica.
  • Entrypoints: son los puertos por los que entra el tráfico de red y el protocolo usado HTTP, TCP o UDP.
  • Enrutadores: analizan las peticiones entrantes en base a unas reglas que utilizan el nombre de dominio, ruta o cabecera, definen que las funcionalidades de middelware que se aplican y a que servicios se redirigen.
  • Servicios: son los destinos de las peticiones entrantes pudiendo aplicar funcionalidad de balanceo de carga entre las diferentes instancias de los servicios de backend para los que se hace de proxy.
  • Middlewares: pueden actualizar la petición como añadir cabeceras HTTP, actualizar la ruta o realizar acciones de intermediario como autenticación o limitación de peticiones.

Ejemplos de funcionalidades

En este ejemplo se utiliza Traefik como proxy inverso para dos instancias de servidor web Nginx. La configuración de los entry points de Traefik es estática que en el ejemplo se define un un archivo de configuración estático.

La configuración de los enrutadores, servicios y middlewares es dinámica pudiendo actualizarse sin necesidad de reiniciar Traefik. La configuración dinámica la obtiene de los proveedores como Consul o Docker donde los servicios al iniciarse incluyen metainformación que Traefik obtiene se autoconfigura como se muestra en el artículo Microservicios con Spring Cloud, Consul, Nomad y Traefik. En el caso de este ejemplo por sencillez la configuración dinámica se proporciona a Traefik en un archivo de texto, aún siendo manual escribir esta configuración Traefik la puede recargar y aplicar sin necesidad de reiniciar el servidor.

La configuración de Traefik del ejemplo se divide en tres secciones que son: enrutadores, middlewares y servicios. En la configuración se definen varios enrutadores que aplican reglas en función del host de la petición, los enrutadores definen que middlewares se aplican y a que servicios se redirigen las peticiones coincidentes.

Esta es la configuración estática de Traefik.

1
2
3
4
5
6
7
providers:
 file:
  watch: true
  filename: /etc/traefik/traefik-dynamic.yml
api:
  insecure: true
  dashboard: true
traefik.yml

El archivo de Docker Compose que inicia Traefik y los servidores de Nginx de backend.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: '3.7'

services:
  traefik:
    image: traefik:latest
    ports:
      - "8090:80"
      - "8080:8080"
    volumes:
      - ./:/etc/traefik/
  nginx-1:
    image: nginx:latest
    volumes:
      - ./nginx-1:/usr/share/nginx/html:ro
    ports:
      - "8081:80"
  nginx-2:
    image: nginx:latest
    volumes:
      - ./nginx-2:/usr/share/nginx/html:ro
    ports:
      - "8082:80"
docker-compose.yml

Y los archivos HTML de los servidores web.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx (nginx-1)!</title>
<link rel="icon" href="data:,">
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx! (nginx-1)</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer > to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for>  using nginx.</em></p>
</body>
</html>
index-1.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx (nginx-2)!</title>
<link rel="icon" href="data:,">
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx! (nginx-2)</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer > to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for>  using nginx.</em></p>
</body>
</html>
index-2.html

Balanceador de carga

Un balanceador de carga permite distribuir la carga entre las diferentes instancias del servicio de backend. Por defecto, se aplican una estrategia round-robin para distribuir la carga de forma equitativa entre las diferentes instancias.

Con esta configuración al realizar peticiones se distribuyen entre la instancia nginx-1 y nginx-2 de forma alternativa. La primera petición a la instancia nginx-1, la segunda a la instancia nginx-2 y la tercera de nuevo a la instancia nginx-1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
http:
  routers:
    ...
    nginx:
      rule: "Host(`nginx.127.0.0.1.sslip.io`)"
      middlewares:
        - nginx-ratelimit
      service: "nginx"
    ...
  middlewares:
    ...
  services:
    ...
    nginx:
      loadBalancer:
        servers:
          - url: http://nginx-1:80/
          - url: http://nginx-2:80/
        passHostHeader: true
    ...
traefik-dynamic-load-balancer.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ curl http://nginx.127.0.0.1.sslip.io:8080/
<!DOCTYPE html>
...
<title>Welcome to nginx (nginx-1)!</title>
...
$ curl http://nginx.127.0.0.1.sslip.io:8080/
<!DOCTYPE html>
...
<title>Welcome to nginx (nginx-2)!</title>
...
$ curl http://nginx.127.0.0.1.sslip.io:8080/
<!DOCTYPE html>
...
<title>Welcome to nginx (nginx-1)!</title>
...
curl-load-balancer.sh

Balanceador de carga con peso

A veces interesa no distribuir la carga de forma uniforme entre las diferentes instancias de los servicios de backend, sino realizar un balanceo de carga con peso.

En este caso el balanceo de carga se configura para que la instancia de nginx-1 tenga un peso de 3 y la instancia de nginx-2 tenga un peso de 1, según estos pesos la instancia nginx-1 de cada 4 peticiones recibe 3 mientras que la instancia nginx-2 de cada 4 peticiones recibe 1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
http:
  routers:
    ...
    nginx-weighted:
      rule: "Host(`nginx-weighted.127.0.0.1.sslip.io`)"
      middlewares:
        - nginx-ratelimit
      service: "nginx-weighted"
    ...
  middlewares:
    ...
  services:
    ...
    nginx-weighted:
      weighted:
        services:
          - name: nginx-1
            weight: 3
          - name: nginx-2
            weight: 1
    ...
traefik-dynamic-load-balancer-weighted.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ curl http://nginx-weighted.127.0.0.1.sslip.io:8080/
<!DOCTYPE html>
...
<title>Welcome to nginx (nginx-1)!</title>
...
$ curl http://nginx-weighted.127.0.0.1.sslip.io:8080/
<!DOCTYPE html>
...
<title>Welcome to nginx (nginx-1)!</title>
...
$ curl http://nginx-weighted.127.0.0.1.sslip.io:8080/
<!DOCTYPE html>
...
<title>Welcome to nginx (nginx-1)!</title>
...
$ curl http://nginx-weighted.127.0.0.1.sslip.io:8080/
<!DOCTYPE html>
...
<title>Welcome to nginx (nginx-2)!</title>
...
$ curl http://nginx-weighted.127.0.0.1.sslip.io:8080/
<!DOCTYPE html>
...
<title>Welcome to nginx (nginx-1)!</title>
...
curl-load-balancer-weighted.sh

Limitar número de peticiones

Para evitar que un servicio reciba más peticiones de las que es capaz de procesar según su nivel de servicio, con el objetivo de evitar que se sature y falle o evitar denegación de servicio por una carga excesiva es posible establecer un límite en el número de peticiones que se envían a los servicios de backend. El límite se puede establecer de forma global para todas las peticiones o agruparse según la dirección IP origen o el valor de una cabecera.

En esta configuración se establece que el servicio de backend no reciba más de una petición por segundo, en caso de que se realicen más peticiones por unidad de tiempo que este límite Traefik devuelve un error 429 Too Many Requests.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
http:
  routers:
    ...
    nginx:
      rule: "Host(`nginx.127.0.0.1.sslip.io`)"
      middlewares:
        - nginx-ratelimit
      service: "nginx"
    ...
  middlewares:
    nginx-ratelimit:
      rateLimit:
        average: 1
  services:
    ...
traefik-dynamic-rate-limit.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ curl -v http://nginx.127.0.0.1.sslip.io:8080/
*   Trying 127.0.0.1:8090...
* Connected to nginx.127.0.0.1.sslip.io (127.0.0.1) port 8090 (#0)
> GET / HTTP/1.1
> Host: nginx.127.0.0.1.sslip.io:8090
> User-Agent: curl/7.78.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 429 Too Many Requests
< Retry-After: 1
< X-Retry-In: 565.087221ms
< Date: Sun, 19 Sep 2021 12:32:41 GMT
< Content-Length: 17
< Content-Type: text/plain; charset=utf-8
< 
* Connection #0 to host nginx.127.0.0.1.sslip.io left intact
curl-rate-limit.sh

Duplicar peticiones o mirroring

Este middleware permite duplicar la petición que se envía a un servicio a otros servicios descartando el resultado devuelto por esos otros servicios.

Esto puede ser útil en el caso de aplicar el patrón de estrangulación ya que permite probar las peticiones sin riesgos. El servicio heredado continúa recibiendo y procesando las peticiones a la vez que el nuevo servicio recibe las mismas peticiones, una vez que el nuevo servicio se comporta de la forma esperada las peticiones del servicio heredado se redirigen al nuevo servicio completando la estrangulación.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
http:
  routers:
    ...
    nginx-mirroring:
      rule: "Host(`nginx-mirroring.127.0.0.1.sslip.io`)"
      service:
        - nginx-mirroring
  middlewares:
    ...
  services:
    ...
    nginx-mirroring:
      mirroring:
        service: nginx-1
        mirrors:
          - name: nginx-2
            percent: 50
traefik-dynamic-mirroring.yml

Al hacer la petición el resultado que se devuelve es el nginx-1.

1
2
3
4
5
$ curl http://nginx-mirroring.127.0.0.1.sslip.io:8080/
<!DOCTYPE html>
...
<title>Welcome to nginx (nginx-1)!</title>
...
curl-mirroring.sh

Traefik al mismo tiempo que realiza la petición a nginx-1 se realiza a nginx-2 como se observa en las trazas de los servidores Nginx.

1
2
nginx-1_1  | 172.20.0.2 - - [19/Sep/2021:12:34:39 +0000] "GET / HTTP/1.1" 200 136 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0" "172.20.0.1"
nginx-2_1  | 172.20.0.2 - - [19/Sep/2021:12:34:39 +0000] "GET / HTTP/1.1" 200 136 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0" "172.20.0.1"
curl-mirroring-nginx.out

Otras funcionalidades

Hay otros middlewares que se pueden aplicar como el de retry para reintentar las peticiones en caso de fallo, aplicar el patrón circuit breaker para aplicar resiliencia evitando enviar peticiones a un servicio que está fallando, autenticación, reescribir el path de las peticiones o quitar un prefijo del path y añadir o eliminar cabeceras.

Al aplicar el patrón de estrangulación para reemplazar aplicaciones heredadas la solución es añadir un intermediario como Traefik que permita redirigir las peticiones a la aplicación heredada o a su reemplazo cuando esté esté implementado. Este intermediario permite ir reemplazando de forma incremental la aplicación heredada.

Traefik también permite delegar la autenticación en Keycloak u otros servicios con los middlewares Forward Auth, Digest Auth y Basic Auth.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
docker-compose up


Comparte el artículo: