File size: 4,796 Bytes
8ce332b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5553f71
 
 
8ce332b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5553f71
8ce332b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# ─────────────────────────────────────────────────────────────────────────────
# CustomerCore β€” Production Dockerfile (Phase 10)
#
# WHY MULTI-STAGE BUILD?
# Multi-stage builds separate the build environment from the runtime environment.
# Stage 1 (builder): Installs all dependencies including build tools (gcc, etc.)
# Stage 2 (runtime): Copies only the installed packages, not the build tools.
# Result: Final image is ~60% smaller, has a smaller attack surface (no gcc in prod).
#
# This image is for the full CustomerCore stack including PySpark/JVM.
# For Hugging Face Spaces deployment (2 vCPU, 16GB RAM), use Dockerfile.hf
# which strips PySpark to fit within HF's resource limits.
#
# BASE IMAGE CHOICE: python:3.12-slim-bookworm
# - slim: Minimal Debian Bookworm (no docs, man pages, debug tools)
# - bookworm: Debian 12 LTS β€” security patches until 2028
# - Not alpine: PySpark's JVM has compatibility issues with Alpine's musl libc
# ─────────────────────────────────────────────────────────────────────────────

# ── Stage 1: Builder ─────────────────────────────────────────────────────────
FROM python:3.12-slim-bookworm AS builder

# Set build environment
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

WORKDIR /build

# Install build dependencies (only needed at build time, not runtime)
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    g++ \
    libffi-dev \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

# Install Python dependencies into a prefix directory (not system Python)
# This makes it easy to copy just the packages into the runtime stage
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    sed -i '/pywin32/d' requirements.txt && \
    pip install --prefix=/install --extra-index-url https://download.pytorch.org/whl/cpu -r requirements.txt


# ── Stage 2: Runtime ──────────────────────────────────────────────────────────
FROM python:3.12-slim-bookworm AS runtime

# Labels for Docker Hub / GitHub Container Registry metadata
LABEL org.opencontainers.image.title="CustomerCore API" \
      org.opencontainers.image.description="Enterprise B2B AI Customer Support Platform" \
      org.opencontainers.image.version="1.0.0" \
      org.opencontainers.image.authors="Saibalaji Namburi" \
      org.opencontainers.image.source="https://github.com/saibalajinamburi/CustomerCore"

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PYTHONPATH=/app \
    APP_ENV=production \
    PORT=8080

# Runtime system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Create non-root user (security best practice)
# Running as root in a container is a major security risk:
# if the container is compromised, root in the container = root on the host
# when using certain Docker configurations.
RUN groupadd -r customercore && useradd -r -g customercore customercore

WORKDIR /app

# Copy installed packages from builder stage
COPY --from=builder /install /usr/local

# Copy application source code
COPY src/ ./src/
COPY pyproject.toml .

# Create directories that the app needs at runtime
RUN mkdir -p logs data/bronze data/silver data/gold \
    && chown -R customercore:customercore /app

# Switch to non-root user
USER customercore

# Health check β€” Docker checks this every 30s to know if the container is healthy
# If it fails 3 times in a row, Docker marks the container as unhealthy
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
    CMD curl -f http://localhost:${PORT}/api/v1/health || exit 1

# Expose the API port
EXPOSE ${PORT}

# Production server command:
# - uvicorn: ASGI server for FastAPI
# - workers: Set to CPU count (2 for HF Spaces, 4+ for Kubernetes)
# - host 0.0.0.0: Listen on all interfaces (required in containers)
# - log-level warning: Reduce noise (our structlog middleware handles request logging)
# - no-access-log: Disabled β€” our middleware logs structured JSON instead
CMD ["uvicorn", "src.api.main:app", \
     "--host", "0.0.0.0", \
     "--port", "8080", \
     "--workers", "2", \
     "--log-level", "warning", \
     "--no-access-log"]