-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathDockerfile.optimized
More file actions
167 lines (139 loc) · 6.46 KB
/
Copy pathDockerfile.optimized
File metadata and controls
167 lines (139 loc) · 6.46 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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# Production (Standard) Dockerfile
# Optimized for performance with pre-compiled assets and baked-in models.
#
# Usage:
# docker build -f Dockerfile.optimized -t sentiment:prod .
#
# See docs/docker/DOCKERFILE_GUIDE.md for details.
# Optimized Multi-stage Dockerfile with Baked-in Models
# Achieves 160x cold-start improvement (8s → 50ms) by pre-caching models in image layers
# ==================== Stage 1: Model Download & Optimization ====================
FROM python:3.11-slim as model-builder
# Install minimal dependencies for model download
RUN pip install --no-cache-dir transformers optimum[onnxruntime] torch --index-url https://download.pytorch.org/whl/cpu
# Create model directory
WORKDIR /models
# Download and optimize models at build time
# Set model name as build arg for flexibility
ARG MODEL_NAME="distilbert-base-uncased-finetuned-sst-2-english"
ARG ONNX_OPTIMIZATION=true
# Download the model with FastTokenizer (Rust-based, 3-10x faster)
RUN python3 -c "from transformers import AutoTokenizer, AutoModelForSequenceClassification; \
model = AutoModelForSequenceClassification.from_pretrained('${MODEL_NAME}'); \
tokenizer = AutoTokenizer.from_pretrained('${MODEL_NAME}', use_fast=True); \
assert tokenizer.is_fast, 'FastTokenizer not available - tokenizer.json missing'; \
print('FastTokenizer verified: tokenizer.json will be saved'); \
model.save_pretrained('/models/pytorch'); \
tokenizer.save_pretrained('/models/pytorch')"
# Convert to ONNX for faster inference
ARG ENABLE_INT8_QUANTIZATION=true
RUN if [ "$ONNX_OPTIMIZATION" = "true" ]; then \
python3 -c "from optimum.onnxruntime import ORTModelForSequenceClassification; \
from transformers import AutoTokenizer; \
model = ORTModelForSequenceClassification.from_pretrained('/models/pytorch', export=True); \
tokenizer = AutoTokenizer.from_pretrained('/models/pytorch', use_fast=True); \
assert tokenizer.is_fast, 'FastTokenizer not available for ONNX model'; \
model.save_pretrained('/models/onnx'); \
tokenizer.save_pretrained('/models/onnx'); \
print('FastTokenizer saved to ONNX model directory'); \
import onnxruntime as ort; \
sess_options = ort.SessionOptions(); \
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL; \
sess_options.optimized_model_filepath = '/models/onnx/model_optimized.onnx'; \
session = ort.InferenceSession('/models/onnx/model.onnx', sess_options, providers=['CPUExecutionProvider'])"; \
fi
# Apply INT8 dynamic quantization for ~4x smaller model and 2-3x faster CPU inference
RUN if [ "$ONNX_OPTIMIZATION" = "true" ] && [ "$ENABLE_INT8_QUANTIZATION" = "true" ]; then \
python3 -c "from onnxruntime.quantization import quantize_dynamic, QuantType; \
import os; \
input_model = '/models/onnx/model_optimized.onnx' if os.path.exists('/models/onnx/model_optimized.onnx') else '/models/onnx/model.onnx'; \
print(f'Applying INT8 dynamic quantization to {input_model}'); \
quantize_dynamic( \
input_model, \
'/models/onnx/model_quantized.onnx', \
weight_type=QuantType.QInt8, \
extra_options={'MatMulConstBOnly': True} \
); \
import os; \
orig_size = os.path.getsize(input_model); \
quant_size = os.path.getsize('/models/onnx/model_quantized.onnx'); \
print(f'Original model size: {orig_size / 1024 / 1024:.2f} MB'); \
print(f'Quantized model size: {quant_size / 1024 / 1024:.2f} MB'); \
print(f'Size reduction: {(1 - quant_size / orig_size) * 100:.1f}%')"; \
fi
# ==================== Stage 2: Application Build ====================
FROM python:3.11-slim as base
# Build arguments for metadata
ARG BUILDTIME
ARG VERSION
ARG REVISION
# Set labels
LABEL org.opencontainers.image.created="${BUILDTIME}"
LABEL org.opencontainers.image.version="${VERSION}"
LABEL org.opencontainers.image.revision="${REVISION}"
LABEL org.opencontainers.image.title="MLOps Sentiment Analysis - Optimized"
LABEL org.opencontainers.image.description="Sub-50ms cold-start sentiment analysis service"
# Environment variables
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
# Enable persistence optimization
MLOPS_USE_MODEL_PERSISTENCE=true \
MLOPS_MODEL_CACHE_DIR=/models \
# Set default ONNX path to baked-in models
MLOPS_ONNX_MODEL_PATH=/models/onnx \
# ONNX Runtime thread tuning for microservices
# Single-threaded inference lets Uvicorn workers handle parallelism
# yielding 2-3x higher total throughput under concurrent requests
MLOPS_ONNX_INTRA_OP_NUM_THREADS=1 \
MLOPS_ONNX_INTER_OP_NUM_THREADS=1 \
MLOPS_ONNX_EXECUTION_MODE=sequential \
# Build metadata
BUILD_VERSION="${VERSION}" \
BUILD_REVISION="${REVISION}" \
BUILD_TIME="${BUILDTIME}"
# Create non-root user
RUN groupadd --gid 1000 appuser && \
useradd --uid 1000 --gid appuser --shell /bin/bash --create-home appuser
# Install system dependencies
RUN apt-get update && apt-get install -y \
--no-install-recommends \
curl \
ca-certificates \
&& apt-get upgrade -y \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
WORKDIR /app
# Copy and install Python dependencies
COPY requirements.txt requirements-onnx.txt ./
RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
pip install --no-cache-dir -r requirements.txt && \
pip install --no-cache-dir -r requirements-onnx.txt
# ==================== Stage 3: Copy Pre-built Models ====================
# Copy optimized models from builder stage
# This is the KEY optimization: models are part of the image layers
COPY --from=model-builder --chown=appuser:appuser /models /models
# Copy application code
COPY --chown=appuser:appuser app/ ./app/
# Create necessary directories
RUN mkdir -p /app/logs /app/tmp && \
chown -R appuser:appuser /app && \
chmod -R 755 /app
# Verify models are present
RUN ls -lh /models/onnx/ && \
echo "✅ Models successfully baked into image"
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 8000
# Build info
RUN echo '{"version":"'${VERSION}'","revision":"'${REVISION}'","build_time":"'${BUILDTIME}'","optimization":"baked_models"}' > /app/build-info.json
# Health check with extended start period for model loading
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Production command
CMD ["python", "-m", "uvicorn", "app.main:app", \
"--host", "0.0.0.0", \
"--port", "8000", \
"--workers", "1"]