-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
91 lines (74 loc) · 3.56 KB
/
Copy pathDockerfile
File metadata and controls
91 lines (74 loc) · 3.56 KB
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
# ── Stage 1: dependency builder ───────────────────────────────────────────────
# Installs all Python packages into a prefix so Stage 2 can copy them cleanly.
# Separating build-time tools (pip, wheel, gcc) from the runtime image is the
# primary lever for keeping the final image under 500 MB.
FROM python:3.11-slim AS builder
WORKDIR /build
# Install build tools needed by native extensions (numpy, tiktoken …)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
g++ \
libgomp1 \
&& rm -rf /var/lib/apt/lists/*
# Copy only the packaging manifests first — Docker layer cache means a code
# change won't invalidate the (slow) pip install layer.
COPY pyproject.toml ./
COPY market_narrator/__init__.py ./market_narrator/__init__.py
# Install runtime dependencies into /install (no dev extras, no cache)
RUN pip install --no-cache-dir --prefix=/install .
# Pre-download fastembed models so the runtime image doesn't need internet
# access (and doesn't hit HuggingFace rate limits on every container start).
# FASTEMBED_CACHE_PATH is set explicitly so we know the exact copy source.
# The retry loop (up to 5 × 60 s) handles transient HuggingFace 429s.
# The post-download assertion makes the build fail loudly if no files landed
# (fastembed can swallow 429 errors and still exit 0).
RUN for i in 1 2 3 4 5; do \
FASTEMBED_CACHE_PATH=/fastembed_cache \
PYTHONPATH=/install/lib/python3.11/site-packages \
python -c "\
import os, sys; \
from fastembed import TextEmbedding, SparseTextEmbedding; \
TextEmbedding('sentence-transformers/all-MiniLM-L6-v2'); \
SparseTextEmbedding('Qdrant/bm25'); \
files = [f for _, _, fs in os.walk('/fastembed_cache') for f in fs]; \
print('cached files:', files[:6]); \
sys.exit(0 if files else 1)" \
&& break; \
echo "fastembed download attempt $i failed, retrying in 60 s..."; sleep 60; \
done
# ── Stage 2: runtime image ────────────────────────────────────────────────────
FROM python:3.11-slim AS runtime
# libgomp is required at runtime by fastembed / numpy BLAS
RUN apt-get update && apt-get install -y --no-install-recommends \
libgomp1 \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Bring in installed packages and pre-downloaded fastembed models from the builder stage
COPY --from=builder /install /usr/local
COPY --from=builder /fastembed_cache /home/mnuser/.cache/fastembed
# Application source
COPY market_narrator/ ./market_narrator/
COPY config/ ./config/
# Non-root user for security
RUN useradd --no-create-home --shell /bin/false mnuser \
&& mkdir -p /home/mnuser/.market_narrator \
&& chown -R mnuser:mnuser /app /home/mnuser
USER mnuser
# Cloud Run injects PORT=8080 by default; Streamlit must bind to 0.0.0.0.
ENV PORT=8080 \
STREAMLIT_SERVER_FILE_WATCHER_TYPE=none \
STREAMLIT_BROWSER_GATHER_USAGE_STATS=false \
STREAMLIT_SERVER_HEADLESS=true \
PYTHONUNBUFFERED=1 \
HOME=/home/mnuser \
FASTEMBED_CACHE_PATH=/home/mnuser/.cache/fastembed
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
CMD curl -f http://localhost:8080/_stcore/health || exit 1
EXPOSE 8080
CMD ["python", "-m", "streamlit", "run", \
"market_narrator/dashboard/app.py", \
"--server.port=8080", \
"--server.address=0.0.0.0", \
"--server.enableCORS=false", \
"--server.enableXsrfProtection=false"]