-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
97 lines (79 loc) · 3.34 KB
/
Copy pathDockerfile
File metadata and controls
97 lines (79 loc) · 3.34 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
92
93
94
95
96
97
# Multi-stage build for production - Optimized for React artifacts
FROM public.ecr.aws/docker/library/node:24-alpine AS frontend-builder
WORKDIR /app/frontend
# Stage 1: Install dependencies (cached separately for faster rebuilds)
COPY frontend/package*.json ./
RUN npm ci --omit=dev --legacy-peer-deps
# Stage 2: Copy source and build (changes to source don't invalidate npm layer)
COPY frontend/src ./src
COPY frontend/public ./public
# Build frontend with production optimizations
# CI=true prevents interactive mode, GENERATE_SOURCEMAP=false reduces build time
# COMPRESS_LEVEL=9 enables maximum compression for static assets
ENV NODE_ENV=production
ENV CI=true
ENV GENERATE_SOURCEMAP=false
ENV REACT_APP_PRODUCTION=true
RUN npm run build
# Optional: Verify build output size
RUN du -sh build/ && ls -lah build/static/js/ && ls -lah build/static/css/
# Production stage
FROM public.ecr.aws/docker/library/python:3.14-slim
WORKDIR /app
# Production Python environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONOPTIMIZE=2
# Copy requirements and install Python dependencies with caching (before user creation for better layer reuse)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt && \
pip cache purge
# Create non-root user with minimal privileges
RUN groupadd --gid 1000 -r appgroup && \
useradd -r --uid 1000 --gid appgroup -d /app appuser && \
mkdir -p /app/.aws/sso/cache && \
chown -R appuser:appgroup /app
# Copy application code and entrypoint script
COPY --chown=appuser:appgroup app.py .
COPY --chown=appuser:appgroup entrypoint.sh .
# Make entrypoint executable
RUN chmod +x entrypoint.sh
# Copy built frontend from builder stage (optimized artifacts only)
# Note: build/ contains minified JS/CSS with hashed filenames for long-term caching
COPY --chown=appuser:appgroup --from=frontend-builder /app/frontend/build ./frontend/build
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 5000
# Set Flask-specific production environment variables
ENV FLASK_ENV=production \
FLASK_DEBUG=False
# Health check for orchestration
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/api/health')" || exit 1
# Run with gunicorn for production
# Settings optimized for AWS environment:
# - workers: 4 (suitable for 1-2 CPU, increase for larger instances)
# - worker-class: gthread (concurrent I/O with threads, better than sync for AWS API calls)
# - threads: 2 per worker (4 workers × 2 threads = 8 concurrent requests)
# - preload-app: load Flask app once, share across workers (memory efficient)
# - max-requests: recycle workers to prevent memory creep from long-running AWS calls
# - timeout: 120s (for large subnet API calls)
# - graceful-timeout: 30s (clean shutdown window)
# - keep-alive: 2s (shorter for unpredictable AWS latency)
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["gunicorn", \
"--bind", "0.0.0.0:5000", \
"--workers", "4", \
"--worker-class", "gthread", \
"--threads", "2", \
"--max-requests", "1000", \
"--max-requests-jitter", "100", \
"--preload", \
"--timeout", "120", \
"--graceful-timeout", "30", \
"--keep-alive", "2", \
"--error-logfile", "-", \
"--access-logfile", "-", \
"--log-level", "info", \
"app:app"]