Spaces:
Running
Running
ming
commited on
Commit
Β·
2043365
1
Parent(s):
cff42a2
feat: add Docker containerization with Ollama integration
Browse files- Add multi-stage Dockerfile with security best practices
- Add docker-compose.yml for full stack deployment
- Add docker-compose.dev.yml for development with hot reload
- Add nginx.conf for production reverse proxy with rate limiting
- Add .dockerignore for optimized builds
- Add setup-ollama.sh script for model management
- Add run-tests.sh script for Docker-based testing
- Update README with comprehensive Docker deployment guide
- Dockerfile +43 -0
- README.md +40 -6
- docker-compose.dev.yml +38 -0
- docker-compose.yml +67 -0
- nginx.conf +47 -0
- scripts/run-tests.sh +21 -0
- scripts/setup-ollama.sh +54 -0
Dockerfile
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Use Python 3.7 slim image for compatibility
|
| 2 |
+
FROM python:3.7-slim
|
| 3 |
+
|
| 4 |
+
# Set environment variables
|
| 5 |
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
| 6 |
+
PYTHONUNBUFFERED=1 \
|
| 7 |
+
PYTHONPATH=/app
|
| 8 |
+
|
| 9 |
+
# Set work directory
|
| 10 |
+
WORKDIR /app
|
| 11 |
+
|
| 12 |
+
# Install system dependencies
|
| 13 |
+
RUN apt-get update \
|
| 14 |
+
&& apt-get install -y --no-install-recommends \
|
| 15 |
+
curl \
|
| 16 |
+
ca-certificates \
|
| 17 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 18 |
+
|
| 19 |
+
# Copy requirements first for better caching
|
| 20 |
+
COPY requirements.txt .
|
| 21 |
+
|
| 22 |
+
# Install Python dependencies
|
| 23 |
+
RUN pip install --no-cache-dir --upgrade pip \
|
| 24 |
+
&& pip install --no-cache-dir -r requirements.txt
|
| 25 |
+
|
| 26 |
+
# Copy application code
|
| 27 |
+
COPY app/ ./app/
|
| 28 |
+
COPY pytest.ini .
|
| 29 |
+
|
| 30 |
+
# Create non-root user for security
|
| 31 |
+
RUN groupadd -r appuser && useradd -r -g appuser appuser \
|
| 32 |
+
&& chown -R appuser:appuser /app
|
| 33 |
+
USER appuser
|
| 34 |
+
|
| 35 |
+
# Expose port
|
| 36 |
+
EXPOSE 8000
|
| 37 |
+
|
| 38 |
+
# Health check
|
| 39 |
+
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
| 40 |
+
CMD curl -f http://localhost:8000/health || exit 1
|
| 41 |
+
|
| 42 |
+
# Run the application
|
| 43 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
README.md
CHANGED
|
@@ -202,12 +202,15 @@ client.newCall(request).execute().use { response ->
|
|
| 202 |
### Running Tests
|
| 203 |
|
| 204 |
```bash
|
| 205 |
-
# Run all tests
|
| 206 |
pytest
|
| 207 |
|
| 208 |
# Run with coverage
|
| 209 |
pytest --cov=app --cov-report=html --cov-report=term
|
| 210 |
|
|
|
|
|
|
|
|
|
|
| 211 |
# Run specific test file
|
| 212 |
pytest tests/test_api.py -v
|
| 213 |
|
|
@@ -255,15 +258,46 @@ tests/
|
|
| 255 |
|
| 256 |
## Docker Deployment
|
| 257 |
|
| 258 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
|
| 260 |
```bash
|
| 261 |
-
#
|
| 262 |
-
docker-compose up --build
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
|
| 264 |
-
|
|
|
|
| 265 |
docker build -t summarizer-backend .
|
| 266 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
```
|
| 268 |
|
| 269 |
### Production Deployment
|
|
|
|
| 202 |
### Running Tests
|
| 203 |
|
| 204 |
```bash
|
| 205 |
+
# Run all tests locally
|
| 206 |
pytest
|
| 207 |
|
| 208 |
# Run with coverage
|
| 209 |
pytest --cov=app --cov-report=html --cov-report=term
|
| 210 |
|
| 211 |
+
# Run tests in Docker
|
| 212 |
+
./scripts/run-tests.sh
|
| 213 |
+
|
| 214 |
# Run specific test file
|
| 215 |
pytest tests/test_api.py -v
|
| 216 |
|
|
|
|
| 258 |
|
| 259 |
## Docker Deployment
|
| 260 |
|
| 261 |
+
### Quick Start with Docker
|
| 262 |
+
|
| 263 |
+
```bash
|
| 264 |
+
# 1. Start Ollama service
|
| 265 |
+
docker-compose up ollama -d
|
| 266 |
+
|
| 267 |
+
# 2. Download a model (first time only)
|
| 268 |
+
./scripts/setup-ollama.sh llama3.1:8b
|
| 269 |
+
|
| 270 |
+
# 3. Start the API
|
| 271 |
+
docker-compose up api -d
|
| 272 |
+
|
| 273 |
+
# 4. Test the setup
|
| 274 |
+
curl http://localhost:8000/health
|
| 275 |
+
```
|
| 276 |
+
|
| 277 |
+
### Development with Hot Reload
|
| 278 |
|
| 279 |
```bash
|
| 280 |
+
# Use development compose file
|
| 281 |
+
docker-compose -f docker-compose.dev.yml up --build
|
| 282 |
+
```
|
| 283 |
+
|
| 284 |
+
### Production with Nginx
|
| 285 |
+
|
| 286 |
+
```bash
|
| 287 |
+
# Start with Nginx reverse proxy
|
| 288 |
+
docker-compose --profile production up --build
|
| 289 |
+
```
|
| 290 |
+
|
| 291 |
+
### Manual Build
|
| 292 |
|
| 293 |
+
```bash
|
| 294 |
+
# Build the image
|
| 295 |
docker build -t summarizer-backend .
|
| 296 |
+
|
| 297 |
+
# Run with Ollama
|
| 298 |
+
docker run -p 8000:8000 \
|
| 299 |
+
-e OLLAMA_HOST=http://host.docker.internal:11434 \
|
| 300 |
+
summarizer-backend
|
| 301 |
```
|
| 302 |
|
| 303 |
### Production Deployment
|
docker-compose.dev.yml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: '3.8'
|
| 2 |
+
|
| 3 |
+
services:
|
| 4 |
+
# Development version with hot reload
|
| 5 |
+
api-dev:
|
| 6 |
+
build: .
|
| 7 |
+
container_name: summarizer-api-dev
|
| 8 |
+
ports:
|
| 9 |
+
- "8000:8000"
|
| 10 |
+
environment:
|
| 11 |
+
- OLLAMA_HOST=http://ollama:11434
|
| 12 |
+
- OLLAMA_MODEL=llama3.1:8b
|
| 13 |
+
- OLLAMA_TIMEOUT=30
|
| 14 |
+
- SERVER_HOST=0.0.0.0
|
| 15 |
+
- SERVER_PORT=8000
|
| 16 |
+
- LOG_LEVEL=DEBUG
|
| 17 |
+
volumes:
|
| 18 |
+
- ./app:/app/app:ro
|
| 19 |
+
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
| 20 |
+
depends_on:
|
| 21 |
+
- ollama
|
| 22 |
+
restart: unless-stopped
|
| 23 |
+
|
| 24 |
+
# Ollama service (same as production)
|
| 25 |
+
ollama:
|
| 26 |
+
image: ollama/ollama:latest
|
| 27 |
+
container_name: summarizer-ollama-dev
|
| 28 |
+
ports:
|
| 29 |
+
- "11434:11434"
|
| 30 |
+
volumes:
|
| 31 |
+
- ollama_data_dev:/root/.ollama
|
| 32 |
+
environment:
|
| 33 |
+
- OLLAMA_HOST=0.0.0.0
|
| 34 |
+
restart: unless-stopped
|
| 35 |
+
|
| 36 |
+
volumes:
|
| 37 |
+
ollama_data_dev:
|
| 38 |
+
driver: local
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: '3.8'
|
| 2 |
+
|
| 3 |
+
services:
|
| 4 |
+
# Ollama service for local LLM inference
|
| 5 |
+
ollama:
|
| 6 |
+
image: ollama/ollama:latest
|
| 7 |
+
container_name: summarizer-ollama
|
| 8 |
+
ports:
|
| 9 |
+
- "11434:11434"
|
| 10 |
+
volumes:
|
| 11 |
+
- ollama_data:/root/.ollama
|
| 12 |
+
environment:
|
| 13 |
+
- OLLAMA_HOST=0.0.0.0
|
| 14 |
+
restart: unless-stopped
|
| 15 |
+
healthcheck:
|
| 16 |
+
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
|
| 17 |
+
interval: 30s
|
| 18 |
+
timeout: 10s
|
| 19 |
+
retries: 3
|
| 20 |
+
start_period: 40s
|
| 21 |
+
|
| 22 |
+
# FastAPI backend service
|
| 23 |
+
api:
|
| 24 |
+
build: .
|
| 25 |
+
container_name: summarizer-api
|
| 26 |
+
ports:
|
| 27 |
+
- "8000:8000"
|
| 28 |
+
environment:
|
| 29 |
+
- OLLAMA_HOST=http://ollama:11434
|
| 30 |
+
- OLLAMA_MODEL=llama3.1:8b
|
| 31 |
+
- OLLAMA_TIMEOUT=30
|
| 32 |
+
- SERVER_HOST=0.0.0.0
|
| 33 |
+
- SERVER_PORT=8000
|
| 34 |
+
- LOG_LEVEL=INFO
|
| 35 |
+
depends_on:
|
| 36 |
+
ollama:
|
| 37 |
+
condition: service_healthy
|
| 38 |
+
restart: unless-stopped
|
| 39 |
+
healthcheck:
|
| 40 |
+
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
| 41 |
+
interval: 30s
|
| 42 |
+
timeout: 10s
|
| 43 |
+
retries: 3
|
| 44 |
+
start_period: 10s
|
| 45 |
+
|
| 46 |
+
# Optional: Nginx reverse proxy for production
|
| 47 |
+
nginx:
|
| 48 |
+
image: nginx:alpine
|
| 49 |
+
container_name: summarizer-nginx
|
| 50 |
+
ports:
|
| 51 |
+
- "80:80"
|
| 52 |
+
- "443:443"
|
| 53 |
+
volumes:
|
| 54 |
+
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
| 55 |
+
depends_on:
|
| 56 |
+
- api
|
| 57 |
+
restart: unless-stopped
|
| 58 |
+
profiles:
|
| 59 |
+
- production
|
| 60 |
+
|
| 61 |
+
volumes:
|
| 62 |
+
ollama_data:
|
| 63 |
+
driver: local
|
| 64 |
+
|
| 65 |
+
networks:
|
| 66 |
+
default:
|
| 67 |
+
name: summarizer-network
|
nginx.conf
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
events {
|
| 2 |
+
worker_connections 1024;
|
| 3 |
+
}
|
| 4 |
+
|
| 5 |
+
http {
|
| 6 |
+
upstream api {
|
| 7 |
+
server api:8000;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
# Rate limiting
|
| 11 |
+
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
|
| 12 |
+
|
| 13 |
+
server {
|
| 14 |
+
listen 80;
|
| 15 |
+
server_name localhost;
|
| 16 |
+
|
| 17 |
+
# Security headers
|
| 18 |
+
add_header X-Frame-Options DENY;
|
| 19 |
+
add_header X-Content-Type-Options nosniff;
|
| 20 |
+
add_header X-XSS-Protection "1; mode=block";
|
| 21 |
+
|
| 22 |
+
# API routes
|
| 23 |
+
location / {
|
| 24 |
+
limit_req zone=api burst=20 nodelay;
|
| 25 |
+
|
| 26 |
+
proxy_pass http://api;
|
| 27 |
+
proxy_set_header Host $host;
|
| 28 |
+
proxy_set_header X-Real-IP $remote_addr;
|
| 29 |
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
| 30 |
+
proxy_set_header X-Forwarded-Proto $scheme;
|
| 31 |
+
|
| 32 |
+
# Timeouts
|
| 33 |
+
proxy_connect_timeout 30s;
|
| 34 |
+
proxy_send_timeout 30s;
|
| 35 |
+
proxy_read_timeout 30s;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
# Health check endpoint (no rate limiting)
|
| 39 |
+
location /health {
|
| 40 |
+
proxy_pass http://api;
|
| 41 |
+
proxy_set_header Host $host;
|
| 42 |
+
proxy_set_header X-Real-IP $remote_addr;
|
| 43 |
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
| 44 |
+
proxy_set_header X-Forwarded-Proto $scheme;
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
}
|
scripts/run-tests.sh
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# Test runner script for Docker environment
|
| 4 |
+
set -e
|
| 5 |
+
|
| 6 |
+
echo "π§ͺ Running tests in Docker environment..."
|
| 7 |
+
|
| 8 |
+
# Build test image
|
| 9 |
+
echo "π¦ Building test image..."
|
| 10 |
+
docker build -t summarizer-backend-test .
|
| 11 |
+
|
| 12 |
+
# Run tests
|
| 13 |
+
echo "π Running tests..."
|
| 14 |
+
docker run --rm \
|
| 15 |
+
-v "$(pwd)/tests:/app/tests:ro" \
|
| 16 |
+
-v "$(pwd)/app:/app/app:ro" \
|
| 17 |
+
-v "$(pwd)/pytest.ini:/app/pytest.ini:ro" \
|
| 18 |
+
summarizer-backend-test \
|
| 19 |
+
pytest tests/ -v --cov=app --cov-report=term-missing
|
| 20 |
+
|
| 21 |
+
echo "β
Tests completed!"
|
scripts/setup-ollama.sh
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# Setup script for Ollama model download
|
| 4 |
+
set -e
|
| 5 |
+
|
| 6 |
+
echo "π Setting up Ollama for Text Summarizer Backend..."
|
| 7 |
+
|
| 8 |
+
# Check if Ollama is running
|
| 9 |
+
if ! curl -s http://localhost:11434/api/tags > /dev/null; then
|
| 10 |
+
echo "β Ollama is not running. Please start Ollama first:"
|
| 11 |
+
echo " docker-compose up ollama -d"
|
| 12 |
+
echo " or"
|
| 13 |
+
echo " ollama serve"
|
| 14 |
+
exit 1
|
| 15 |
+
fi
|
| 16 |
+
|
| 17 |
+
# Default model
|
| 18 |
+
MODEL=${1:-"llama3.1:8b"}
|
| 19 |
+
|
| 20 |
+
echo "π₯ Downloading model: $MODEL"
|
| 21 |
+
echo "This may take several minutes depending on your internet connection..."
|
| 22 |
+
|
| 23 |
+
# Pull the model
|
| 24 |
+
ollama pull "$MODEL"
|
| 25 |
+
|
| 26 |
+
echo "β
Model $MODEL downloaded successfully!"
|
| 27 |
+
|
| 28 |
+
# Test the model
|
| 29 |
+
echo "π§ͺ Testing model..."
|
| 30 |
+
TEST_RESPONSE=$(curl -s -X POST http://localhost:11434/api/generate \
|
| 31 |
+
-H "Content-Type: application/json" \
|
| 32 |
+
-d '{
|
| 33 |
+
"model": "'"$MODEL"'",
|
| 34 |
+
"prompt": "Summarize this: Hello world",
|
| 35 |
+
"stream": false
|
| 36 |
+
}')
|
| 37 |
+
|
| 38 |
+
if echo "$TEST_RESPONSE" | grep -q "response"; then
|
| 39 |
+
echo "β
Model test successful!"
|
| 40 |
+
else
|
| 41 |
+
echo "β Model test failed. Response:"
|
| 42 |
+
echo "$TEST_RESPONSE"
|
| 43 |
+
exit 1
|
| 44 |
+
fi
|
| 45 |
+
|
| 46 |
+
echo ""
|
| 47 |
+
echo "π Setup complete! Your text summarizer backend is ready to use."
|
| 48 |
+
echo ""
|
| 49 |
+
echo "Next steps:"
|
| 50 |
+
echo "1. Start the API: docker-compose up api -d"
|
| 51 |
+
echo "2. Test the API: curl http://localhost:8000/health"
|
| 52 |
+
echo "3. Try summarization: curl -X POST http://localhost:8000/api/v1/summarize/ \\"
|
| 53 |
+
echo " -H 'Content-Type: application/json' \\"
|
| 54 |
+
echo " -d '{\"text\": \"Your text to summarize here...\"}'"
|