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:
- 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.
- 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.
| Etiqueta | Descripción | Ejemplo |
|---|---|---|
rediacc.service_name | Identidad del servicio | myapp |
rediacc.service_ip | IP de loopback asignada | 127.0.11.2 |
rediacc.network_id | ID del daemon del repositorio | 2816 |
rediacc.repo_name | Repository name | marketing |
rediacc.tcp_ports | Puertos TCP en los que escucha el servicio | 8080,8443 |
rediacc.udp_ports | Puertos UDP en los que escucha el servicio | 53 |
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 etiquetasrediacc.*, que se inyectan automáticamente.
Exponer Servicios HTTP/HTTPS
Requisitos Previos
-
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 -
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
| Etiqueta | Propósito |
|---|---|
traefik.enable=true | Habilita el enrutamiento personalizado de Traefik para este contenedor |
traefik.http.routers.{name}.rule | Regla de enrutamiento — típicamente Host(\dominio`)` |
traefik.http.routers.{name}.entrypoints | En qué puertos escuchar: websecure (HTTPS IPv4), websecure-v6 (HTTPS IPv6) |
traefik.http.routers.{name}.tls.certresolver | Resolvedor de certificados — use letsencrypt para Let’s Encrypt automático |
traefik.http.services.{name}.loadbalancer.server.port | El 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 porrenet 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 pushpara 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=truesignifica 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}oudp-{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:
| Puerto | Protocolo | Uso Común |
|---|---|---|
| 80 | HTTP | Web (redirección automática a HTTPS) |
| 443 | HTTPS | Web (TLS) |
| 3306 | TCP | MySQL/MariaDB |
| 5432 | TCP | PostgreSQL |
| 6379 | TCP | Redis |
| 27017 | TCP | MongoDB |
| 11211 | TCP | Memcached |
| 5672 | TCP | RabbitMQ |
| 9092 | TCP | Kafka |
| 53 | UDP | DNS |
| 10000—10010 | TCP | Rango 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:
| Registro | Tipo | Contenido | Creado por |
|---|---|---|---|
server-1.example.com | A / AAAA | IP pública de la máquina | push-infra |
*.server-1.example.com | A / AAAA | IP pública de la máquina | push-infra |
*.marketing.server-1.example.com | A / AAAA | IP pública de la máquina | repo 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
| Problema | Causa | Solución |
|---|---|---|
| Servicio no aparece en las rutas | Contenedor no ejecutándose o sin etiquetas | Verifique con docker ps en el daemon del repositorio; revise las etiquetas |
| Certificado no emitido | DNS no apunta al servidor o token de Cloudflare inválido | Verifique la resolución DNS; revise los permisos del token de la API de Cloudflare |
| 502 Bad Gateway | La aplicación no escucha en el puerto declarado | Verifique que la aplicación se vincule a su {SERVICE}_IP y que el puerto coincida con loadbalancer.server.port |
| Puerto TCP no alcanzable | Puerto no registrado en la infraestructura | Ejecute rdc config infra set --tcp-ports ... y push-infra |
| Servidor de rutas con versión antigua | El binario se actualizó pero el servicio no se reinició | Ocurre automáticamente al aprovisionar; manual: sudo systemctl restart rediacc-router |
| Relay STUN/TURN no alcanzable | Direcciones de relay cacheadas al inicio | Recree 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.