Docker: Enrutamiento HTTPS

Cómo gestionar el enrutamiento HTTP y el cifrado TLS (HTTPS) en tus contenedores Docker

👋 ¡Bienvenido a la documentación de Stackhero!

Stackhero ofrece una solución Docker cloud CaaS (Containers as a Service) lista para usar que proporciona una serie de beneficios, incluyendo:

  • Despliega fácilmente tus contenedores en producción con solo un docker-compose up.
  • Nombre de dominio personalizable asegurado con HTTPS (por ejemplo, https://api.tu-empresa.com, https://www.tu-empresa.com, https://backoffice.tu-empresa.com).
  • Rendimiento óptimo y seguridad robusta gracias a una VM privada y dedicada.
  • Actualizaciones sin esfuerzo con solo un clic.

Ahorra tiempo y simplifica tu vida: ¡solo toma 5 minutos probar la solución de alojamiento en la nube Docker CaaS de Stackhero y desplegar tus contenedores en producción!

Cada instancia de Stackhero para Docker incluye un servidor Traefik dedicado y preconfigurado que gestiona el tráfico HTTP y lo cifra automáticamente utilizando certificados TLS. Esta configuración simplificada hace que el enrutamiento sea sencillo y eficiente. Por ejemplo:

  • Envía el tráfico de https://www.my-company.com (con o sin el prefijo www) a tu contenedor frontend.
  • Ruta https://www.my-company.com/documentations a tu contenedor documentations.
  • Dirige https://api.my-project.io a tu contenedor api.

Puedes gestionar un número ilimitado de dominios con creación y renovación automática de certificados TLS. Lo mejor de todo es que puedes configurar todo esto con solo tres líneas.

En todos los ejemplos de configuración siguientes, tendrás que reemplazar <XXXXXX>.stackhero-network.com con el nombre de dominio de tu instancia de Stackhero para Docker.

A continuación, se muestra un ejemplo básico de un archivo docker-compose.yml:

services:
  test:
    image: nginx
    labels:
      - "traefik.enable=true" # Habilitar Traefik para enrutar el tráfico a este contenedor
      - "traefik.http.routers.test.rule=Host(`<XXXXXX>.stackhero-network.com`)" # Definir el host
      - "traefik.http.routers.test.tls.certresolver=letsencrypt" # Usar 'letsencrypt' como el resolutor de certificados TLS

En este ejemplo, un contenedor llamado test se ejecuta utilizando la imagen de Nginx. La configuración clave se proporciona en la sección de etiquetas. Simplemente necesitas copiar estas líneas en tu archivo docker-compose.yml y reemplazar <XXXXXX>.stackhero-network.com con tu dominio de servicio.

Puedes desplegar el contenedor usando:

docker context use <XXXXXX>.stackhero-network.com
docker-compose up

Después de que el contenedor se inicie, visita https://<XXXXXX>.stackhero-network.com/ para ver la página "Welcome to nginx!".

Página de bienvenida de NginxPágina de bienvenida de Nginx

Si no ves la página de bienvenida de Nginx, verifica el panel de control de Traefik para posibles errores.

Con esta configuración, las solicitudes HTTP enviadas a <XXXXXX>.stackhero-network.com se enrutan a tu contenedor test y Traefik crea y gestiona automáticamente los certificados TLS para HTTPS.

En el ejemplo anterior, utilizamos el dominio predeterminado <XXXXXX>.stackhero-network.com. En la práctica, probablemente usarás tus propios dominios de empresa o proyecto, como www.my-company.com o api.my-project.io. La sección a continuación explica cómo configurar dominios personalizados.

En este ejemplo, configurarás el dominio api.my-project.io. Reemplaza my-project.io con un dominio que poseas y api con tu subdominio deseado.

Primero, actualiza la configuración DNS de tu dominio para que api.my-project.io apunte a tu dominio <XXXXXX>.stackhero-network.com.

  1. Inicia sesión en tu proveedor de dominio y accede a tu configuración DNS.
  2. Crea una nueva entrada llamada api (u otro subdominio de tu elección), establece su tipo en CNAME y establece su destino en <XXXXXX>.stackhero-network.com.

Ejemplo de configuración DNS en la interfaz DNS de CloudflareEjemplo de configuración DNS en la interfaz DNS de Cloudflare

Una vez configurado el DNS, puedes validarlo ejecutando:

host api.my-project.io

Deberías ver una respuesta similar a:

api.my-project.io is an alias for <XXXXXX>.stackhero-network.com

La propagación DNS puede tardar hasta 24 horas dependiendo de tu proveedor. Si el comando host no devuelve la respuesta esperada, espera y vuelve a intentarlo más tarde.

A continuación, actualiza tu archivo docker-compose.yml con la siguiente configuración:

services:
  api:
    image: traefik/whoami
    hostname: api
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.my-project.io`)" # Añade tu dominio aquí
      - "traefik.http.services.api.loadbalancer.server.port=<PORT>" # Reemplaza "<PORT>" con el puerto en el que tu API está escuchando
      - "traefik.http.routers.api.tls.certresolver=letsencrypt"

No olvides reemplazar api.my-project.io con tu dominio real en la etiqueta Traefik Host.

Despliega tu contenedor con:

docker-compose up -d

Luego visita https://api.my-project.io. Deberías ver una página de texto que muestra el nombre de host de tu contenedor api.

Prueba de que Traefik está gestionando nuestro tráfico HTTP con cifrado TLS para nuestro nuevo dominioPrueba de que Traefik está gestionando nuestro tráfico HTTP con cifrado TLS para nuestro nuevo dominio

En la primera creación del contenedor, los certificados TLS pueden tardar unos segundos en generarse. Si encuentras un error TLS, espera unos segundos y actualiza la página para permitir que se generen los certificados.

¡Felicidades, ahora has configurado tu primer dominio personalizado!

Al definir una URL de sitio web como my-company.com, es una buena idea configurar también un subdominio "www". Esto asegura que los usuarios que se conecten a través de www.my-company.com sean redirigidos a tu sitio principal y ayuda a evitar problemas de contenido duplicado.

En el ejemplo a continuación, se gestionan tanto my-company.com como www.my-company.com. Los usuarios que acceden a www.my-company.com son redirigidos a my-company.com:

services:
  frontend:
    image: traefik/whoami
    hostname: frontend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`my-company.com`) || Host(`www.my-company.com`)" # Añade ambos dominios aquí
      - "traefik.http.services.frontend.loadbalancer.server.port=<PORT>" # Reemplaza "<PORT>" con el puerto en el que tu frontend está escuchando
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"

      # Redirigir 'www.my-company.com' a 'my-company.com':
      - 'traefik.http.routers.frontend.middlewares=redirect-www'
      - "traefik.http.middlewares.redirect-www.redirectregex.regex=^https://www.my-company.com/(.*)"
      - "traefik.http.middlewares.redirect-www.redirectregex.replacement=https://my-company.com/$${1}"
      - "traefik.http.middlewares.redirect-www.redirectregex.permanent=true"

Supongamos que tienes un contenedor dedicado a tu sitio de documentación. Podrías querer enrutar https://my-company.com/docs a este contenedor mientras envías otras solicitudes, como https://my-company.com/, a tu contenedor frontend. El ejemplo a continuación muestra cómo se logra esto:

services:
  documentations:
    image: traefik/whoami
    hostname: documentations
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.documentations.rule=Host(`my-company.com`) && PathPrefix(`/docs`)" # Define el prefijo de camino aquí
      - "traefik.http.services.documentations.loadbalancer.server.port=<PORT>" # Reemplaza "<PORT>" con el puerto en el que tu contenedor de documentación está escuchando
      - "traefik.http.routers.documentations.tls.certresolver=letsencrypt"

  frontend:
    image: traefik/whoami
    hostname: frontend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`my-company.com`) || Host(`www.my-company.com`)"
      - "traefik.http.services.frontend.loadbalancer.server.port=<PORT>" # Reemplaza "<PORT>" con el puerto en el que tu frontend está escuchando
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"

      # Redirigir 'www.my-company.com' a 'my-company.com':
      - 'traefik.http.routers.frontend.middlewares=redirect-www'
      - "traefik.http.middlewares.redirect-www.redirectregex.regex=^https://www.my-company.com/(.*)"
      - "traefik.http.middlewares.redirect-www.redirectregex.replacement=https://my-company.com/$${1}"
      - "traefik.http.middlewares.redirect-www.redirectregex.permanent=true"

Ahora, al visitar https://my-company.com/docs (o cualquier subruta como https://my-company.com/docs/something) se mostrará contenido del contenedor documentations. Otros caminos, por ejemplo https://my-company.com/help, serán servidos por el contenedor frontend.

Por defecto, Traefik se conecta al primer puerto expuesto del contenedor. En algunos casos, podrías necesitar especificar un puerto particular. El ejemplo a continuación demuestra cómo definir un puerto personalizado:

services:
  frontend:
    image: traefik/whoami
    hostname: frontend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`my-company.com`)"
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"

      # Enrutar tráfico de 'https://my-company.com' al contenedor 'frontend' en el puerto 80
      - "traefik.http.services.frontend.loadbalancer.server.port=80"

Al definir un contenedor en tu archivo docker-compose.yml, algunos campos son importantes para una mejor consistencia y facilidad de gestión. Considera usar la siguiente configuración recomendada:

services:
  <CONTAINER_NAME>:
    image: traefik/whoami
    hostname: <CONTAINER_NAME>
    container_name: <CONTAINER_NAME>
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.<CONTAINER_NAME>.rule=Host(`my-company.com`)"
      - "traefik.http.routers.<CONTAINER_NAME>.tls.certresolver=letsencrypt"

Simplemente reemplaza <CONTAINER_NAME> con el nombre deseado para tu contenedor, por ejemplo frontend.

Al crear un subdominio, Let's Encrypt sigue las RFC 952 y 1123, que solo permiten caracteres en el conjunto [a-zA-Z0-9-].

Aunque los guiones bajos ('_') están permitidos en los nombres de registros DNS, no son aceptables en los nombres de host. Como resultado, Let's Encrypt rechaza subdominios como "my_subdomain.example.com" y muestra el error "Domain name contains an invalid character".

Para resolver este problema, simplemente elimina cualquier guion bajo de tus subdominios.