From 31c567653af0f2d5e9a3ad9c463e6eb5c65807a0 Mon Sep 17 00:00:00 2001 From: "Juan M. Ley" Date: Wed, 6 May 2026 14:23:28 -0600 Subject: [PATCH] docker stuff almost complete --- .gitignore | 3 + docker-compose-py.yaml | 203 ++++++++++++++++++ dockerdotenv.env | 30 +++ dockerfile | 63 ++++++ src/core/config.py | 5 + .../metrics_repository_postgres.py | 3 +- 6 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 docker-compose-py.yaml create mode 100644 dockerdotenv.env create mode 100644 dockerfile diff --git a/.gitignore b/.gitignore index f746f77..639578e 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,6 @@ node_modules/ *.pem credentials.json migrations/ + + +*.tar.gz \ No newline at end of file diff --git a/docker-compose-py.yaml b/docker-compose-py.yaml new file mode 100644 index 0000000..49cf123 --- /dev/null +++ b/docker-compose-py.yaml @@ -0,0 +1,203 @@ +# ============================================================ +# VoxPopuli - docker-compose.yaml +# Orquesta la app + toda la infraestructura de datos/mensajería +# - MySQL → API Usuarios (8000) +# - MongoDB → API Reportes (8001) +# - MongoDB → API Notificaciones(8002) +# - MongoDB → API Moderación (8003) +# - PostgreSQL → API Métricas (8004) ← faltaba esto +# - RabbitMQ → cola de mensajes entre microservicios +# ============================================================ +# Uso rápido: +# cp .env.example .env # ajusta credenciales +# docker compose up --build +# ============================================================ + +name: voxpopuli + +services: + + # ────────────────────────────────────────── + # INFRAESTRUCTURA + # ────────────────────────────────────────── + + mysql: + image: mysql:8.0 + container_name: voxpopuli_mysql + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} + MYSQL_DATABASE: voxpopuli_users + MYSQL_USER: ${MYSQL_USER:-voxpopuli} + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-voxpopuli_pass} + ports: + - "${MYSQL_PORT:-3306}:3306" + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-rootpassword}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - voxpopuli_net + + mongodb-reports: + image: mongo:7.0 + container_name: voxpopuli_mongo_reports + restart: unless-stopped + environment: + MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER:-admin} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD:-admin_password} + MONGO_INITDB_DATABASE: voxpopuli_reports + command: mongod --auth + ports: + - "${MONGO_REPORTS_PORT:-27017}:27017" + volumes: + - mongo_reports_data:/data/db + healthcheck: + test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok", "--username", "${MONGO_USER:-admin}", "--password", "${MONGO_PASSWORD:-admin_password}", "--authenticationDatabase", "admin"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + networks: + - voxpopuli_net + + mongodb-notifications: + image: mongo:7.0 + container_name: voxpopuli_mongo_notifications + restart: unless-stopped + environment: + MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER:-admin} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD:-admin_password} + MONGO_INITDB_DATABASE: voxpopuli_notifications + command: mongod --auth + ports: + - "${MONGO_NOTIFICATIONS_PORT:-27018}:27017" + volumes: + - mongo_notifications_data:/data/db + healthcheck: + test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok", "--username", "${MONGO_USER:-admin}", "--password", "${MONGO_PASSWORD:-admin_password}", "--authenticationDatabase", "admin"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + networks: + - voxpopuli_net + + # PostgreSQL para la API de Métricas + postgres: + image: postgres:16-alpine + container_name: voxpopuli_postgres + restart: unless-stopped + environment: + POSTGRES_USER: ${POSTGRES_USER:-voxpopuli} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-voxpopuli_pass} + POSTGRES_DB: voxpopuli_metrics + ports: + - "${POSTGRES_PORT:-5432}:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-voxpopuli} -d voxpopuli_metrics"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + networks: + - voxpopuli_net + + rabbitmq: + image: rabbitmq:3.13-management + container_name: voxpopuli_rabbitmq + restart: unless-stopped + environment: + RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-voxpopuli} + RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS:-voxpopuli_pass} + ports: + - "${RABBITMQ_AMQP_PORT:-5672}:5672" + - "${RABBITMQ_MGMT_PORT:-15672}:15672" + volumes: + - rabbitmq_data:/var/lib/rabbitmq + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - voxpopuli_net + + # ────────────────────────────────────────── + # APLICACIÓN + # ────────────────────────────────────────── + + app: + build: + context: . + dockerfile: Dockerfile + image: voxpopuli-app:latest + container_name: voxpopuli_app + restart: unless-stopped + env_file: + - .env + environment: + # MySQL - Usuarios + MYSQL_URL: mysql+pymysql://${MYSQL_USER:-voxpopuli}:${MYSQL_PASSWORD:-voxpopuli_pass}@mysql/voxpopuli_users + # MongoDB - Reportes + MONGODB_URL: mongodb://${MONGO_USER:-admin}:${MONGO_PASSWORD:-admin_password}@mongodb-reports:27017 + MONGODB_DB: voxpopuli_reports + # MongoDB - Notificaciones + MONGODB_NOTIFICATIONS_URL: mongodb://${MONGO_USER:-admin}:${MONGO_PASSWORD:-admin_password}@mongodb-notifications:27017 + MONGODB_NOTIFICATIONS_DB: voxpopuli_notifications + # PostgreSQL - Métricas + POSTGRES_URL: postgresql://${POSTGRES_USER:-voxpopuli}:${POSTGRES_PASSWORD:-voxpopuli_pass}@postgres:5432/voxpopuli_metrics + # RabbitMQ + RABBITMQ_URL: amqp://${RABBITMQ_USER:-voxpopuli}:${RABBITMQ_PASS:-voxpopuli_pass}@rabbitmq:5672/ + # App + HOST: 0.0.0.0 + LOG_LEVEL: ${LOG_LEVEL:-info} + ports: + - "8000:8000" # API Usuarios + - "8001:8001" # API Reportes + - "8002:8002" # API Notificaciones + - "8003:8003" # API Moderación + - "8004:8004" # API Métricas + depends_on: + mysql: + condition: service_healthy + mongodb-reports: + condition: service_healthy + mongodb-notifications: + condition: service_healthy + postgres: + condition: service_healthy + rabbitmq: + condition: service_healthy + networks: + - voxpopuli_net + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/')"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 20s + +# ────────────────────────────────────────── +# VOLÚMENES +# ────────────────────────────────────────── +volumes: + mysql_data: + mongo_reports_data: + mongo_notifications_data: + postgres_data: + rabbitmq_data: + +# ────────────────────────────────────────── +# RED INTERNA +# ────────────────────────────────────────── +networks: + voxpopuli_net: + driver: bridge \ No newline at end of file diff --git a/dockerdotenv.env b/dockerdotenv.env new file mode 100644 index 0000000..8425767 --- /dev/null +++ b/dockerdotenv.env @@ -0,0 +1,30 @@ +# ============================================================ +# VoxPopuli - Variables de entorno para Docker +# Copia este archivo como .env y ajusta los valores +# ============================================================ + +# ── MySQL (API Usuarios) ───────────────────────────────────── +MYSQL_ROOT_PASSWORD=rootpassword +MYSQL_USER=voxpopuli +MYSQL_PASSWORD=voxpopuli_pass +MYSQL_PORT=3306 + +# ── MongoDB (Reportes + Notificaciones) ────────────────────── +MONGO_USER=admin +MONGO_PASSWORD=admin_password +MONGO_REPORTS_PORT=27017 +MONGO_NOTIFICATIONS_PORT=27018 + +# ── PostgreSQL (API Métricas) ──────────────────────────────── +POSTGRES_USER=voxpopuli +POSTGRES_PASSWORD=voxpopuli_pass +POSTGRES_PORT=5432 + +# ── RabbitMQ ───────────────────────────────────────────────── +RABBITMQ_USER=voxpopuli +RABBITMQ_PASS=voxpopuli_pass +RABBITMQ_AMQP_PORT=5672 +RABBITMQ_MGMT_PORT=15672 + +# ── App ────────────────────────────────────────────────────── +LOG_LEVEL=info diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..83a0053 --- /dev/null +++ b/dockerfile @@ -0,0 +1,63 @@ +# ============================================================ +# VoxPopuli - Dockerfile +# Microservicios FastAPI: Usuarios (8000), Reportes (8001), +# Notificaciones (8002), Moderación (8003), Métricas (8004) +# ============================================================ + +# --- Stage 1: Builder --- +FROM python:3.11-slim AS builder + +WORKDIR /build + +# Instalar dependencias del sistema necesarias para compilación +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc \ + libffi-dev \ + libssl-dev \ + default-libmysqlclient-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +# Copiar e instalar dependencias Python en un directorio aislado +COPY requirements.txt . +RUN pip install --upgrade pip && \ + pip install --prefix=/install --no-cache-dir -r requirements.txt + + +# --- Stage 2: Runtime --- +FROM python:3.11-slim + +LABEL maintainer="Hokzaap S. de R.L. de C.V." +LABEL description="VoxPopuli - Infraestructura de Voz Ciudadana" + +WORKDIR /app + +# Instalar únicamente librerías de runtime +RUN apt-get update && apt-get install -y --no-install-recommends \ + default-libmysqlclient-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /app/logs/ + +# Copiar paquetes instalados desde el builder +COPY --from=builder /install /usr/local + +# Copiar código fuente +COPY src/ ./src/ + +# Variables de entorno con valores por defecto (sobreescribibles via .env o compose) +ENV HOST=0.0.0.0 \ + LOG_LEVEL=info \ + PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONPATH=/app/src + +# Puertos expuestos por los cinco microservicios +EXPOSE 8000 8001 8002 8003 8004 + +# Healthcheck básico contra la API de Usuarios +HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \ + CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/')" || exit 1 + +# Punto de entrada +CMD ["python", "src/main.py"] \ No newline at end of file diff --git a/src/core/config.py b/src/core/config.py index 031e66f..9d48d3f 100644 --- a/src/core/config.py +++ b/src/core/config.py @@ -35,6 +35,11 @@ class Settings(BaseSettings): default=os.getenv("RABBITMQ_URI", "localhost") ) + + postgres_url: str = Field( + default=os.getenv("POSTGRES_URL", "postgresql://voxpopuli:voxpopuli_pass@localhost:5432/voxpopuli_metrics"), + description="Base de datos PostgreSQL para métricas" + ) # JWT Configuration jwt_secret_key: str = Field( diff --git a/src/infrastructure/adapters/persistence/metrics_repository_postgres.py b/src/infrastructure/adapters/persistence/metrics_repository_postgres.py index 1f70e01..e6ed3c6 100644 --- a/src/infrastructure/adapters/persistence/metrics_repository_postgres.py +++ b/src/infrastructure/adapters/persistence/metrics_repository_postgres.py @@ -10,6 +10,7 @@ from core.config import ConfSettings Base = declarative_base() +#settings = ConfSettings() class MetricModel(Base): """Modelo SQLAlchemy para métricas en Postgres""" @@ -28,7 +29,7 @@ class MetricsRepositoryPostgres(MetricsRepository): """Implementación de repositorio de métricas con PostgreSQL""" def __init__(self): - db_url = f"postgresql://voxpopuli:voxpopuli_pass@localhost:5432/voxpopuli_metrics" + db_url = f"postgresql://voxpopuli:voxpopuli_pass@postgres:5432/voxpopuli_metrics" self.engine = create_engine(db_url, echo=False) Base.metadata.create_all(self.engine) self.SessionLocal = sessionmaker(bind=self.engine)