Commit 7fd76787 authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

perf: multi-stage Docker builds, .dockerignore, singleton StarRocks connection

parent 7b429387
Pipeline #3378 failed with stage
# ═══ Git & IDE ═══
.git
.gitignore
.vscode
.idea
*.swp
*.swo
# ═══ Python cache ═══
__pycache__
*.pyc
.env
*.pyo
*.egg-info
.pytest_cache
.mypy_cache
.ruff_cache
# ═══ Virtual environments ═══
.venv
venv
.git
.gitignore
env
# ═══ Docker ═══
Dockerfile*
docker-compose*
.dockerignore
logs
data
# ═══ Dev & test files ═══
tests/
*.test.py
setup_readonly_user.py
# ═══ Docs & artifacts ═══
*.md
LICENSE
# ═══ OS files ═══
.DS_Store
Thumbs.db
# ═══ Logs ═══
*.log
logs/
# ═══ Local env backup ═══
.env.local
.env.backup
.env.example
# ============================================================
# DOCKERFILE.DEV - Local Development (Hot Reload + Cache)
# DOCKERFILE.DEV — Multi-stage Dev Build (Hot Reload)
# ============================================================
# Sử dụng Python 3.11 Slim để tối ưu dung lượng
# Stage 1: Builder — install dependencies
# Stage 2: Runtime — dev server with reload
# ============================================================
# ──── Stage 1: Builder ────
FROM python:3.11-slim AS builder
WORKDIR /build
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt \
&& pip install --no-cache-dir --user watchdog[watchmedo]
# ──── Stage 2: Runtime ────
FROM python:3.11-slim
# Thiết lập thư mục làm việc
WORKDIR /app
# Thiết lập biến môi trường
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 curl \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get purge -y --auto-remove
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV ENV=development
# Copy requirements.txt trước để tận dụng Docker cache
COPY requirements.txt .
# Cài đặt thư viện Python (Docker layer cache)
RUN pip install -r requirements.txt && pip install watchdog[watchmedo]
# Copy installed packages from builder
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
# Copy toàn bộ source code vào image
# Source code mounted via volume in docker-compose (not COPY)
COPY . .
# Expose port 5000
EXPOSE 5000
# Health check (optional)
HEALTHCHECK --interval=10s --timeout=5s --start-period=5s --retries=2 \
CMD python -c "import requests; requests.get('http://localhost:5000/docs')" || exit 1
CMD curl -f http://localhost:5000/docs || exit 1
CMD ["gunicorn", "--workers", "1", "--worker-class", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:5000", "--timeout", "120", "--reload", "server:app"]
# ============================================================
# DOCKERFILE.PROD — Multi-stage Production Build
# ============================================================
# Stage 1: Build — install dependencies in isolated layer
# Stage 2: Runtime — minimal image with only what's needed
# ============================================================
FROM python:3.11-slim
# ──── Stage 1: Builder ────
FROM python:3.11-slim AS builder
WORKDIR /build
# Install only build-time system deps (if any wheels need compiling)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
# Install to user site-packages (no root pollution)
RUN pip install --no-cache-dir --user -r requirements.txt
# ──── Stage 2: Runtime ────
FROM python:3.11-slim
WORKDIR /app
# Minimal runtime deps only
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 curl \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get purge -y --auto-remove
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV ENV=development
ENV ENV=production
COPY requirements.txt .
RUN pip install -r requirements.txt
# Copy only installed packages from builder (no pip cache, no gcc)
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
# Copy application source
COPY . .
EXPOSE 5000
......@@ -19,4 +48,13 @@ EXPOSE 5000
ENV WORKERS=1
ENV TIMEOUT=60
CMD gunicorn server:app --workers $WORKERS --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:5000 --timeout $TIMEOUT
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:5000/docs || exit 1
CMD gunicorn server:app \
--workers $WORKERS \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:5000 \
--timeout $TIMEOUT \
--access-logfile - \
--error-logfile -
......@@ -9,7 +9,7 @@ from typing import Optional
from fastapi import APIRouter, Query
from fastapi.responses import JSONResponse
from common.starrocks_connection import StarRocksConnection
from common.starrocks_connection import get_db_connection
from agent.tools.product_mapping import resolve_color, resolve_product_name, SYNONYM_TO_DB
logger = logging.getLogger(__name__)
......@@ -21,7 +21,7 @@ TABLE_NAME = "shared_source.magento_product_dimension_with_text_embedding"
@router.get("/api/products/overview", summary="Product catalog KPI overview")
async def products_overview():
"""Return aggregate KPIs for the product catalog."""
db = StarRocksConnection()
db = get_db_connection()
try:
rows = await db.execute_query_async(f"""
SELECT
......@@ -104,7 +104,7 @@ async def products_list(
offset: int = Query(0, ge=0),
):
"""Return paginated product listing grouped by internal_ref_code."""
db = StarRocksConnection()
db = get_db_connection()
# Build WHERE clauses
clauses = []
......@@ -234,7 +234,7 @@ async def products_colors(
code: str = Query(..., description="internal_ref_code"),
):
"""Return all color variants for a given internal_ref_code."""
db = StarRocksConnection()
db = get_db_connection()
try:
variants = await db.execute_query_async(
f"""
......@@ -266,7 +266,7 @@ async def products_colors(
@router.get("/api/products/filters", summary="Available filter options")
async def products_filters():
"""Return all available filter values for color, product_line, age, season."""
db = StarRocksConnection()
db = get_db_connection()
try:
colors = await db.execute_query_async(f"""
SELECT master_color, COUNT(DISTINCT internal_ref_code) AS cnt
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment