Compartir comentarios
Las respuestas se generan en base a la documentación.

Enrutamiento HTTP con Traefik

Introducción

Durante el desarrollo local, es bastante común necesitar ejecutar múltiples servicios HTTP. Es posible que tengas tanto una API como una aplicación frontend, un servicio de WireMock para simular puntos finales de datos (endpoints) o un visualizador de base de datos (como phpMyAdmin o pgAdmin). En muchas configuraciones de desarrollo, estos servicios se exponen en diferentes puertos, lo que requiere que recuerdes qué puerto corresponde a cada servicio, pero también puede introducir otros problemas (como CORS).

Un proxy inverso puede simplificar enormemente esta configuración al ser el único servicio expuesto y enrutar las solicitudes al servicio adecuado según la URL de la solicitud (ya sea por ruta o por nombre de host). Traefik es un proxy inverso y balanceador de carga moderno y nativo de la nube que hace que el desarrollo y el despliegue de aplicaciones multiservicio sean más sencillos. Esta guía te mostrará cómo usar Traefik con Docker para mejorar tu entorno de desarrollo.

En esta guía, aprenderás a:

  1. Iniciar Traefik con Docker
  2. Configurar reglas de enrutamiento para dividir el tráfico entre dos contenedores
  3. Usar Traefik en un entorno de desarrollo contenedorizado
  4. Usar Traefik para enviar solicitudes a cargas de trabajo no contenedorizadas

Requisitos previos

Se requieren los siguientes requisitos previos para seguir esta guía práctica:

Uso de Traefik con Docker

Una de las características únicas de Traefik es su capacidad para configurarse de muchas formas. Al utilizar el proveedor de Docker, Traefik obtiene su configuración de otros contenedores en ejecución mediante etiquetas (labels). Traefik monitoreará los eventos del motor (para inicios y paradas de contenedores), extraerá las etiquetas y actualizará su configuración.

Aunque existen muchas etiquetas monitoreadas por Traefik, las dos más comunes serán:

Hagamos una demostración rápida de cómo iniciar Traefik y luego configurar dos contenedores adicionales para que sean accesibles utilizando diferentes nombres de host.

  1. Para que dos contenedores puedan comunicarse entre sí, deben estar en la misma red. Crea una red llamada traefik-demo utilizando el comando docker network create:

    $ docker network create traefik-demo
    
  2. Inicia un contenedor Traefik utilizando uno de los siguientes métodos. Estos comandos exponen Traefik en el puerto 80, montan el socket de Docker (que se utiliza para monitorear los contenedores para actualizar la configuración) y pasan el argumento --providers.docker para configurar Traefik para que use el proveedor de Docker.

    Las imágenes protegidas de Docker (Docker Hardened Images - DHI) para Traefik están disponibles en Docker Hub. Si aún no te has autenticado, primero ejecuta:

    $ docker login dhi.io

    Luego inicia un contenedor utilizando la imagen Hardened:

    $ docker run -d --network=traefik-demo \
      -p 80:80 \
      -v /var/run/docker.sock:/var/run/docker.sock \
      dhi.io/traefik:3.6.2 \
      --providers.docker
    

    También puedes usar la imagen oficial de Docker Hub:

    $ docker run -d --network=traefik-demo \
      -p 80:80 \
      -v /var/run/docker.sock:/var/run/docker.sock \
      traefik:v3.6.2 \
      --providers.docker
    
  3. Ahora, inicia un contenedor Nginx simple y define las etiquetas que Traefik está monitoreando para configurar el enrutamiento HTTP. Ten en cuenta que el contenedor Nginx no expone ningún puerto.

    Las imágenes protegidas de Docker (DHI) para Nginx están disponibles en Nginx DHI image. Si aún no te has autenticado, primero ejecuta:

    $ docker login dhi.io
    $ docker run -d --network=traefik-demo \
      --label 'traefik.http.routers.nginx.rule=Host(`nginx.localhost`)' \
      dhi.io/nginx:1.29.3
    

    También puedes ejecutar la imagen oficial de Nginx de la siguiente manera:

    $ docker run -d --network=traefik-demo \
      --label 'traefik.http.routers.nginx.rule=Host(`nginx.localhost`)' \
      nginx:1.29.3
    

    Una vez que el contenedor se inicie, abre tu navegador en http://nginx.localhost para ver la aplicación (todos los navegadores basados en Chromium enrutan las solicitudes *.localhost localmente sin necesidad de configuración adicional).

  4. Inicia una segunda aplicación que utilizará un nombre de host diferente.

    $ docker run -d --network=traefik-demo --label 'traefik.http.routers.welcome.rule=Host(`welcome.localhost`)' docker/welcome-to-docker
    

    Una vez que el contenedor se inicie, abre tu navegador en http://welcome.localhost. Deberías ver un sitio web "Welcome to Docker".

Uso de Traefik en desarrollo

Ahora que has probado Traefik, es hora de intentar usarlo en un entorno de desarrollo. En este ejemplo, utilizarás una aplicación de ejemplo que tiene un frontend y un backend divididos. Esta pila de aplicaciones tiene la siguiente configuración:

  1. Todas las solicitudes a /api van al servicio API.
  2. Todas las demás solicitudes a localhost van al cliente frontend.
  3. Dado que la aplicación utiliza MySQL, db.localhost debería proporcionar phpMyAdmin para facilitar el acceso a la base de datos durante el desarrollo.
Diagrama de arquitectura que muestra a Traefik enrutando solicitudes a otros contenedores según la ruta de la solicitud

Se puede acceder a la aplicación en GitHub en dockersamples/easy-http-routing-with-traefik.

  1. En el archivo compose.yaml, Traefik está utilizando la siguiente configuración:

    services:
      proxy:
        image: dhi.io/traefik:3.6.2
        command: --providers.docker
        ports:
          - 80:80
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
    services:
      proxy:
        image: traefik:v3.6.2
        command: --providers.docker
        ports:
          - 80:80
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock

    Ten en cuenta que esta es esencialmente la misma configuración que se usó anteriormente, pero ahora con la sintaxis de Compose.

  2. El servicio client tiene la siguiente configuración, que iniciará el contenedor y le proporcionará las etiquetas para recibir solicitudes en localhost.

    Las imágenes protegidas de Docker (DHI) para Nginx están disponibles en Nginx DHI image.

    Si aún no te has autenticado, primero ejecuta:

    $ docker login dhi.io

    Puedes usarlo como tu imagen base como se muestra a continuación:

    services:
      # …
      client:
        image: dhi.io/nginx:1.29.3-alpine3.21
        volumes:
          - "./client:/usr/share/nginx/html"
        labels:
          traefik.http.routers.client.rule: "Host(`localhost`)"
    services:
      # …
      client:
        image: nginx:1.29.3-alpine3.22
        volumes:
          - "./client:/usr/share/nginx/html"
        labels:
          traefik.http.routers.client.rule: "Host(`localhost`)"
  3. El servicio api tiene una configuración similar, pero notarás que la regla de enrutamiento tiene dos condiciones: el host debe ser "localhost" y la ruta de la URL debe tener el prefijo "/api". Dado que esta regla es más específica, Traefik la evaluará primero en comparación con la regla del cliente.

    services:
      # …
      api:
        build: ./dev/api
        volumes:
          - "./api:/var/www/html/api"
        labels:
          traefik.http.routers.api.rule: "Host(`localhost`) && PathPrefix(`/api`)"
  4. Y finalmente, el servicio phpmyadmin está configurado para recibir solicitudes para el nombre de host "db.localhost". El servicio también tiene variables de entorno definidas para iniciar sesión automáticamente, haciendo que sea un poco más sencillo ingresar a la aplicación.

    services:
      # …
      phpmyadmin:
        image: phpmyadmin:5.2.1
        labels:
          traefik.http.routers.db.rule: "Host(`db.localhost`)"
        environment:
          PMA_USER: root
          PMA_PASSWORD: password
  5. Antes de iniciar la pila, detén el contenedor Nginx si aún se está ejecutando.

    Y eso es todo. Ahora, solo necesitas poner en marcha la pila de Compose con docker compose up y todos los servicios y aplicaciones estarán listos para el desarrollo.

Enviar tráfico a cargas de trabajo no contenedorizadas

En algunas situaciones, es posible que desees reenviar solicitudes a aplicaciones que no se ejecutan en contenedores. En el siguiente diagrama de arquitectura, se utiliza la misma aplicación de antes, pero la API y las aplicaciones de React ahora se ejecutan de forma nativa en la máquina host.

Un diagrama de arquitectura que muestra varios componentes y el enrutamiento entre ellos. Traefik puede enviar solicitudes tanto a cargas de trabajo contenedorizadas como no contenedorizadas

Para lograr esto, Traefik necesitará usar otro método para configurarse. Con el proveedor File puedes definir las reglas de enrutamiento en un documento YAML. Aquí tienes un archivo de ejemplo:

http:
  routers:
    native-api:
      rule: "Host(`localhost`) && PathPrefix(`/api`)"
      service: native-api
    native-client:
      rule: "Host(`localhost`)"
      service: native-client

  services:
    native-api:
      loadBalancer:
        servers:
          - url: "http://host.docker.internal:3000/"
    native-client:
      loadBalancer:
        servers:
          - url: "http://host.docker.internal:5173/"

Esta configuración indica que las solicitudes para localhost/api se reenviarán a un servicio llamado native-api, que luego reenvía la solicitud a http://host.docker.internal:3000. El nombre de host host.docker.internal es un nombre que proporciona Docker Desktop para enviar solicitudes a la máquina host.

Con este archivo, el único cambio es en la configuración de Compose para Traefik. Específicamente, han cambiado dos cosas:

  1. El archivo de configuración se monta dentro del contenedor Traefik (la ruta de destino exacta depende de ti).
  2. Se actualiza el comando (command) para añadir el proveedor file y apuntar a la ubicación del archivo de configuración.
services:
  proxy:
    image: dhi.io/traefik:3.6.2
    command: --providers.docker --providers.file.filename=/config/traefik-config.yaml --api.insecure
    ports:
      - 80:80
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dev/traefik-config.yaml:/config/traefik-config.yaml
services:
  proxy:
    image: traefik:v3.6.2
    command: --providers.docker --providers.file.filename=/config/traefik-config.yaml --api.insecure
    ports:
      - 80:80
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dev/traefik-config.yaml:/config/traefik-config.yaml

Iniciar la aplicación de ejemplo

Para ejecutar la aplicación de ejemplo que reenvía solicitudes de Traefik a aplicaciones que se ejecutan de forma nativa, realiza los siguientes pasos:

  1. Si aún tienes la pila de Compose en ejecución, deténla con el siguiente comando:

    $ docker compose down
    
  2. Inicia la aplicación utilizando el archivo compose-native.yaml proporcionado:

    $ docker compose -f compose-native.yaml up
    

    Al abrir http://localhost se devolverá un error "502 Bad Gateway" porque las otras aplicaciones aún no se están ejecutando.

  3. Inicia la API ejecutando los siguientes pasos:

    cd api
    yarn install
    yarn dev
    
  4. Inicia el frontend ejecutando los siguientes pasos en una nueva terminal (comenzando desde la raíz del proyecto):

    cd client
    yarn install
    yarn dev
    
  5. Abre la aplicación en http://localhost. Deberías ver una aplicación que recupera un mensaje de http://localhost/api/messages. También puedes abrir http://db.localhost para ver o ajustar los mensajes disponibles directamente desde la base de datos Mongo. Traefik se asegurará de que las solicitudes se enruten correctamente al contenedor o aplicación correctos.

  6. Cuando hayas terminado, ejecuta docker compose down para detener los contenedores y detén las aplicaciones de Yarn presionando ctrl+c.

Resumen

Ejecutar múltiples servicios no tiene por qué requerir una configuración complicada de puertos y una buena memoria. Con herramientas como Traefik, es sencillo lanzar los servicios que necesitas y acceder a ellos sin complicaciones, ya sea para la aplicación en sí (como el frontend y el backend) o para herramientas de desarrollo adicionales (como phpMyAdmin).