Saltar al contenido principal Saltar a navegación Saltar al pie de página

Red

Exponga servicios con el proxy inverso, etiquetas Docker, certificados TLS, DNS y redirección de puertos TCP/UDP.

Red

Esta página explica cómo los servicios ejecutándose dentro de daemons Docker aislados se hacen accesibles desde internet. Cubre el sistema de proxy inverso, las etiquetas Docker para enrutamiento, los certificados TLS, el DNS y la redirección de puertos TCP/UDP.

Para saber cómo los servicios obtienen sus IPs de loopback y el sistema de slots de .rediacc.json, consulte Servicios.

Cómo Funciona

Rediacc utiliza un sistema de proxy de dos componentes para enrutar tráfico externo a los contenedores:

  1. Servidor de rutas — un servicio systemd que descubre contenedores en ejecución en todos los daemons Docker de los repositorios. Inspecciona las etiquetas de los contenedores y genera la configuración de enrutamiento, servida como un endpoint YAML.
  2. Traefik — un proxy inverso que consulta el servidor de rutas cada 5 segundos y aplica las rutas descubiertas. Gestiona el enrutamiento HTTP/HTTPS, la terminación TLS y la redirección TCP/UDP.

El flujo es el siguiente:

Internet → Traefik (puertos 80/443/TCP/UDP)
               ↓ consulta cada 5s
           Servidor de rutas (descubre contenedores)
               ↓ inspecciona etiquetas
           Daemons Docker (/var/run/rediacc/docker-*.sock)

           Contenedores (vinculados a IPs loopback 127.x.x.x)

Cuando agrega las etiquetas correctas a un contenedor y lo inicia con renet compose, automáticamente se vuelve enrutable — no se necesita configuración manual del proxy.

El binario del servidor de rutas se mantiene sincronizado con la versión de su CLI. Cuando la CLI actualiza el binario renet en una máquina, el servidor de rutas se reinicia automáticamente (~1–2 segundos). Esto no causa tiempo de inactividad, Traefik continúa sirviendo tráfico con su última configuración conocida durante el reinicio y recoge la nueva configuración en la siguiente consulta. Las conexiones de clientes existentes no se ven afectadas. Los contenedores de su aplicación no se tocan.

Etiquetas Docker

El enrutamiento se controla mediante etiquetas de contenedores Docker. Hay dos niveles:

Nivel 1: Etiquetas rediacc.* (Automáticas)

Estas etiquetas son inyectadas automáticamente por renet compose al iniciar servicios. No necesita agregarlas manualmente.

EtiquetaDescripciónEjemplo
rediacc.service_nameIdentidad del serviciomyapp
rediacc.service_ipIP de loopback asignada127.0.11.2
rediacc.network_idID del daemon del repositorio2816
rediacc.repo_nameRepository namemarketing
rediacc.tcp_portsPuertos TCP en los que escucha el servicio8080,8443
rediacc.udp_portsPuertos UDP en los que escucha el servicio53

Cuando un contenedor tiene solo etiquetas rediacc.* (sin traefik.enable=true), el servidor de rutas genera una ruta automática usando el nombre del repositorio y el subdominio de la máquina:

{service}.{repoName}.{machineName}.{baseDomain}

Por ejemplo, un servicio llamado myapp en un repositorio llamado marketing en la máquina server-1 con dominio base example.com obtiene:

myapp.marketing.server-1.example.com

Cada repositorio tiene su propio nivel de subdominio, por lo que las bifurcaciones y diferentes repos nunca colisionan. Cuando bifurca un repositorio (p. ej., marketing-staging), la bifurcación obtiene automáticamente rutas distintas. Para servicios con dominios personalizados, use etiquetas de Nivel 2 o la etiqueta rediacc.domain.

Dominio personalizado via rediacc.domain

Puede establecer un dominio personalizado para un servicio usando la etiqueta rediacc.domain en su docker-compose.yml. Se admiten tanto nombres cortos como dominios completos:

labels:
  # Nombre corto, se resuelve a cloud.example.com usando el baseDomain de la máquina
  - "rediacc.domain=cloud"

  # Dominio completo, se usa tal cual
  - "rediacc.domain=cloud.example.com"

Un valor sin puntos se trata como nombre corto y se le agrega automáticamente el baseDomain de la máquina. Un valor con puntos se usa como dominio completo.

Cuando machineName está configurado, los servicios con dominio personalizado obtienen dos rutas: una en el dominio base (cloud.example.com) y otra en el subdominio de la máquina (cloud.server-1.example.com).

Nivel 2: Etiquetas traefik.* (Definidas por el Usuario)

Agregue estas etiquetas a su docker-compose.yml cuando desee enrutamiento con dominio personalizado, TLS o puntos de entrada específicos. Configurar traefik.enable=true indica al servidor de rutas que use sus reglas personalizadas en lugar de generar una ruta automática.

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.myapp.rule=Host(`app.example.com`)"
  - "traefik.http.routers.myapp.entrypoints=websecure,websecure-v6"
  - "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
  - "traefik.http.services.myapp.loadbalancer.server.port=8080"

Estas usan la sintaxis estándar de etiquetas de Traefik v3.

Consejo: Los servicios solo internos (bases de datos, cachés, colas de mensajes) no deben tener traefik.enable=true. Solo necesitan etiquetas rediacc.*, que se inyectan automáticamente.

Exponer Servicios HTTP/HTTPS

Requisitos Previos

  1. Infraestructura configurada en la máquina (Configuración de Máquinas — Configuración de Infraestructura):

    # Credenciales compartidas (una vez por configuración, aplica a todas las máquinas)
    rdc config infra set -m server-1 \
      --cert-email admin@example.com \
      --cf-dns-token your-cloudflare-api-token
    
    # Configuración específica de la máquina
    rdc config infra set -m server-1 \
      --public-ipv4 203.0.113.50 \
      --base-domain example.com
    
    rdc config infra push -m server-1
  2. Registros DNS apuntando su dominio a la IP pública del servidor (consulte Configuración de DNS más abajo).

Agregar Etiquetas

Agregue etiquetas traefik.* a los servicios que desea exponer en su docker-compose.yml:

services:
  myapp:
    image: myapp:latest
    environment:
      - LISTEN_ADDR=${MYAPP_IP}:8080
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.myapp.rule=Host(`app.example.com`)"
      - "traefik.http.routers.myapp.entrypoints=websecure,websecure-v6"
      - "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
      - "traefik.http.services.myapp.loadbalancer.server.port=8080"

  database:
    image: postgres:17
    command: ["-c", "listen_addresses=${DATABASE_IP}"]
    # Sin etiquetas traefik, la base de datos es solo interna
EtiquetaPropósito
traefik.enable=trueHabilita el enrutamiento personalizado de Traefik para este contenedor
traefik.http.routers.{name}.ruleRegla de enrutamiento — típicamente Host(\dominio`)`
traefik.http.routers.{name}.entrypointsEn qué puertos escuchar: websecure (HTTPS IPv4), websecure-v6 (HTTPS IPv6)
traefik.http.routers.{name}.tls.certresolverResolvedor de certificados — use letsencrypt para Let’s Encrypt automático
traefik.http.services.{name}.loadbalancer.server.portEl puerto en el que su aplicación escucha dentro del contenedor

El {name} en las etiquetas es un identificador arbitrario — solo necesita ser consistente entre las etiquetas de router/servicio/middleware relacionadas.

Nota: Las etiquetas rediacc.* (rediacc.service_name, rediacc.service_ip, rediacc.network_id) se inyectan automáticamente por renet compose. No necesita agregarlas a su archivo compose.

Certificados TLS

Los certificados TLS se obtienen automáticamente vía Let’s Encrypt usando el desafío DNS-01 de Cloudflare. Las credenciales se configuran una vez por configuración (compartidas entre todas las máquinas):

rdc config infra set -m server-1 \
  --cert-email admin@example.com \
  --cf-dns-token your-cloudflare-api-token

Las rutas automáticas usan certificados comodín a nivel del subdominio del repositorio (*.marketing.server-1.example.com) en lugar de certificados por servicio. Esto evita los límites de velocidad de Let’s Encrypt y acelera el inicio. Las rutas con dominio personalizado usan comodines a nivel de máquina (*.server-1.example.com).

Para rutas de Nivel 2 con traefik.http.routers.{name}.tls.certresolver=letsencrypt, los SANs de dominio comodín se inyectan automáticamente basándose en el nombre de host de la ruta.

El token de la API DNS de Cloudflare necesita el permiso Zone:DNS:Edit para los dominios que desea asegurar.

Redirección de Puertos TCP/UDP

Para protocolos no HTTP (servidores de correo, DNS, bases de datos expuestas externamente), use la redirección de puertos TCP/UDP.

Paso 1: Registrar Puertos

Agregue los puertos requeridos durante la configuración de infraestructura:

rdc config infra set -m server-1 \
  --tcp-ports 25,143,465,587,993 \
  --udp-ports 53

rdc config infra push -m server-1

Esto crea puntos de entrada de Traefik llamados tcp-{port} y udp-{port}.

Ejemplo de TCP Plano (Base de Datos)

Para exponer una base de datos externamente sin paso directo de TLS (Traefik redirige TCP sin procesar):

services:
  postgres:
    image: postgres:17
    command: -c listen_addresses=${POSTGRES_IP} -c port=5432
    labels:
      - "traefik.enable=true"
      - "traefik.tcp.routers.mydb.entrypoints=tcp-5432"
      - "traefik.tcp.routers.mydb.rule=HostSNI(`*`)"
      - "traefik.tcp.services.mydb.loadbalancer.server.port=5432"

El puerto 5432 está preconfigurado (ver abajo), por lo que no se necesita configuración con --tcp-ports.

Nota de seguridad: Exponer una base de datos a internet es un riesgo. Use esto solo cuando los clientes remotos necesiten acceso directo. Para la mayoría de las configuraciones, mantenga la base de datos interna y conéctese a través de su aplicación.

Después de agregar o eliminar puertos, siempre vuelva a ejecutar rdc config infra push para actualizar la configuración del proxy.

Paso 2: Agregar Etiquetas TCP/UDP

Use etiquetas traefik.tcp.* o traefik.udp.* en su archivo compose:

services:
  mail-server:
    image: ghcr.io/docker-mailserver/docker-mailserver:latest
    labels:
      - "traefik.enable=true"

      # SMTP (puerto 25)
      - "traefik.tcp.routers.mail-smtp.entrypoints=tcp-25"
      - "traefik.tcp.routers.mail-smtp.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-smtp.service=mail-smtp"
      - "traefik.tcp.services.mail-smtp.loadbalancer.server.port=25"

      # IMAPS (puerto 993), paso directo de TLS
      - "traefik.tcp.routers.mail-imaps.entrypoints=tcp-993"
      - "traefik.tcp.routers.mail-imaps.rule=HostSNI(`mail.example.com`)"
      - "traefik.tcp.routers.mail-imaps.tls.passthrough=true"
      - "traefik.tcp.routers.mail-imaps.service=mail-imaps"
      - "traefik.tcp.services.mail-imaps.loadbalancer.server.port=993"

Conceptos clave:

  • HostSNI(\*`)` coincide con cualquier nombre de host (para protocolos que no envían SNI, como SMTP sin cifrar)
  • tls.passthrough=true significa que Traefik redirige la conexión TLS sin descifrar — la aplicación gestiona TLS por sí misma
  • Los nombres de los puntos de entrada siguen la convención tcp-{port} o udp-{port}

Puertos Preconfigurados

Los siguientes puertos TCP/UDP tienen puntos de entrada por defecto (no es necesario agregarlos vía --tcp-ports). Los puntos de entrada solo se generan para las familias de direcciones configuradas, los puntos de entrada IPv4 requieren --public-ipv4, los puntos de entrada IPv6 requieren --public-ipv6:

PuertoProtocoloUso Común
80HTTPWeb (redirección automática a HTTPS)
443HTTPSWeb (TLS)
3306TCPMySQL/MariaDB
5432TCPPostgreSQL
6379TCPRedis
27017TCPMongoDB
11211TCPMemcached
5672TCPRabbitMQ
9092TCPKafka
53UDPDNS
10000—10010TCPRango dinámico (asignación automática)

Configuración de DNS

DNS Automático (Cloudflare)

Cuando --cf-dns-token está configurado, rdc config infra push crea automáticamente los registros DNS necesarios en Cloudflare:

RegistroTipoContenidoCreado por
server-1.example.comA / AAAAIP pública de la máquinapush-infra
*.server-1.example.comA / AAAAIP pública de la máquinapush-infra
*.marketing.server-1.example.comA / AAAAIP pública de la máquinarepo up

Los registros a nivel de máquina son creados por push-infra y cubren las rutas con dominio personalizado (rediacc.domain). Los registros comodín por repositorio son creados automáticamente por repo up y cubren las rutas automáticas para ese repositorio.

Esto es idempotente, los registros existentes se actualizan si la IP cambia, y se dejan sin cambios si ya son correctos.

El comodín del dominio base (*.example.com) debe crearse manualmente si usa etiquetas de dominio personalizadas como rediacc.domain=erp.

DNS Manual

Si no usa Cloudflare o gestiona DNS manualmente, cree registros A (IPv4) y/o AAAA (IPv6):

# Subdominio de la máquina (para rutas con dominio personalizado como rediacc.domain=erp)
server-1.example.com           A     203.0.113.50
*.server-1.example.com         A     203.0.113.50
*.server-1.example.com         AAAA  2001:db8::1

# Comodines por repositorio (para rutas automáticas como myapp.marketing.server-1.example.com)
*.marketing.server-1.example.com    A     203.0.113.50
*.marketing.server-1.example.com    AAAA  2001:db8::1

# Comodín del dominio base (para servicios con dominio personalizado como rediacc.domain=erp)
*.example.com                  A     203.0.113.50

Con Cloudflare DNS configurado, los registros comodín por repositorio son creados automáticamente por repo up. Con múltiples máquinas, cada máquina obtiene sus propios registros DNS apuntando a su propia IP.

Middlewares

Los middlewares de Traefik modifican las solicitudes y respuestas. Aplíquelos mediante etiquetas.

HSTS (HTTP Strict Transport Security)

labels:
  - "traefik.http.middlewares.myapp-hsts.headers.stsSeconds=15768000"
  - "traefik.http.middlewares.myapp-hsts.headers.stsIncludeSubdomains=true"
  - "traefik.http.middlewares.myapp-hsts.headers.stsPreload=true"
  - "traefik.http.routers.myapp.middlewares=myapp-hsts"

Buffering para Subida de Archivos Grandes

labels:
  - "traefik.http.middlewares.myapp-buffering.buffering.maxRequestBodyBytes=536870912"
  - "traefik.http.routers.myapp.middlewares=myapp-buffering"

Múltiples Middlewares

Encadene middlewares separándolos por comas:

labels:
  - "traefik.http.routers.myapp.middlewares=myapp-hsts,myapp-buffering"

Para la lista completa de middlewares disponibles, consulte la documentación de middlewares de Traefik.

Diagnósticos

Si un servicio no es accesible, conéctese por SSH al servidor y verifique los endpoints del servidor de rutas:

Verificación de Estado

curl -s http://127.0.0.1:7111/health | python3 -m json.tool

Muestra el estado general, el número de routers y servicios descubiertos, y si las rutas automáticas están habilitadas.

Rutas Descubiertas

curl -s http://127.0.0.1:7111/routes.json | python3 -m json.tool

Lista todos los routers HTTP, TCP y UDP con sus reglas, puntos de entrada y servicios backend.

Asignaciones de Puertos

curl -s http://127.0.0.1:7111/ports | python3 -m json.tool

Muestra los mapeos de puertos TCP y UDP para puertos asignados dinámicamente.

Problemas Comunes

ProblemaCausaSolución
Servicio no aparece en las rutasContenedor no ejecutándose o sin etiquetasVerifique con docker ps en el daemon del repositorio; revise las etiquetas
Certificado no emitidoDNS no apunta al servidor o token de Cloudflare inválidoVerifique la resolución DNS; revise los permisos del token de la API de Cloudflare
502 Bad GatewayLa aplicación no escucha en el puerto declaradoVerifique que la aplicación se vincule a su {SERVICE}_IP y que el puerto coincida con loadbalancer.server.port
Puerto TCP no alcanzablePuerto no registrado en la infraestructuraEjecute rdc config infra set --tcp-ports ... y push-infra
Servidor de rutas con versión antiguaEl binario se actualizó pero el servicio no se reinicióOcurre automáticamente al aprovisionar; manual: sudo systemctl restart rediacc-router
Relay STUN/TURN no alcanzableDirecciones de relay cacheadas al inicioRecree el servicio después de cambios de DNS o IP para que recoja la nueva configuración de red

Ejemplo Completo

Este ejemplo despliega una aplicación web con una base de datos PostgreSQL. La aplicación es accesible públicamente en app.example.com con TLS; la base de datos es solo interna.

docker-compose.yml

services:
  webapp:
    image: myregistry/webapp:latest
    environment:
      DATABASE_URL: postgresql://app:changeme@${POSTGRES_IP}:5432/webapp
      LISTEN_ADDR: ${WEBAPP_IP}:3000
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webapp.rule=Host(`app.example.com`)"
      - "traefik.http.routers.webapp.entrypoints=websecure,websecure-v6"
      - "traefik.http.routers.webapp.tls.certresolver=letsencrypt"
      - "traefik.http.services.webapp.loadbalancer.server.port=3000"
      # HSTS
      - "traefik.http.middlewares.webapp-hsts.headers.stsSeconds=15768000"
      - "traefik.http.middlewares.webapp-hsts.headers.stsIncludeSubdomains=true"
      - "traefik.http.routers.webapp.middlewares=webapp-hsts"

  postgres:
    image: postgres:17
    environment:
      POSTGRES_DB: webapp
      POSTGRES_USER: app
      POSTGRES_PASSWORD: changeme
    command: -c listen_addresses=${POSTGRES_IP} -c port=5432
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    # Sin etiquetas traefik, solo interno

Rediaccfile

#!/bin/bash

up() {
    mkdir -p data/postgres
    renet compose -- up -d
}

down() {
    renet compose -- down
}

DNS

Cree un registro A apuntando app.example.com a la IP pública de su servidor:

app.example.com   A   203.0.113.50

Desplegar

rdc repo up --name my-app -m server-1 --mount

En pocos segundos, el servidor de rutas descubre el contenedor, Traefik recoge la ruta, solicita un certificado TLS y https://app.example.com está activo.