- Dockerfile: https://github.com/JVarelaR/EjemploHostingDocker.git
- Docker Compose: https://github.com/JVarelaR/DockerComposeWebExample.git
Esta plataforma permite registrar usuarios, iniciar sesión y desplegar proyectos web desde repositorios Git usando Docker. Cada proyecto se ejecuta como uno o varios contenedores y queda disponible localmente mediante un subdominio con el formato:
http://nombre-proyecto.nombre-usuario.localhost
El sistema está construido alrededor de una aplicación Flask que coordina la interfaz web, la autenticación con Roble, la persistencia de usuarios y proyectos, y la administración de contenedores mediante Docker. Traefik funciona como reverse proxy y enruta las solicitudes HTTP hacia la aplicación principal o hacia los proyectos desplegados según el host recibido.
Se soportan dos tipos de despliegue:
- Repositorios con
Dockerfile - Repositorios con
docker-compose
La persistencia de usuarios y proyectos se realiza mediante la API REST de Roble.
La arquitectura está definida por el archivo principal de Docker Compose y por los módulos internos de la aplicación Flask.
Usuario
│
▼
Traefik
│
▼
Flask
├── Roble API
└── Docker Engine
│
└── Contenedores de proyectos
El entorno principal contiene dos servicios:
web: aplicación Flask que expone la interfaz web, maneja autenticación, valida proyectos, clona repositorios y controla Docker.traefik: reverse proxy que escucha en el puerto80y enruta solicitudes hacia Flask o hacia los contenedores desplegados.
Ambos servicios comparten la red Docker hosting_net. La aplicación Flask monta el socket /var/run/docker.sock, lo que le permite crear, iniciar, detener y eliminar contenedores. También monta ./tmp en /tmp/hosting para almacenar temporalmente los repositorios clonados.
Traefik se configura con el proveedor Docker habilitado y con exposedbydefault=false, para que solamente los servicios con labels explícitas quedan expuestos.
La aplicación principal queda disponible en:
http://localhost
Los proyectos desplegados se publican mediante labels generadas por el backend. Para cada proyecto se construye un host local a partir del nombre del proyecto y del usuario.
Además, el servicio web incluye una regla de Traefik para recibir hosts con la forma proyecto.usuario.localhost. Esto permite detectar solicitudes dirigidas a proyectos detenidos, reactivar sus contenedores y redirigir nuevamente la solicitud al mismo URL.
El frontend utiliza plantillas Jinja y JavaScript para manejar autenticación, creación de proyectos y administración de contenedores desde el panel de usuario.
La interfaz interactúa con el backend Flask para ejecutar operaciones de autenticación y administración de proyectos.
app.py: inicializa Flask, CORS, rate limiting, blueprints y la lógica de reactivación por host.routes/auth_routes.py: maneja registro, login y logout.routes/project_routes.py: maneja creación y administración de proyectos.services/auth_services.py: encapsula llamadas HTTP a Roble para autenticación.services/db_services.py: realiza operaciones sobre usuarios y proyectos en Roble.services/project_services.py: valida proyectos, clona repositorios y coordina despliegues.services/docker_services.py: administra contenedores y stacks Docker Compose.services/compose_transformer.py: adapta archivos Compose al entorno de hosting.services/project_wakeup.py: reactiva proyectos detenidos cuando reciben solicitudes.services/check_innactives.py: revisa periódicamente proyectos activos para aplicar control de inactividad.services/docker_naming.py: normaliza nombres y genera hosts locales.
Roble se utiliza para:
- autenticación de usuarios;
- persistencia de datos de usuarios y proyectos.
El backend utiliza endpoints de autenticación bajo:
/auth/{ROBLE_DB_NAME}
y endpoints REST bajo:
/database/{ROBLE_DB_NAME}
Las operaciones autenticadas utilizan el access token almacenado en la sesión Flask. Si Roble responde con 401, el backend intenta renovar el token usando el refresh token.
Para proyectos con Dockerfile, el backend:
- clona el repositorio;
- valida que exista un
Dockerfile; - construye una imagen Docker a partir del repositorio clonado;
- crea un contenedor conectado a
hosting_net; - agrega labels de Traefik;
- guarda el ID del contenedor en Roble.
Para proyectos con docker-compose, el backend:
- clona el repositorio;
- valida que exista un archivo Compose compatible;
- genera un archivo Compose adaptado para el entorno de hosting;
- elimina configuraciones incompatibles con el entorno compartido;
- conecta los servicios a
hosting_net; - agrega labels de Traefik al servicio público;
- levanta automáticamente los servicios;
- guarda los IDs de los contenedores creados.
El usuario puede registrarse desde /register o iniciar sesión desde /login.
Durante el registro, Flask envía a Roble el correo, contraseña y nombre de usuario usando el endpoint signup-direct. Después del registro, ejecuta login para obtener los tokens de sesión.
Durante el inicio de sesión, Flask autentica el usuario contra Roble y almacena en sesión:
access_token;refresh_token;email;user_id;username.
Después de autenticar, el backend consulta la tabla users. Si no existe una fila local, crea un registro con la información básica del usuario.
El logout intenta cerrar la sesión en Roble y luego limpia la sesión local de Flask.
La creación se inicia desde el panel de proyectos. El frontend envía a POST /projects/create:
- nombre;
- descripción;
- URL del repositorio;
- puerto;
- tipo de despliegue.
El backend valida:
- que exista una sesión activa;
- que el usuario no tenga otro proyecto con el mismo nombre;
- que el repositorio sea accesible;
- que el tipo de despliegue sea válido;
- que el archivo requerido exista;
- que el puerto configurado sea válido.
El repositorio se clona temporalmente antes del despliegue. Si el proyecto utiliza Dockerfile, el backend construye una imagen Docker. Si utiliza Compose, genera un archivo para el entorno de hosting.
Cuando Docker crea los contenedores, el backend registra el proyecto en Roble con estado inicial running.
El host público de cada proyecto se genera con:
{nombre-normalizado}.{usuario-normalizado}.localhost
Traefik utiliza labels generadas por el backend para asociar cada host local con el puerto interno correspondiente.
Desde el panel de proyectos se pueden:
- iniciar proyectos;
- detener proyectos;
- eliminar proyectos.
En proyectos con Dockerfile, las operaciones se aplican directamente sobre el contenedor asociado. En proyectos con Compose, las operaciones utilizan los servicios creados desde el archivo Compose generado.
Al eliminar un proyecto, el backend intenta eliminar contenedores, imágenes y recursos asociados. También elimina el registro correspondiente en Roble.
Cuando llega una solicitud a un host con la forma proyecto.usuario.localhost, Flask verifica si el proyecto existe y si sus contenedores están detenidos.
Si el proyecto corresponde a un registro válido, el backend intenta iniciar nuevamente los contenedores y actualiza el estado del proyecto a running.
Después de reactivar el proyecto, la solicitud original se redirige nuevamente al mismo URL.
Las rutas de proyectos requieren sesión activa. Antes de iniciar, detener o eliminar un proyecto, el backend verifica que el recurso pertenezca al usuario autenticado.
El proyecto aplica rate limiting con Flask-Limiter:
/login:3 per minute;/register:3 per minute;/projects/create:3 per minute;/projects/start:10 per minute;/projects/stop:10 per minute;/projects/delete:10 per minute.
Traefik utiliza exposición explícita mediante labels. Para proyectos Compose, el backend genera una versión adaptada del archivo original y elimina configuraciones incompatibles con el entorno compartido.
Los contenedores desplegados incluyen límites de CPU y memoria para evitar sobrecarga del sistema.
También se crean con política de reinicio unless-stopped y se conectan a la red hosting_net.
El sistema ejecuta revisiones periódicas para detectar proyectos inactivos. Cuando un proyecto supera el umbral configurado, el backend detiene automáticamente sus contenedores para optimizar recursos.
La persistencia se realiza mediante la API REST de Roble. El servicio db_services.py contiene las operaciones de lectura, inserción, actualización y eliminación.
id: identificador del usuario.username: nombre usado para construir identificadores y subdominios locales.email: correo usado durante autenticación.created_at: fecha de creación del registro.
Durante el registro o login, el backend busca usuarios por correo. Si no existe una fila local, crea un nuevo registro asociado al usuario autenticado.
_id: identificador del proyecto.nombre: nombre visible del proyecto.descripcion: descripción mostrada en el panel.repo_url: URL del repositorio Git.deployment_type: tipo de despliegue.puerto_externo: puerto utilizado por Traefik.containers_ids: IDs de contenedores asociados.repo_path: ruta local del repositorio clonado.estado: estado actual del proyecto.ultimo_acceso: referencia temporal para control de actividad.user_id: usuario propietario.created_at: fecha de creación.
La relación entre usuarios y proyectos se maneja mediante user_id. El backend utiliza esta relación para listar proyectos y validar permisos sobre las operaciones de administración.