feat: Add complete production deployment infrastructure
- Docker configuration: - Multi-stage Dockerfiles for backend (Python 3.11) and frontend (Node 20) - Production docker-compose.yml with all services - Development docker-compose.dev.yml with hot-reload - Nginx reverse proxy: - SSL/TLS termination with modern cipher suites - Rate limiting and security headers - Caching and compression - Load balancing ready - Kubernetes manifests: - Deployment, Service, Ingress configurations - ConfigMap and Secrets - HPA for auto-scaling - PersistentVolumeClaims - Deployment scripts: - deploy.sh: Automated deployment with health checks - backup.sh: Automated backup with retention - health-check.sh: Service health monitoring - setup-ssl.sh: Let's Encrypt SSL automation - Monitoring: - Prometheus configuration - Grafana dashboards (optional) - Structured logging - Documentation: - DEPLOYMENT_GUIDE.md: Complete deployment instructions - Environment templates (.env.production) Ready for commercial deployment!
This commit is contained in:
parent
8f9ca669cf
commit
29178a75a5
75
.env.production
Normal file
75
.env.production
Normal file
@ -0,0 +1,75 @@
|
||||
# ============================================
|
||||
# Document Translation API - Production Environment
|
||||
# ============================================
|
||||
# IMPORTANT: Review and update all values before deployment
|
||||
|
||||
# ===========================================
|
||||
# Application Settings
|
||||
# ===========================================
|
||||
APP_NAME=Document Translation API
|
||||
APP_ENV=production
|
||||
DEBUG=false
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
# ===========================================
|
||||
# Server Configuration
|
||||
# ===========================================
|
||||
HTTP_PORT=80
|
||||
HTTPS_PORT=443
|
||||
BACKEND_PORT=8000
|
||||
FRONTEND_PORT=3000
|
||||
|
||||
# ===========================================
|
||||
# Domain Configuration
|
||||
# ===========================================
|
||||
DOMAIN=translate.yourdomain.com
|
||||
NEXT_PUBLIC_API_URL=https://translate.yourdomain.com
|
||||
|
||||
# ===========================================
|
||||
# Translation Service Configuration
|
||||
# ===========================================
|
||||
TRANSLATION_SERVICE=ollama
|
||||
OLLAMA_BASE_URL=http://ollama:11434
|
||||
OLLAMA_MODEL=llama3
|
||||
|
||||
# DeepL API (optional)
|
||||
DEEPL_API_KEY=
|
||||
|
||||
# OpenAI API (optional)
|
||||
OPENAI_API_KEY=
|
||||
OPENAI_MODEL=gpt-4o-mini
|
||||
|
||||
# ===========================================
|
||||
# File Upload Settings
|
||||
# ===========================================
|
||||
MAX_FILE_SIZE_MB=50
|
||||
ALLOWED_EXTENSIONS=.docx,.xlsx,.pptx
|
||||
|
||||
# ===========================================
|
||||
# Rate Limiting
|
||||
# ===========================================
|
||||
RATE_LIMIT_ENABLED=true
|
||||
RATE_LIMIT_REQUESTS_PER_MINUTE=60
|
||||
RATE_LIMIT_TRANSLATIONS_PER_MINUTE=10
|
||||
RATE_LIMIT_TRANSLATIONS_PER_HOUR=100
|
||||
RATE_LIMIT_TRANSLATIONS_PER_DAY=500
|
||||
|
||||
# ===========================================
|
||||
# Security (CHANGE THESE!)
|
||||
# ===========================================
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=CHANGE_THIS_SECURE_PASSWORD
|
||||
|
||||
# CORS Configuration
|
||||
CORS_ORIGINS=https://translate.yourdomain.com
|
||||
|
||||
# ===========================================
|
||||
# Monitoring (Optional)
|
||||
# ===========================================
|
||||
GRAFANA_USER=admin
|
||||
GRAFANA_PASSWORD=CHANGE_THIS_TOO
|
||||
|
||||
# ===========================================
|
||||
# SSL Configuration
|
||||
# ===========================================
|
||||
LETSENCRYPT_EMAIL=admin@yourdomain.com
|
||||
455
DEPLOYMENT_GUIDE.md
Normal file
455
DEPLOYMENT_GUIDE.md
Normal file
@ -0,0 +1,455 @@
|
||||
# 🚀 Document Translation API - Production Deployment Guide
|
||||
|
||||
Complete guide for deploying the Document Translation API in production environments.
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
1. [Quick Start](#quick-start)
|
||||
2. [Architecture Overview](#architecture-overview)
|
||||
3. [Docker Deployment](#docker-deployment)
|
||||
4. [Kubernetes Deployment](#kubernetes-deployment)
|
||||
5. [SSL/TLS Configuration](#ssltls-configuration)
|
||||
6. [Environment Configuration](#environment-configuration)
|
||||
7. [Monitoring & Logging](#monitoring--logging)
|
||||
8. [Scaling & Performance](#scaling--performance)
|
||||
9. [Backup & Recovery](#backup--recovery)
|
||||
10. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Docker & Docker Compose v2.0+
|
||||
- Domain name (for production)
|
||||
- SSL certificate (or use Let's Encrypt)
|
||||
|
||||
### One-Command Deployment
|
||||
|
||||
```bash
|
||||
# Clone and deploy
|
||||
git clone https://github.com/your-repo/translate-api.git
|
||||
cd translate-api
|
||||
git checkout production-deployment
|
||||
|
||||
# Configure environment
|
||||
cp .env.example .env.production
|
||||
# Edit .env.production with your settings
|
||||
|
||||
# Deploy!
|
||||
./scripts/deploy.sh production
|
||||
```
|
||||
|
||||
Your application will be available at `https://your-domain.com`
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Internet │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ Nginx │
|
||||
│ (Reverse Proxy)│
|
||||
│ - SSL/TLS │
|
||||
│ - Rate Limit │
|
||||
│ - Caching │
|
||||
└────────┬────────┘
|
||||
┌──────────────┼──────────────┐
|
||||
│ │ │
|
||||
┌────────▼────┐ ┌──────▼─────┐ ┌─────▼─────┐
|
||||
│ Frontend │ │ Backend │ │ Admin │
|
||||
│ (Next.js) │ │ (FastAPI) │ │ Dashboard│
|
||||
│ Port 3000 │ │ Port 8000 │ │ │
|
||||
└─────────────┘ └──────┬─────┘ └───────────┘
|
||||
│
|
||||
┌────────────────────────┼────────────────────────┐
|
||||
│ │ │
|
||||
┌────────▼────────┐ ┌──────────▼──────────┐ ┌───────▼───────┐
|
||||
│ Ollama │ │ Google/DeepL/ │ │ Redis │
|
||||
│ (Local LLM) │ │ OpenAI APIs │ │ (Cache) │
|
||||
└─────────────────┘ └─────────────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐳 Docker Deployment
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
translate-api/
|
||||
├── docker/
|
||||
│ ├── backend/
|
||||
│ │ └── Dockerfile
|
||||
│ ├── frontend/
|
||||
│ │ └── Dockerfile
|
||||
│ ├── nginx/
|
||||
│ │ ├── nginx.conf
|
||||
│ │ ├── conf.d/
|
||||
│ │ │ └── default.conf
|
||||
│ │ └── ssl/
|
||||
│ │ ├── fullchain.pem
|
||||
│ │ └── privkey.pem
|
||||
│ └── prometheus/
|
||||
│ └── prometheus.yml
|
||||
├── scripts/
|
||||
│ ├── deploy.sh
|
||||
│ ├── backup.sh
|
||||
│ ├── health-check.sh
|
||||
│ └── setup-ssl.sh
|
||||
├── docker-compose.yml
|
||||
├── docker-compose.dev.yml
|
||||
├── .env.production
|
||||
└── .env.example
|
||||
```
|
||||
|
||||
### Basic Deployment
|
||||
|
||||
```bash
|
||||
# Production (with nginx, SSL)
|
||||
docker compose --env-file .env.production up -d
|
||||
|
||||
# View logs
|
||||
docker compose logs -f
|
||||
|
||||
# Check status
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### With Optional Services
|
||||
|
||||
```bash
|
||||
# With local Ollama LLM
|
||||
docker compose --profile with-ollama up -d
|
||||
|
||||
# With Redis caching
|
||||
docker compose --profile with-cache up -d
|
||||
|
||||
# With monitoring (Prometheus + Grafana)
|
||||
docker compose --profile with-monitoring up -d
|
||||
|
||||
# All services
|
||||
docker compose --profile with-ollama --profile with-cache --profile with-monitoring up -d
|
||||
```
|
||||
|
||||
### Service Endpoints
|
||||
|
||||
| Service | Internal Port | External Access |
|
||||
|---------|--------------|-----------------|
|
||||
| Frontend | 3000 | https://domain.com/ |
|
||||
| Backend API | 8000 | https://domain.com/api/ |
|
||||
| Admin Dashboard | 8000 | https://domain.com/admin |
|
||||
| Health Check | 8000 | https://domain.com/health |
|
||||
| Prometheus | 9090 | Internal only |
|
||||
| Grafana | 3001 | https://domain.com:3001 |
|
||||
|
||||
---
|
||||
|
||||
## ☸️ Kubernetes Deployment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Kubernetes cluster (1.25+)
|
||||
- kubectl configured
|
||||
- Ingress controller (nginx-ingress)
|
||||
- cert-manager (for SSL)
|
||||
|
||||
### Deploy to Kubernetes
|
||||
|
||||
```bash
|
||||
# Create namespace and deploy
|
||||
kubectl apply -f k8s/deployment.yaml
|
||||
|
||||
# Check status
|
||||
kubectl get pods -n translate-api
|
||||
kubectl get services -n translate-api
|
||||
kubectl get ingress -n translate-api
|
||||
|
||||
# View logs
|
||||
kubectl logs -f deployment/backend -n translate-api
|
||||
```
|
||||
|
||||
### Scaling
|
||||
|
||||
```bash
|
||||
# Manual scaling
|
||||
kubectl scale deployment/backend --replicas=5 -n translate-api
|
||||
|
||||
# Auto-scaling is configured via HPA
|
||||
kubectl get hpa -n translate-api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 SSL/TLS Configuration
|
||||
|
||||
### Option 1: Let's Encrypt (Recommended)
|
||||
|
||||
```bash
|
||||
# Automated setup
|
||||
./scripts/setup-ssl.sh translate.yourdomain.com admin@yourdomain.com
|
||||
```
|
||||
|
||||
### Option 2: Custom Certificate
|
||||
|
||||
```bash
|
||||
# Place your certificates in:
|
||||
docker/nginx/ssl/fullchain.pem # Full certificate chain
|
||||
docker/nginx/ssl/privkey.pem # Private key
|
||||
docker/nginx/ssl/chain.pem # CA chain (optional)
|
||||
```
|
||||
|
||||
### Option 3: Self-Signed (Development Only)
|
||||
|
||||
```bash
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout docker/nginx/ssl/privkey.pem \
|
||||
-out docker/nginx/ssl/fullchain.pem \
|
||||
-subj "/CN=localhost"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Environment Configuration
|
||||
|
||||
### Required Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `DOMAIN` | Your domain name | - |
|
||||
| `ADMIN_USERNAME` | Admin login | admin |
|
||||
| `ADMIN_PASSWORD` | Admin password | changeme123 |
|
||||
| `TRANSLATION_SERVICE` | Default provider | ollama |
|
||||
|
||||
### Translation Providers
|
||||
|
||||
```bash
|
||||
# Ollama (Local LLM)
|
||||
TRANSLATION_SERVICE=ollama
|
||||
OLLAMA_BASE_URL=http://ollama:11434
|
||||
OLLAMA_MODEL=llama3
|
||||
|
||||
# Google Translate (Free)
|
||||
TRANSLATION_SERVICE=google
|
||||
|
||||
# DeepL (Requires API key)
|
||||
TRANSLATION_SERVICE=deepl
|
||||
DEEPL_API_KEY=your-api-key
|
||||
|
||||
# OpenAI (Requires API key)
|
||||
TRANSLATION_SERVICE=openai
|
||||
OPENAI_API_KEY=your-api-key
|
||||
OPENAI_MODEL=gpt-4o-mini
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```bash
|
||||
RATE_LIMIT_REQUESTS_PER_MINUTE=60
|
||||
RATE_LIMIT_TRANSLATIONS_PER_MINUTE=10
|
||||
RATE_LIMIT_TRANSLATIONS_PER_HOUR=100
|
||||
RATE_LIMIT_TRANSLATIONS_PER_DAY=500
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Monitoring & Logging
|
||||
|
||||
### Enable Monitoring
|
||||
|
||||
```bash
|
||||
docker compose --profile with-monitoring up -d
|
||||
```
|
||||
|
||||
### Access Dashboards
|
||||
|
||||
- **Prometheus**: http://localhost:9090
|
||||
- **Grafana**: http://localhost:3001 (admin/admin)
|
||||
|
||||
### Log Locations
|
||||
|
||||
```bash
|
||||
# Docker logs
|
||||
docker compose logs backend
|
||||
docker compose logs frontend
|
||||
docker compose logs nginx
|
||||
|
||||
# Application logs (inside container)
|
||||
docker exec translate-backend cat /app/logs/app.log
|
||||
```
|
||||
|
||||
### Health Checks
|
||||
|
||||
```bash
|
||||
# Manual check
|
||||
./scripts/health-check.sh --verbose
|
||||
|
||||
# API health endpoint
|
||||
curl https://your-domain.com/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Scaling & Performance
|
||||
|
||||
### Horizontal Scaling
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
backend:
|
||||
deploy:
|
||||
replicas: 4
|
||||
```
|
||||
|
||||
### Performance Tuning
|
||||
|
||||
```bash
|
||||
# Backend workers (in Dockerfile CMD)
|
||||
CMD ["uvicorn", "main:app", "--workers", "4"]
|
||||
|
||||
# Nginx connections
|
||||
worker_connections 2048;
|
||||
|
||||
# File upload limits
|
||||
client_max_body_size 100M;
|
||||
```
|
||||
|
||||
### Resource Limits
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2G
|
||||
cpus: '1'
|
||||
reservations:
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💾 Backup & Recovery
|
||||
|
||||
### Automated Backup
|
||||
|
||||
```bash
|
||||
# Run backup
|
||||
./scripts/backup.sh /path/to/backups
|
||||
|
||||
# Add to crontab (daily at 2 AM)
|
||||
0 2 * * * /path/to/scripts/backup.sh /backups
|
||||
```
|
||||
|
||||
### Restore from Backup
|
||||
|
||||
```bash
|
||||
# Extract backup
|
||||
tar xzf translate_backup_20241130.tar.gz
|
||||
|
||||
# Restore files
|
||||
docker cp translate_backup/uploads translate-backend:/app/
|
||||
docker cp translate_backup/outputs translate-backend:/app/
|
||||
|
||||
# Restart services
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Port Already in Use
|
||||
```bash
|
||||
# Find process
|
||||
netstat -tulpn | grep :8000
|
||||
# Kill process
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
#### Container Won't Start
|
||||
```bash
|
||||
# Check logs
|
||||
docker compose logs backend --tail 100
|
||||
|
||||
# Check resources
|
||||
docker stats
|
||||
```
|
||||
|
||||
#### SSL Certificate Issues
|
||||
```bash
|
||||
# Test certificate
|
||||
openssl s_client -connect your-domain.com:443
|
||||
|
||||
# Renew Let's Encrypt
|
||||
./scripts/renew-ssl.sh
|
||||
```
|
||||
|
||||
#### Memory Issues
|
||||
```bash
|
||||
# Increase limits in docker-compose.yml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 4G
|
||||
```
|
||||
|
||||
### Getting Help
|
||||
|
||||
1. Check logs: `docker compose logs -f`
|
||||
2. Run health check: `./scripts/health-check.sh`
|
||||
3. Review configuration: `.env.production`
|
||||
4. Check container status: `docker compose ps`
|
||||
|
||||
---
|
||||
|
||||
## 📝 Maintenance Commands
|
||||
|
||||
```bash
|
||||
# Update application
|
||||
git pull origin production-deployment
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
|
||||
# Prune unused resources
|
||||
docker system prune -a
|
||||
|
||||
# View resource usage
|
||||
docker stats
|
||||
|
||||
# Enter container shell
|
||||
docker exec -it translate-backend /bin/bash
|
||||
|
||||
# Database backup (if using)
|
||||
docker exec translate-db pg_dump -U user database > backup.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Security Checklist
|
||||
|
||||
- [ ] Change default admin password
|
||||
- [ ] Configure SSL/TLS
|
||||
- [ ] Set up firewall rules
|
||||
- [ ] Enable rate limiting
|
||||
- [ ] Configure CORS properly
|
||||
- [ ] Regular security updates
|
||||
- [ ] Backup encryption
|
||||
- [ ] Monitor access logs
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support
|
||||
|
||||
For issues and feature requests, please open a GitHub issue or contact support.
|
||||
|
||||
**Version**: 2.0.0
|
||||
**Last Updated**: November 2025
|
||||
40
docker-compose.dev.yml
Normal file
40
docker-compose.dev.yml
Normal file
@ -0,0 +1,40 @@
|
||||
# Document Translation API - Development Docker Compose
|
||||
# Usage: docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/backend/Dockerfile
|
||||
target: builder # Use builder stage for dev
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/venv # Don't override venv
|
||||
environment:
|
||||
- DEBUG=true
|
||||
- LOG_LEVEL=DEBUG
|
||||
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
||||
ports:
|
||||
- "8000:8000"
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/frontend/Dockerfile
|
||||
target: builder
|
||||
volumes:
|
||||
- ./frontend:/app
|
||||
- /app/node_modules
|
||||
- /app/.next
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
command: npm run dev
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
||||
# No nginx in dev - direct access to services
|
||||
nginx:
|
||||
profiles:
|
||||
- disabled
|
||||
208
docker-compose.yml
Normal file
208
docker-compose.yml
Normal file
@ -0,0 +1,208 @@
|
||||
# Document Translation API - Production Docker Compose
|
||||
# Usage: docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# ===========================================
|
||||
# Backend API Service
|
||||
# ===========================================
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/backend/Dockerfile
|
||||
container_name: translate-backend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TRANSLATION_SERVICE=${TRANSLATION_SERVICE:-ollama}
|
||||
- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://ollama:11434}
|
||||
- OLLAMA_MODEL=${OLLAMA_MODEL:-llama3}
|
||||
- DEEPL_API_KEY=${DEEPL_API_KEY:-}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||
- MAX_FILE_SIZE_MB=${MAX_FILE_SIZE_MB:-50}
|
||||
- RATE_LIMIT_REQUESTS_PER_MINUTE=${RATE_LIMIT_REQUESTS_PER_MINUTE:-60}
|
||||
- RATE_LIMIT_TRANSLATIONS_PER_MINUTE=${RATE_LIMIT_TRANSLATIONS_PER_MINUTE:-10}
|
||||
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-changeme123}
|
||||
- CORS_ORIGINS=${CORS_ORIGINS:-*}
|
||||
volumes:
|
||||
- uploads_data:/app/uploads
|
||||
- outputs_data:/app/outputs
|
||||
- logs_data:/app/logs
|
||||
networks:
|
||||
- translate-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2G
|
||||
reservations:
|
||||
memory: 512M
|
||||
|
||||
# ===========================================
|
||||
# Frontend Web Service
|
||||
# ===========================================
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/frontend/Dockerfile
|
||||
args:
|
||||
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://backend:8000}
|
||||
container_name: translate-frontend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://backend:8000}
|
||||
networks:
|
||||
- translate-network
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
reservations:
|
||||
memory: 128M
|
||||
|
||||
# ===========================================
|
||||
# Nginx Reverse Proxy
|
||||
# ===========================================
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: translate-nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${HTTP_PORT:-80}:80"
|
||||
- "${HTTPS_PORT:-443}:443"
|
||||
volumes:
|
||||
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
- ./docker/nginx/ssl:/etc/nginx/ssl:ro
|
||||
- nginx_cache:/var/cache/nginx
|
||||
networks:
|
||||
- translate-network
|
||||
depends_on:
|
||||
- frontend
|
||||
- backend
|
||||
healthcheck:
|
||||
test: ["CMD", "nginx", "-t"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
# ===========================================
|
||||
# Ollama (Optional - Local LLM)
|
||||
# ===========================================
|
||||
ollama:
|
||||
image: ollama/ollama:latest
|
||||
container_name: translate-ollama
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ollama_data:/root/.ollama
|
||||
networks:
|
||||
- translate-network
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: all
|
||||
capabilities: [gpu]
|
||||
profiles:
|
||||
- with-ollama
|
||||
|
||||
# ===========================================
|
||||
# Redis (Optional - For caching & sessions)
|
||||
# ===========================================
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: translate-redis
|
||||
restart: unless-stopped
|
||||
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- translate-network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
profiles:
|
||||
- with-cache
|
||||
|
||||
# ===========================================
|
||||
# Prometheus (Optional - Monitoring)
|
||||
# ===========================================
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
container_name: translate-prometheus
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||
- prometheus_data:/prometheus
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--storage.tsdb.path=/prometheus'
|
||||
- '--web.enable-lifecycle'
|
||||
networks:
|
||||
- translate-network
|
||||
profiles:
|
||||
- with-monitoring
|
||||
|
||||
# ===========================================
|
||||
# Grafana (Optional - Dashboards)
|
||||
# ===========================================
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
container_name: translate-grafana
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_USER=${GRAFANA_USER:-admin}
|
||||
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
|
||||
- GF_USERS_ALLOW_SIGN_UP=false
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./docker/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
|
||||
networks:
|
||||
- translate-network
|
||||
depends_on:
|
||||
- prometheus
|
||||
profiles:
|
||||
- with-monitoring
|
||||
|
||||
# ===========================================
|
||||
# Networks
|
||||
# ===========================================
|
||||
networks:
|
||||
translate-network:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.28.0.0/16
|
||||
|
||||
# ===========================================
|
||||
# Volumes
|
||||
# ===========================================
|
||||
volumes:
|
||||
uploads_data:
|
||||
driver: local
|
||||
outputs_data:
|
||||
driver: local
|
||||
logs_data:
|
||||
driver: local
|
||||
nginx_cache:
|
||||
driver: local
|
||||
ollama_data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
prometheus_data:
|
||||
driver: local
|
||||
grafana_data:
|
||||
driver: local
|
||||
65
docker/backend/Dockerfile
Normal file
65
docker/backend/Dockerfile
Normal file
@ -0,0 +1,65 @@
|
||||
# Document Translation API - Backend Dockerfile
|
||||
# Multi-stage build for optimized production image
|
||||
|
||||
FROM python:3.11-slim as builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install build dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
libmagic1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy requirements first for better caching
|
||||
COPY requirements.txt .
|
||||
|
||||
# Create virtual environment and install dependencies
|
||||
RUN python -m venv /opt/venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Production stage
|
||||
FROM python:3.11-slim as production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install runtime dependencies only
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libmagic1 \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get clean
|
||||
|
||||
# Copy virtual environment from builder
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# Create non-root user for security
|
||||
RUN groupadd -r translator && useradd -r -g translator translator
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /app/uploads /app/outputs /app/logs /app/temp \
|
||||
&& chown -R translator:translator /app
|
||||
|
||||
# Copy application code
|
||||
COPY --chown=translator:translator . .
|
||||
|
||||
# Switch to non-root user
|
||||
USER translator
|
||||
|
||||
# Environment variables
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
PORT=8000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:${PORT}/health || exit 1
|
||||
|
||||
# Expose port
|
||||
EXPOSE ${PORT}
|
||||
|
||||
# Run with uvicorn
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
|
||||
51
docker/frontend/Dockerfile
Normal file
51
docker/frontend/Dockerfile
Normal file
@ -0,0 +1,51 @@
|
||||
# Document Translation Frontend - Dockerfile
|
||||
# Multi-stage build for optimized production
|
||||
|
||||
# Build stage
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY frontend/package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci --only=production=false
|
||||
|
||||
# Copy source code
|
||||
COPY frontend/ .
|
||||
|
||||
# Build arguments for environment
|
||||
ARG NEXT_PUBLIC_API_URL=http://localhost:8000
|
||||
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM node:20-alpine AS production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nextjs -u 1001
|
||||
|
||||
# Copy built assets from builder
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
|
||||
# Set correct permissions
|
||||
RUN chown -R nextjs:nodejs /app
|
||||
|
||||
USER nextjs
|
||||
|
||||
# Environment
|
||||
ENV NODE_ENV=production \
|
||||
PORT=3000 \
|
||||
HOSTNAME="0.0.0.0"
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
150
docker/nginx/conf.d/default.conf
Normal file
150
docker/nginx/conf.d/default.conf
Normal file
@ -0,0 +1,150 @@
|
||||
# Document Translation API - Main Server Block
|
||||
# HTTP to HTTPS redirect and main application routing
|
||||
|
||||
# HTTP server - redirect to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name _;
|
||||
|
||||
# Allow health checks on HTTP
|
||||
location /health {
|
||||
proxy_pass http://backend/health;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
}
|
||||
|
||||
# ACME challenge for Let's Encrypt
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# Redirect all other traffic to HTTPS
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS server - main application
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name _;
|
||||
|
||||
# SSL certificates (replace with your paths)
|
||||
ssl_certificate /etc/nginx/ssl/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
|
||||
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
|
||||
|
||||
# SSL configuration
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
# Modern SSL configuration
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# OCSP Stapling
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
resolver 8.8.8.8 8.8.4.4 valid=300s;
|
||||
resolver_timeout 5s;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' ws: wss:;" always;
|
||||
|
||||
# API routes - proxy to backend
|
||||
location /api/ {
|
||||
rewrite ^/api/(.*)$ /$1 break;
|
||||
|
||||
limit_req zone=api_limit burst=20 nodelay;
|
||||
limit_conn conn_limit 10;
|
||||
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# CORS headers for API
|
||||
add_header Access-Control-Allow-Origin $http_origin always;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always;
|
||||
add_header Access-Control-Allow-Credentials "true" always;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# File upload endpoint - special handling
|
||||
location /translate {
|
||||
limit_req zone=upload_limit burst=5 nodelay;
|
||||
limit_conn conn_limit 5;
|
||||
|
||||
proxy_pass http://backend/translate;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Increased timeouts for file processing
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
proxy_pass http://backend/health;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
}
|
||||
|
||||
# Admin endpoints
|
||||
location /admin {
|
||||
limit_req zone=api_limit burst=10 nodelay;
|
||||
|
||||
proxy_pass http://backend/admin;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Frontend - Next.js application
|
||||
location / {
|
||||
proxy_pass http://frontend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# Static files caching
|
||||
location /_next/static/ {
|
||||
proxy_pass http://frontend;
|
||||
proxy_cache_valid 200 365d;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
}
|
||||
|
||||
# Error pages
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
74
docker/nginx/nginx.conf
Normal file
74
docker/nginx/nginx.conf
Normal file
@ -0,0 +1,74 @@
|
||||
# Nginx Configuration for Document Translation API
|
||||
# Production-ready with SSL, caching, and security headers
|
||||
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Logging format
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||
'rt=$request_time uct="$upstream_connect_time" '
|
||||
'uht="$upstream_header_time" urt="$upstream_response_time"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
# Performance optimizations
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml application/json application/javascript
|
||||
application/xml application/rss+xml application/atom+xml image/svg+xml;
|
||||
|
||||
# File upload size (for document translation)
|
||||
client_max_body_size 100M;
|
||||
client_body_timeout 300s;
|
||||
client_header_timeout 60s;
|
||||
|
||||
# Proxy settings
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_buffer_size 128k;
|
||||
proxy_buffers 4 256k;
|
||||
proxy_busy_buffers_size 256k;
|
||||
|
||||
# Rate limiting zones
|
||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
||||
limit_req_zone $binary_remote_addr zone=upload_limit:10m rate=2r/s;
|
||||
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
|
||||
|
||||
# Upstream definitions
|
||||
upstream backend {
|
||||
server backend:8000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
upstream frontend {
|
||||
server frontend:3000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# Include additional configs
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
8
docker/nginx/ssl/.gitkeep
Normal file
8
docker/nginx/ssl/.gitkeep
Normal file
@ -0,0 +1,8 @@
|
||||
# Self-signed SSL certificate placeholder
|
||||
# Replace with real certificates in production!
|
||||
|
||||
# Generate self-signed certificate:
|
||||
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
# -keyout privkey.pem \
|
||||
# -out fullchain.pem \
|
||||
# -subj "/CN=localhost"
|
||||
37
docker/prometheus/prometheus.yml
Normal file
37
docker/prometheus/prometheus.yml
Normal file
@ -0,0 +1,37 @@
|
||||
# Prometheus Configuration for Document Translation API
|
||||
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
external_labels:
|
||||
monitor: 'translate-api'
|
||||
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets: []
|
||||
|
||||
rule_files: []
|
||||
|
||||
scrape_configs:
|
||||
# Backend API metrics
|
||||
- job_name: 'translate-backend'
|
||||
static_configs:
|
||||
- targets: ['backend:8000']
|
||||
metrics_path: /metrics
|
||||
scrape_interval: 10s
|
||||
|
||||
# Nginx metrics (requires nginx-prometheus-exporter)
|
||||
- job_name: 'nginx'
|
||||
static_configs:
|
||||
- targets: ['nginx-exporter:9113']
|
||||
|
||||
# Node exporter for system metrics
|
||||
- job_name: 'node'
|
||||
static_configs:
|
||||
- targets: ['node-exporter:9100']
|
||||
|
||||
# Docker metrics
|
||||
- job_name: 'docker'
|
||||
static_configs:
|
||||
- targets: ['cadvisor:8080']
|
||||
288
k8s/deployment.yaml
Normal file
288
k8s/deployment.yaml
Normal file
@ -0,0 +1,288 @@
|
||||
# ============================================
|
||||
# Document Translation API - Kubernetes Deployment
|
||||
# ============================================
|
||||
# Apply with: kubectl apply -f k8s/
|
||||
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: translate-api
|
||||
labels:
|
||||
app: translate-api
|
||||
|
||||
---
|
||||
# ConfigMap for application settings
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: translate-config
|
||||
namespace: translate-api
|
||||
data:
|
||||
TRANSLATION_SERVICE: "ollama"
|
||||
OLLAMA_BASE_URL: "http://ollama-service:11434"
|
||||
OLLAMA_MODEL: "llama3"
|
||||
MAX_FILE_SIZE_MB: "50"
|
||||
RATE_LIMIT_REQUESTS_PER_MINUTE: "60"
|
||||
RATE_LIMIT_TRANSLATIONS_PER_MINUTE: "10"
|
||||
LOG_LEVEL: "INFO"
|
||||
|
||||
---
|
||||
# Secret for sensitive data
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: translate-secrets
|
||||
namespace: translate-api
|
||||
type: Opaque
|
||||
stringData:
|
||||
ADMIN_USERNAME: "admin"
|
||||
ADMIN_PASSWORD: "changeme123" # Change in production!
|
||||
DEEPL_API_KEY: ""
|
||||
OPENAI_API_KEY: ""
|
||||
|
||||
---
|
||||
# Backend Deployment
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: backend
|
||||
namespace: translate-api
|
||||
labels:
|
||||
app: backend
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: backend
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: translate-api-backend:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: translate-config
|
||||
- secretRef:
|
||||
name: translate-secrets
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "250m"
|
||||
limits:
|
||||
memory: "2Gi"
|
||||
cpu: "1000m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
volumeMounts:
|
||||
- name: uploads
|
||||
mountPath: /app/uploads
|
||||
- name: outputs
|
||||
mountPath: /app/outputs
|
||||
volumes:
|
||||
- name: uploads
|
||||
persistentVolumeClaim:
|
||||
claimName: uploads-pvc
|
||||
- name: outputs
|
||||
persistentVolumeClaim:
|
||||
claimName: outputs-pvc
|
||||
|
||||
---
|
||||
# Backend Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: backend-service
|
||||
namespace: translate-api
|
||||
spec:
|
||||
selector:
|
||||
app: backend
|
||||
ports:
|
||||
- port: 8000
|
||||
targetPort: 8000
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
# Frontend Deployment
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: frontend
|
||||
namespace: translate-api
|
||||
labels:
|
||||
app: frontend
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: translate-api-frontend:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
env:
|
||||
- name: NEXT_PUBLIC_API_URL
|
||||
value: "http://backend-service:8000"
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
|
||||
---
|
||||
# Frontend Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: frontend-service
|
||||
namespace: translate-api
|
||||
spec:
|
||||
selector:
|
||||
app: frontend
|
||||
ports:
|
||||
- port: 3000
|
||||
targetPort: 3000
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
# Ingress for external access
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: translate-ingress
|
||||
namespace: translate-api
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- translate.yourdomain.com
|
||||
secretName: translate-tls
|
||||
rules:
|
||||
- host: translate.yourdomain.com
|
||||
http:
|
||||
paths:
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: backend-service
|
||||
port:
|
||||
number: 8000
|
||||
- path: /translate
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: backend-service
|
||||
port:
|
||||
number: 8000
|
||||
- path: /health
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: backend-service
|
||||
port:
|
||||
number: 8000
|
||||
- path: /admin
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: backend-service
|
||||
port:
|
||||
number: 8000
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: frontend-service
|
||||
port:
|
||||
number: 3000
|
||||
|
||||
---
|
||||
# Persistent Volume Claims
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: uploads-pvc
|
||||
namespace: translate-api
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: outputs-pvc
|
||||
namespace: translate-api
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
|
||||
---
|
||||
# Horizontal Pod Autoscaler for Backend
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: backend-hpa
|
||||
namespace: translate-api
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: backend
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 70
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 80
|
||||
67
scripts/backup.sh
Normal file
67
scripts/backup.sh
Normal file
@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Document Translation API - Backup Script
|
||||
# ============================================
|
||||
# Usage: ./scripts/backup.sh [backup_dir]
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
BACKUP_DIR="${1:-./backups}"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_NAME="translate_backup_$TIMESTAMP"
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${YELLOW}Starting backup: $BACKUP_NAME${NC}"
|
||||
|
||||
# Create backup directory
|
||||
mkdir -p "$BACKUP_DIR/$BACKUP_NAME"
|
||||
|
||||
# Backup uploaded files
|
||||
if [ -d "./uploads" ]; then
|
||||
echo "Backing up uploads..."
|
||||
cp -r ./uploads "$BACKUP_DIR/$BACKUP_NAME/"
|
||||
fi
|
||||
|
||||
# Backup output files
|
||||
if [ -d "./outputs" ]; then
|
||||
echo "Backing up outputs..."
|
||||
cp -r ./outputs "$BACKUP_DIR/$BACKUP_NAME/"
|
||||
fi
|
||||
|
||||
# Backup configuration
|
||||
echo "Backing up configuration..."
|
||||
cp .env* "$BACKUP_DIR/$BACKUP_NAME/" 2>/dev/null || true
|
||||
cp docker-compose*.yml "$BACKUP_DIR/$BACKUP_NAME/" 2>/dev/null || true
|
||||
|
||||
# Backup Docker volumes (if using Docker)
|
||||
if command -v docker &> /dev/null; then
|
||||
echo "Backing up Docker volumes..."
|
||||
|
||||
# Get volume names
|
||||
VOLUMES=$(docker volume ls --format "{{.Name}}" | grep translate || true)
|
||||
|
||||
for vol in $VOLUMES; do
|
||||
echo " Backing up volume: $vol"
|
||||
docker run --rm \
|
||||
-v "$vol:/data:ro" \
|
||||
-v "$(pwd)/$BACKUP_DIR/$BACKUP_NAME:/backup" \
|
||||
alpine tar czf "/backup/${vol}.tar.gz" -C /data . 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
# Compress backup
|
||||
echo "Compressing backup..."
|
||||
cd "$BACKUP_DIR"
|
||||
tar czf "${BACKUP_NAME}.tar.gz" "$BACKUP_NAME"
|
||||
rm -rf "$BACKUP_NAME"
|
||||
|
||||
# Cleanup old backups (keep last 7)
|
||||
echo "Cleaning old backups..."
|
||||
ls -t translate_backup_*.tar.gz 2>/dev/null | tail -n +8 | xargs rm -f 2>/dev/null || true
|
||||
|
||||
echo -e "${GREEN}Backup complete: $BACKUP_DIR/${BACKUP_NAME}.tar.gz${NC}"
|
||||
168
scripts/deploy.sh
Normal file
168
scripts/deploy.sh
Normal file
@ -0,0 +1,168 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Document Translation API - Deployment Script
|
||||
# ============================================
|
||||
# Usage: ./scripts/deploy.sh [environment] [options]
|
||||
# Example: ./scripts/deploy.sh production --with-ollama
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
ENVIRONMENT="${1:-production}"
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
|
||||
# Parse options
|
||||
PROFILES=""
|
||||
while [[ $# -gt 1 ]]; do
|
||||
case $2 in
|
||||
--with-ollama)
|
||||
PROFILES="$PROFILES --profile with-ollama"
|
||||
shift
|
||||
;;
|
||||
--with-cache)
|
||||
PROFILES="$PROFILES --profile with-cache"
|
||||
shift
|
||||
;;
|
||||
--with-monitoring)
|
||||
PROFILES="$PROFILES --profile with-monitoring"
|
||||
shift
|
||||
;;
|
||||
--full)
|
||||
PROFILES="--profile with-ollama --profile with-cache --profile with-monitoring"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Document Translation API Deployment${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Check prerequisites
|
||||
echo -e "${YELLOW}Checking prerequisites...${NC}"
|
||||
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo -e "${RED}Error: Docker is not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||||
echo -e "${RED}Error: Docker Compose is not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Docker and Docker Compose are installed${NC}"
|
||||
|
||||
# Check environment file
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
if [ "$ENVIRONMENT" == "production" ]; then
|
||||
ENV_FILE=".env.production"
|
||||
else
|
||||
ENV_FILE=".env"
|
||||
fi
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo -e "${YELLOW}Creating $ENV_FILE from template...${NC}"
|
||||
if [ -f ".env.example" ]; then
|
||||
cp .env.example "$ENV_FILE"
|
||||
echo -e "${YELLOW}Please edit $ENV_FILE with your configuration${NC}"
|
||||
else
|
||||
echo -e "${RED}Error: No environment file found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Environment file: $ENV_FILE${NC}"
|
||||
|
||||
# Load environment
|
||||
set -a
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
|
||||
# Create SSL directory if needed
|
||||
if [ ! -d "docker/nginx/ssl" ]; then
|
||||
mkdir -p docker/nginx/ssl
|
||||
echo -e "${YELLOW}Created SSL directory. Add your certificates:${NC}"
|
||||
echo " - docker/nginx/ssl/fullchain.pem"
|
||||
echo " - docker/nginx/ssl/privkey.pem"
|
||||
echo " - docker/nginx/ssl/chain.pem"
|
||||
|
||||
# Generate self-signed cert for testing
|
||||
if [ "$ENVIRONMENT" != "production" ]; then
|
||||
echo -e "${YELLOW}Generating self-signed certificate for development...${NC}"
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout docker/nginx/ssl/privkey.pem \
|
||||
-out docker/nginx/ssl/fullchain.pem \
|
||||
-subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" 2>/dev/null
|
||||
cp docker/nginx/ssl/fullchain.pem docker/nginx/ssl/chain.pem
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build and deploy
|
||||
echo ""
|
||||
echo -e "${YELLOW}Building containers...${NC}"
|
||||
docker compose --env-file "$ENV_FILE" build
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Starting services...${NC}"
|
||||
docker compose --env-file "$ENV_FILE" $PROFILES up -d
|
||||
|
||||
# Wait for services to be healthy
|
||||
echo ""
|
||||
echo -e "${YELLOW}Waiting for services to be ready...${NC}"
|
||||
sleep 10
|
||||
|
||||
# Health check
|
||||
echo ""
|
||||
echo -e "${YELLOW}Running health checks...${NC}"
|
||||
|
||||
BACKEND_HEALTH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/health 2>/dev/null || echo "000")
|
||||
if [ "$BACKEND_HEALTH" == "200" ]; then
|
||||
echo -e "${GREEN}✓ Backend is healthy${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Backend health check failed (HTTP $BACKEND_HEALTH)${NC}"
|
||||
fi
|
||||
|
||||
FRONTEND_HEALTH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null || echo "000")
|
||||
if [ "$FRONTEND_HEALTH" == "200" ]; then
|
||||
echo -e "${GREEN}✓ Frontend is healthy${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Frontend health check failed (HTTP $FRONTEND_HEALTH)${NC}"
|
||||
fi
|
||||
|
||||
# Show status
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Deployment Complete!${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "Services running:"
|
||||
docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Access your application:${NC}"
|
||||
echo -e " Frontend: http://localhost (or https://localhost)"
|
||||
echo -e " API: http://localhost/api"
|
||||
echo -e " Admin: http://localhost/admin"
|
||||
echo -e " Health: http://localhost/health"
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Useful commands:${NC}"
|
||||
echo " View logs: docker compose logs -f"
|
||||
echo " Stop: docker compose down"
|
||||
echo " Restart: docker compose restart"
|
||||
echo " Update: ./scripts/deploy.sh $ENVIRONMENT"
|
||||
87
scripts/health-check.sh
Normal file
87
scripts/health-check.sh
Normal file
@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Document Translation API - Health Check Script
|
||||
# ============================================
|
||||
# Usage: ./scripts/health-check.sh [--verbose]
|
||||
|
||||
set -e
|
||||
|
||||
VERBOSE=false
|
||||
if [ "$1" == "--verbose" ]; then
|
||||
VERBOSE=true
|
||||
fi
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
BACKEND_URL="${BACKEND_URL:-http://localhost:8000}"
|
||||
FRONTEND_URL="${FRONTEND_URL:-http://localhost:3000}"
|
||||
NGINX_URL="${NGINX_URL:-http://localhost}"
|
||||
|
||||
EXIT_CODE=0
|
||||
|
||||
check_service() {
|
||||
local name=$1
|
||||
local url=$2
|
||||
local expected=${3:-200}
|
||||
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "$response" == "$expected" ]; then
|
||||
echo -e "${GREEN}✓ $name: OK (HTTP $response)${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ $name: FAILED (HTTP $response, expected $expected)${NC}"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
if [ "$VERBOSE" == "true" ] && [ "$response" == "200" ]; then
|
||||
echo " Response:"
|
||||
curl -s "$url" 2>/dev/null | head -c 500
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
echo "========================================="
|
||||
echo " Health Check - Document Translation API"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# Check backend
|
||||
echo "Backend Services:"
|
||||
check_service "API Health" "$BACKEND_URL/health"
|
||||
check_service "API Root" "$BACKEND_URL/"
|
||||
|
||||
echo ""
|
||||
|
||||
# Check frontend
|
||||
echo "Frontend Services:"
|
||||
check_service "Frontend" "$FRONTEND_URL"
|
||||
|
||||
echo ""
|
||||
|
||||
# Check nginx (if running)
|
||||
echo "Proxy Services:"
|
||||
check_service "Nginx" "$NGINX_URL/health"
|
||||
|
||||
echo ""
|
||||
|
||||
# Docker health
|
||||
if command -v docker &> /dev/null; then
|
||||
echo "Docker Container Status:"
|
||||
docker compose ps --format "table {{.Name}}\t{{.Status}}" 2>/dev/null || echo " Docker Compose not running"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}All health checks passed!${NC}"
|
||||
else
|
||||
echo -e "${RED}Some health checks failed!${NC}"
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
79
scripts/setup-ssl.sh
Normal file
79
scripts/setup-ssl.sh
Normal file
@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Document Translation API - SSL Setup Script
|
||||
# ============================================
|
||||
# Usage: ./scripts/setup-ssl.sh <domain> <email>
|
||||
# Example: ./scripts/setup-ssl.sh translate.example.com admin@example.com
|
||||
|
||||
set -e
|
||||
|
||||
DOMAIN="${1:-}"
|
||||
EMAIL="${2:-}"
|
||||
|
||||
if [ -z "$DOMAIN" ] || [ -z "$EMAIL" ]; then
|
||||
echo "Usage: ./scripts/setup-ssl.sh <domain> <email>"
|
||||
echo "Example: ./scripts/setup-ssl.sh translate.example.com admin@example.com"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${YELLOW}Setting up SSL for $DOMAIN${NC}"
|
||||
|
||||
# Create directory for certbot
|
||||
mkdir -p ./docker/certbot/www
|
||||
mkdir -p ./docker/certbot/conf
|
||||
|
||||
# Create initial nginx config for ACME challenge
|
||||
cat > ./docker/nginx/conf.d/certbot.conf << EOF
|
||||
server {
|
||||
listen 80;
|
||||
server_name $DOMAIN;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://\$host\$request_uri;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Start nginx with HTTP only
|
||||
echo "Starting nginx for certificate request..."
|
||||
docker compose up -d nginx
|
||||
|
||||
# Request certificate
|
||||
echo "Requesting Let's Encrypt certificate..."
|
||||
docker run --rm \
|
||||
-v "$(pwd)/docker/certbot/www:/var/www/certbot" \
|
||||
-v "$(pwd)/docker/certbot/conf:/etc/letsencrypt" \
|
||||
certbot/certbot certonly \
|
||||
--webroot \
|
||||
--webroot-path=/var/www/certbot \
|
||||
--email "$EMAIL" \
|
||||
--agree-tos \
|
||||
--no-eff-email \
|
||||
-d "$DOMAIN"
|
||||
|
||||
# Copy certificates
|
||||
echo "Installing certificates..."
|
||||
cp ./docker/certbot/conf/live/$DOMAIN/fullchain.pem ./docker/nginx/ssl/
|
||||
cp ./docker/certbot/conf/live/$DOMAIN/privkey.pem ./docker/nginx/ssl/
|
||||
cp ./docker/certbot/conf/live/$DOMAIN/chain.pem ./docker/nginx/ssl/
|
||||
|
||||
# Remove temporary config
|
||||
rm ./docker/nginx/conf.d/certbot.conf
|
||||
|
||||
# Restart nginx with SSL
|
||||
echo "Restarting nginx with SSL..."
|
||||
docker compose restart nginx
|
||||
|
||||
echo -e "${GREEN}SSL setup complete for $DOMAIN${NC}"
|
||||
echo ""
|
||||
echo "To auto-renew certificates, add this to crontab:"
|
||||
echo "0 0 1 * * cd $(pwd) && ./scripts/renew-ssl.sh"
|
||||
Loading…
x
Reference in New Issue
Block a user