chore: PostgreSQL migration compatibility and infrastructure improvements
Database & Migrations: - Update all Alembic migrations for PostgreSQL compatibility - Remove SQLite-specific syntax (AUTOINCREMENT, etc.) - Add database utility helpers for PostgreSQL operations - Fix services to use PostgreSQL-compatible queries Documentation: - Add comprehensive Docker deployment guide - Add production deployment documentation - Add infrastructure architecture documentation - Update database setup guide for PostgreSQL-only - Expand troubleshooting guide Architecture & Validation: - Add migration.yaml rules for SQL compatibility checking - Enhance validate_architecture.py with migration validation - Update architecture rules to validate Alembic migrations Development: - Fix duplicate install-all target in Makefile - Add Celery/Redis validation to install.py script - Add docker-compose.test.yml for CI testing - Add squash_migrations.py utility script - Update tests for PostgreSQL compatibility - Improve test fixtures in conftest.py Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,524 @@
|
||||
# Docker Deployment
|
||||
|
||||
This guide covers deploying Wizamart using Docker and Docker Compose.
|
||||
|
||||
**Best for:** Teams who want consistent environments and easy rollbacks.
|
||||
|
||||
---
|
||||
|
||||
## Development vs Production
|
||||
|
||||
| Aspect | Development | Production |
|
||||
|--------|-------------|------------|
|
||||
| Compose file | `docker-compose.yml` | `docker-compose.prod.yml` |
|
||||
| App server | Hot reload enabled | Multiple workers |
|
||||
| Database | Local volume | Persistent volume with backups |
|
||||
| SSL | Not needed | Required (via Nginx) |
|
||||
| Logging | Console | File + centralized |
|
||||
|
||||
---
|
||||
|
||||
## Development Setup
|
||||
|
||||
```bash
|
||||
# Start all services
|
||||
make docker-up
|
||||
|
||||
# Or manually
|
||||
docker compose up -d
|
||||
|
||||
# View logs
|
||||
docker compose logs -f
|
||||
|
||||
# Stop services
|
||||
make docker-down
|
||||
```
|
||||
|
||||
### Current Services
|
||||
|
||||
| Service | Port | Purpose |
|
||||
|---------|------|---------|
|
||||
| db | 5432 | PostgreSQL database |
|
||||
| redis | 6379 | Cache and queue broker |
|
||||
| api | 8000 | FastAPI application |
|
||||
|
||||
---
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### 1. Create Production Compose File
|
||||
|
||||
```yaml
|
||||
# docker-compose.prod.yml
|
||||
services:
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
restart: always
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000"
|
||||
environment:
|
||||
DATABASE_URL: postgresql://wizamart_user:${DB_PASSWORD}@db:5432/wizamart_db
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
CELERY_BROKER_URL: redis://redis:6379/1
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- uploads:/app/uploads
|
||||
- logs:/app/logs
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1G
|
||||
|
||||
celery:
|
||||
build: .
|
||||
restart: always
|
||||
command: celery -A app.celery worker --loglevel=info --concurrency=4
|
||||
environment:
|
||||
DATABASE_URL: postgresql://wizamart_user:${DB_PASSWORD}@db:5432/wizamart_db
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
CELERY_BROKER_URL: redis://redis:6379/1
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
volumes:
|
||||
- logs:/app/logs
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
|
||||
celery-beat:
|
||||
build: .
|
||||
restart: always
|
||||
command: celery -A app.celery beat --loglevel=info
|
||||
environment:
|
||||
CELERY_BROKER_URL: redis://redis:6379/1
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 256M
|
||||
|
||||
db:
|
||||
image: postgres:15-alpine
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_DB: wizamart_db
|
||||
POSTGRES_USER: wizamart_user
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U wizamart_user -d wizamart_db"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: always
|
||||
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 300M
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
restart: always
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
- ./static:/app/static:ro
|
||||
- uploads:/app/uploads:ro
|
||||
- /etc/letsencrypt:/etc/letsencrypt:ro
|
||||
depends_on:
|
||||
- api
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
uploads:
|
||||
logs:
|
||||
```
|
||||
|
||||
### 2. Create Dockerfile
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Tailwind CLI
|
||||
RUN curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 \
|
||||
&& chmod +x tailwindcss-linux-x64 \
|
||||
&& mv tailwindcss-linux-x64 /usr/local/bin/tailwindcss
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install Python dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application
|
||||
COPY . .
|
||||
|
||||
# Build Tailwind CSS
|
||||
RUN tailwindcss -i ./static/admin/css/tailwind.css -o ./static/admin/css/tailwind.output.css --minify \
|
||||
&& tailwindcss -i ./static/vendor/css/tailwind.css -o ./static/vendor/css/tailwind.output.css --minify \
|
||||
&& tailwindcss -i ./static/shop/css/tailwind.css -o ./static/shop/css/tailwind.output.css --minify \
|
||||
&& tailwindcss -i ./static/platform/css/tailwind.css -o ./static/platform/css/tailwind.output.css --minify
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -m -u 1000 wizamart && chown -R wizamart:wizamart /app
|
||||
USER wizamart
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
|
||||
```
|
||||
|
||||
### 3. Nginx Configuration
|
||||
|
||||
```bash
|
||||
mkdir -p nginx/conf.d
|
||||
```
|
||||
|
||||
```nginx
|
||||
# nginx/conf.d/wizamart.conf
|
||||
upstream api {
|
||||
server api:8000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name yourdomain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
|
||||
# Static files
|
||||
location /static {
|
||||
alias /app/static;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
location /uploads {
|
||||
alias /app/uploads;
|
||||
expires 7d;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://api;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Deploy
|
||||
|
||||
```bash
|
||||
# Create .env file with production values
|
||||
cp .env.example .env
|
||||
nano .env
|
||||
|
||||
# Set database password
|
||||
export DB_PASSWORD=$(openssl rand -hex 16)
|
||||
echo "DB_PASSWORD=$DB_PASSWORD" >> .env
|
||||
|
||||
# Build and start
|
||||
docker compose -f docker-compose.prod.yml build
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# Run migrations
|
||||
docker compose -f docker-compose.prod.yml exec api alembic upgrade head
|
||||
|
||||
# Initialize data
|
||||
docker compose -f docker-compose.prod.yml exec api python scripts/init_production.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Daily Operations
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker compose -f docker-compose.prod.yml logs -f
|
||||
|
||||
# Specific service
|
||||
docker compose -f docker-compose.prod.yml logs -f api
|
||||
docker compose -f docker-compose.prod.yml logs -f celery
|
||||
|
||||
# Last 100 lines
|
||||
docker compose -f docker-compose.prod.yml logs --tail 100 api
|
||||
```
|
||||
|
||||
### Access Container Shell
|
||||
|
||||
```bash
|
||||
# API container
|
||||
docker compose -f docker-compose.prod.yml exec api bash
|
||||
|
||||
# Database
|
||||
docker compose -f docker-compose.prod.yml exec db psql -U wizamart_user -d wizamart_db
|
||||
|
||||
# Redis
|
||||
docker compose -f docker-compose.prod.yml exec redis redis-cli
|
||||
```
|
||||
|
||||
### Restart Services
|
||||
|
||||
```bash
|
||||
# Single service
|
||||
docker compose -f docker-compose.prod.yml restart api
|
||||
|
||||
# All services
|
||||
docker compose -f docker-compose.prod.yml restart
|
||||
```
|
||||
|
||||
### Deploy Updates
|
||||
|
||||
```bash
|
||||
# Pull latest code
|
||||
git pull origin main
|
||||
|
||||
# Rebuild and restart
|
||||
docker compose -f docker-compose.prod.yml build api celery
|
||||
docker compose -f docker-compose.prod.yml up -d api celery
|
||||
|
||||
# Run migrations if needed
|
||||
docker compose -f docker-compose.prod.yml exec api alembic upgrade head
|
||||
```
|
||||
|
||||
### Rollback
|
||||
|
||||
```bash
|
||||
# View image history
|
||||
docker images wizamart-api
|
||||
|
||||
# Tag current as backup
|
||||
docker tag wizamart-api:latest wizamart-api:backup
|
||||
|
||||
# Rollback to previous
|
||||
docker compose -f docker-compose.prod.yml down api
|
||||
docker tag wizamart-api:previous wizamart-api:latest
|
||||
docker compose -f docker-compose.prod.yml up -d api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backups
|
||||
|
||||
### Database Backup
|
||||
|
||||
```bash
|
||||
# Create backup
|
||||
docker compose -f docker-compose.prod.yml exec db pg_dump -U wizamart_user wizamart_db | gzip > backup_$(date +%Y%m%d).sql.gz
|
||||
|
||||
# Restore backup
|
||||
gunzip -c backup_20240115.sql.gz | docker compose -f docker-compose.prod.yml exec -T db psql -U wizamart_user -d wizamart_db
|
||||
```
|
||||
|
||||
### Volume Backup
|
||||
|
||||
```bash
|
||||
# Backup all volumes
|
||||
docker run --rm \
|
||||
-v wizamart_postgres_data:/data \
|
||||
-v $(pwd)/backups:/backup \
|
||||
alpine tar czf /backup/postgres_$(date +%Y%m%d).tar.gz /data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Resource Usage
|
||||
|
||||
```bash
|
||||
docker stats
|
||||
```
|
||||
|
||||
### Health Checks
|
||||
|
||||
```bash
|
||||
# Check service health
|
||||
docker compose -f docker-compose.prod.yml ps
|
||||
|
||||
# Test API health
|
||||
curl -s http://localhost:8000/health | jq
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container Won't Start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker compose -f docker-compose.prod.yml logs api
|
||||
|
||||
# Check container status
|
||||
docker compose -f docker-compose.prod.yml ps -a
|
||||
|
||||
# Inspect container
|
||||
docker inspect <container_id>
|
||||
```
|
||||
|
||||
### Database Connection Issues
|
||||
|
||||
```bash
|
||||
# Test from API container
|
||||
docker compose -f docker-compose.prod.yml exec api python -c "
|
||||
from app.core.database import engine
|
||||
with engine.connect() as conn:
|
||||
print('Connected!')
|
||||
"
|
||||
```
|
||||
|
||||
### Out of Disk Space
|
||||
|
||||
```bash
|
||||
# Check disk usage
|
||||
docker system df
|
||||
|
||||
# Clean up
|
||||
docker system prune -a --volumes
|
||||
```
|
||||
|
||||
### Memory Issues
|
||||
|
||||
```bash
|
||||
# Check memory usage
|
||||
docker stats --no-stream
|
||||
|
||||
# Increase limits in docker-compose.prod.yml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### Non-Root User
|
||||
|
||||
All containers run as non-root users. The Dockerfile creates a `wizamart` user.
|
||||
|
||||
### Secret Management
|
||||
|
||||
```bash
|
||||
# Use Docker secrets (Swarm mode)
|
||||
echo "your-password" | docker secret create db_password -
|
||||
|
||||
# Or use environment files
|
||||
# Never commit .env to git
|
||||
```
|
||||
|
||||
### Network Isolation
|
||||
|
||||
```yaml
|
||||
# Add to docker-compose.prod.yml
|
||||
networks:
|
||||
frontend:
|
||||
backend:
|
||||
|
||||
services:
|
||||
nginx:
|
||||
networks:
|
||||
- frontend
|
||||
api:
|
||||
networks:
|
||||
- frontend
|
||||
- backend
|
||||
db:
|
||||
networks:
|
||||
- backend
|
||||
redis:
|
||||
networks:
|
||||
- backend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scaling
|
||||
|
||||
### Horizontal Scaling
|
||||
|
||||
```bash
|
||||
# Scale API containers
|
||||
docker compose -f docker-compose.prod.yml up -d --scale api=3
|
||||
|
||||
# Update nginx upstream
|
||||
upstream api {
|
||||
server api_1:8000;
|
||||
server api_2:8000;
|
||||
server api_3:8000;
|
||||
}
|
||||
```
|
||||
|
||||
### Moving to Kubernetes
|
||||
|
||||
When you outgrow Docker Compose, see our Kubernetes migration guide (coming soon).
|
||||
|
||||
Reference in New Issue
Block a user