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:
2026-01-11 17:52:28 +01:00
parent 2792414395
commit 3614d448e4
45 changed files with 3179 additions and 507 deletions

View File

@@ -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).