Todos los artículos

Traefik, un entorno de desarrollo potenciado

Más flexibilidad en nuestro entorno de desarrollo con Traefik.

Tu proyecto actual está configurado con docker-compose y todo funciona perfectamente. De repente necesitas una corrección urgente en un proyecto que terminaste hace seis meses. Aquel también estaba bien configurado con docker-compose, pero ahora no funciona. ¿Qué pasó? Ambos proyectos tienen servicios que usan los mismos puertos. ¿Te suena? Es muy común si trabajas en varios proyectos. Necesitas configurar puertos diferentes para mantenerlos todos en ejecución, o detener uno antes de iniciar el otro. Me resulta bastante molesto. Buscando una solución encontré Traefik.

Traefik es un proxy inverso y balanceador de carga moderno que se configura automática y dinámicamente. Tiene muchas funcionalidades interesantes:

  • Balanceo de carga.
  • Puerta de enlace para API.
  • Gestión de certificados.
  • Y más.

En este artículo nos centraremos en su capacidad de usar el descubrimiento de servicios para configurarse dinámicamente a partir de los propios servicios. Esto permite ejecutar varios servicios aunque escuchen en el mismo puerto. Al configurarlos detrás de Traefik, no necesitamos especificar en qué puerto escuchan: ¡Traefik los configura dinámicamente!

Sin embargo, las imágenes de los servicios deben exponer los puertos para que Traefik pueda comunicarse con ellos. Consulta la sección de resolución de problemas.

Instalación

Podemos seguir el ejemplo básico de Traefik con Docker para ejecutarlo en nuestro entorno de desarrollo.

Ejemplo básico

version: "3.3"

services:

  traefik:
    image: "traefik"
    container_name: "traefik"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    networks: 
      - traefik

  whoami:
    image: "containous/whoami"
    container_name: "simple-service"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
      - "traefik.http.routers.whoami.entrypoints=web"
      - "traefik.docker.network=traefik"
    networks: 
      - traefik

networks: 
  traefik:
    external: true

Descubrimiento de servicios en acción

En el ejemplo básico, el servicio whoami usa etiquetas para configurar su comunicación con Traefik:

whoami:
		...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
      - "traefik.http.routers.whoami.entrypoints=web"
      - "traefik.docker.network=traefik"
		...

Después de ejecutar docker-compose up -d, podrás acceder a whoami.localhost en tu navegador.

En mi ejemplo básico personalizado añadí la red externa traefik para permitir que servicios definidos en otros archivos docker-compose se comuniquen con Traefik. Podemos definir todos los servicios en un único archivo, pero prefiero dedicar uno a los servicios de cada proyecto.

Llamé traefik a la red, pero puede tener cualquier nombre.

Este es un ejemplo del archivo docker-compose que uso en mi proyecto Habits-Inventivery:

version: "3.8"

services:
  php:
    image: webcu/laravel-installer
    restart: always
    working_dir: /laravel
    volumes:
        - .:/laravel
        - ./docker/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.habits.rule=Host(`habits.localhost`)"
      - "traefik.http.routers.habits.entrypoints=web"
      - "traefik.docker.network=traefik"
    networks:
      - traefik
      - mysql
    entrypoint: ["php", "-S", "0.0.0.0:80", "-t", "public", "server.php"]

networks:
  traefik:
    external: true
  mysql:
    external: true

Una vez iniciado Traefik, preparar el entorno de un proyecto nuevo es muy rápido. Creamos un archivo docker-compose en el proyecto, definimos los servicios necesarios y conectamos los que lo requieran a Traefik mediante la red externa. Entonces podemos centrarnos en entregar el producto.

Resolución de problemas

  • Traefik no puede conectarse a servicios creados en otro archivo docker-compose.yml.

    • ¿Utilizan la misma red externa que Traefik?

      Para comunicarse con servicios creados en otro archivo docker-compose.yml, Traefik debe compartir su red.

    • ¿Tiene el contenedor puertos abiertos?

      Si usas una imagen estándar, es poco probable que te ocurra, pero si experimentas con una propia, como yo, puede pasar 🙂.

      Comprobé que los servicios compartían la misma red externa, pero seguía sin ver los otros contenedores en el panel de Traefik. El problema era que la imagen del contenedor necesitaba exponer un puerto para que otros servicios pudieran comunicarse con él.

      ¿Cómo lo descubrí? Tenía una imagen containous/whoami que funcionaba correctamente. Primero la dupliqué en el mismo docker-compose.yml que Traefik. Todo funcionó. Moví el nuevo servicio al archivo de los otros servicios y volvió a funcionar. Esto mostró que el problema no era la red compartida. Cambié la imagen del nuevo servicio por una de Node y Traefik dejó de verlo. Con una imagen de PHP ocurrió lo mismo. Revisé el código fuente de containous/whoami para ver qué tenía de diferente. Vi que exponía el puerto 80, que había configurado como punto de entrada web de Traefik; esto es configurable.

      traefik:
          image: "traefik"
          container_name: "traefik"
          command:
            - "--api.insecure=true"
            - "--providers.docker=true"
            - "--providers.docker.exposedbydefault=false"
            - "--entrypoints.web.address=:80"

      Modifiqué el Dockerfile de mi imagen PHP para exponer el puerto 80, pero seguía sin funcionar. ¿El problema? Olvidé reconstruir la imagen al recrear el contenedor y seguía usando la que no tenía puertos abiertos. Reconstruí la imagen, recreé el contenedor y los servicios aparecieron en el panel de Traefik.

      La forma más rápida de resolverlo es comparar los contenedores en ejecución. Con docker-compose ps vi que whoami exponía el puerto 80, pero los otros servicios que quería conectar a Traefik no.

  • No puedes acceder desde Firefox a sitios locales terminados en .localhost.

    Este problema me volvía loco. Pensaba que algo fallaba en los servidores web, pero comprobé que Chrome accedía sin problemas. Al mirar las solicitudes de Mozilla deduje inicialmente que la causa era la política de referente.

    Investigando más, descubrí que Mozilla no resolvía los dominios *.localhost a 127.0.0.1. Este es el seguimiento del error:

    1220810: considerar resolver los nombres localhost a la dirección de bucle local.

    La solución más sencilla es añadir manualmente a /etc/hosts entradas para las URL que queremos gestionar localmente, pero resulta repetitivo y tedioso.

    ¿Podemos hacerlo mejor?

    Podríamos crear un script que asigne una URL a localhost. Suena mejor, pero sigue requiriendo intervención manual.

    Configuración automática de proxy (PAC).

    Es una solución mucho más general que no requiere intervención manual después de la configuración inicial. La idea surgió de este problema: El DNS con Traefik no funciona · Incidencia n.º 3 · jtreminio/dashtainer.

    Revisé typicode/hotel, copié la función, sustituí los valores de conf por valores fijos y creé /etc/proxy.pac.

    Después abrí la configuración de proxy de Mozilla y añadí el archivo:

    ¡Y listo, problema resuelto!

Conclusiones

Traefik permite crear entornos de desarrollo dinámicos gracias a su capacidad de configurarse automática y dinámicamente. Es una herramienta potente, con funcionalidades que van mucho más allá del entorno local.

Referencias