Enable frontend in Docker Compose and add deployment documentation
Changes: - Uncommented frontend service in docker-compose.yml - Updated README.md with complete setup instructions for both backend and frontend - Added DEPLOYMENT.md with comprehensive production deployment guide Docker Compose: - Frontend now included in docker-compose up - All services (postgres, redis, backend, frontend) start together - Frontend runs on port 3000 with hot reload in development mode README Updates: - Added frontend .env setup instructions - Listed all required environment variables for both services - Updated "Access the application" section with frontend URL - Clarified that database tables are created automatically on first run DEPLOYMENT.md (new): - Complete production deployment guide - Server setup with Docker installation - Production environment configuration - Nginx reverse proxy setup with SSL/TLS - Let's Encrypt SSL certificate instructions - Database backup and restore procedures - Monitoring and logging setup - Security checklist - Performance optimization tips - Scaling strategies for high-traffic scenarios - Troubleshooting guide Now users can: 1. Run `docker-compose up --build` to start all services 2. Access frontend at http://localhost:3000 3. Access backend API at http://localhost:8000 4. Follow DEPLOYMENT.md for production deployment Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e43feb6163
commit
e100f83cb6
3 changed files with 509 additions and 23 deletions
469
DEPLOYMENT.md
Normal file
469
DEPLOYMENT.md
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
# Production Deployment Guide
|
||||
|
||||
This guide covers deploying APAC Ops Bot to production.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Server with Docker and Docker Compose installed
|
||||
- Domain name (optional, for HTTPS)
|
||||
- Azure AD application configured for production URLs
|
||||
- OpenAI API key with Responses API access
|
||||
|
||||
## Deployment Steps
|
||||
|
||||
### 1. Server Setup
|
||||
|
||||
```bash
|
||||
# Update system
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
|
||||
# Install Docker
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sudo sh get-docker.sh
|
||||
|
||||
# Install Docker Compose
|
||||
sudo apt install docker-compose -y
|
||||
|
||||
# Add current user to docker group
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
### 2. Clone Repository
|
||||
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd apac-ops-bot
|
||||
```
|
||||
|
||||
### 3. Configure Environment Variables
|
||||
|
||||
**Backend Production (.env):**
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
cp .env.example .env
|
||||
nano .env
|
||||
```
|
||||
|
||||
Update with production values:
|
||||
|
||||
```env
|
||||
# App
|
||||
APP_NAME=Seapac Ops Bot
|
||||
APP_ENV=production
|
||||
DEBUG=False
|
||||
SECRET_KEY=<generate-strong-secret-key>
|
||||
CORS_ORIGINS=https://your-domain.com
|
||||
|
||||
# Database (use strong password)
|
||||
DATABASE_URL=postgresql+asyncpg://apac_ops_bot:STRONG_PASSWORD@postgres:5432/apac_ops_bot
|
||||
|
||||
# Azure AD (production app registration)
|
||||
AZURE_TENANT_ID=your-production-tenant-id
|
||||
AZURE_CLIENT_ID=your-production-client-id
|
||||
AZURE_CLIENT_SECRET=your-production-client-secret
|
||||
AZURE_REDIRECT_URI=https://your-domain.com/api/v1/auth/msal/callback
|
||||
|
||||
# OpenAI
|
||||
OPENAI_API_KEY=your-openai-api-key
|
||||
OPENAI_VECTOR_STORE_ID=vs_QkOKiQCqzCHS4iFT5lP9qUxc
|
||||
OPENAI_MODEL=gpt-5-nano-2025-08-07
|
||||
OPENAI_API_BASE=https://api.openai.com/v1
|
||||
|
||||
# Redis
|
||||
REDIS_URL=redis://redis:6379/0
|
||||
|
||||
# Rate Limiting (adjust as needed)
|
||||
RATE_LIMIT_PER_MINUTE=30
|
||||
RATE_LIMIT_PER_DAY=1000
|
||||
|
||||
# Token Costs
|
||||
PROMPT_TOKEN_COST=0.00005
|
||||
CACHED_PROMPT_TOKEN_COST=0.000005
|
||||
COMPLETION_TOKEN_COST=0.0004
|
||||
```
|
||||
|
||||
**Frontend Production (.env):**
|
||||
|
||||
```bash
|
||||
cd ../frontend
|
||||
cp .env.example .env
|
||||
nano .env
|
||||
```
|
||||
|
||||
Update with production values:
|
||||
|
||||
```env
|
||||
REACT_APP_API_URL=https://your-domain.com/api/v1
|
||||
REACT_APP_WS_URL=wss://your-domain.com/ws
|
||||
REACT_APP_AZURE_CLIENT_ID=your-production-client-id
|
||||
REACT_APP_AZURE_TENANT_ID=your-production-tenant-id
|
||||
REACT_APP_AZURE_REDIRECT_URI=https://your-domain.com
|
||||
REACT_APP_NAME=Seapac Ops Bot
|
||||
```
|
||||
|
||||
### 4. Update Docker Compose for Production
|
||||
|
||||
Create `docker-compose.prod.yml`:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: apac_ops_bot_postgres
|
||||
environment:
|
||||
POSTGRES_USER: apac_ops_bot
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: apac_ops_bot
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./backups:/backups
|
||||
networks:
|
||||
- apac_network
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U apac_ops_bot"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: apac_ops_bot_redis
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- apac_network
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
target: production
|
||||
container_name: apac_ops_bot_backend
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
environment:
|
||||
- DATABASE_URL=postgresql+asyncpg://apac_ops_bot:${POSTGRES_PASSWORD}@postgres:5432/apac_ops_bot
|
||||
- REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- apac_network
|
||||
restart: always
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
target: production
|
||||
container_name: apac_ops_bot_frontend
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- apac_network
|
||||
restart: always
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: apac_ops_bot_nginx
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||
- ./certbot/conf:/etc/letsencrypt:ro
|
||||
- ./certbot/www:/var/www/certbot:ro
|
||||
depends_on:
|
||||
- backend
|
||||
- frontend
|
||||
networks:
|
||||
- apac_network
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
|
||||
networks:
|
||||
apac_network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
### 5. Configure Nginx
|
||||
|
||||
Create `nginx/nginx.conf`:
|
||||
|
||||
```nginx
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
upstream backend {
|
||||
server backend:8000;
|
||||
}
|
||||
|
||||
upstream frontend {
|
||||
server frontend:80;
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS server
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name your-domain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
# Backend API
|
||||
location /api/ {
|
||||
proxy_pass http://backend;
|
||||
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;
|
||||
}
|
||||
|
||||
# WebSocket
|
||||
location /ws {
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
# Frontend
|
||||
location / {
|
||||
proxy_pass http://frontend;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. SSL Certificate (Let's Encrypt)
|
||||
|
||||
```bash
|
||||
# Install certbot
|
||||
sudo apt install certbot python3-certbot-nginx -y
|
||||
|
||||
# Get certificate
|
||||
sudo certbot certonly --webroot -w ./certbot/www -d your-domain.com
|
||||
|
||||
# Auto-renewal (add to crontab)
|
||||
sudo crontab -e
|
||||
# Add: 0 12 * * * /usr/bin/certbot renew --quiet
|
||||
```
|
||||
|
||||
### 7. Start Production Services
|
||||
|
||||
```bash
|
||||
# Set environment variables for docker-compose
|
||||
export POSTGRES_PASSWORD=$(openssl rand -base64 32)
|
||||
export REDIS_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
# Save passwords securely
|
||||
echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env.secrets
|
||||
echo "REDIS_PASSWORD=$REDIS_PASSWORD" >> .env.secrets
|
||||
chmod 600 .env.secrets
|
||||
|
||||
# Start services
|
||||
docker-compose -f docker-compose.prod.yml up -d --build
|
||||
|
||||
# Check logs
|
||||
docker-compose -f docker-compose.prod.yml logs -f
|
||||
```
|
||||
|
||||
### 8. Database Initialization
|
||||
|
||||
```bash
|
||||
# Run migrations
|
||||
docker-compose -f docker-compose.prod.yml exec backend alembic upgrade head
|
||||
|
||||
# Create initial admin user (optional)
|
||||
docker-compose -f docker-compose.prod.yml exec backend python -c "
|
||||
from app.services.user_service import create_admin_user
|
||||
create_admin_user('admin@example.com', 'Admin User')
|
||||
"
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Health Checks
|
||||
|
||||
```bash
|
||||
# Check all services
|
||||
docker-compose -f docker-compose.prod.yml ps
|
||||
|
||||
# Check backend health
|
||||
curl https://your-domain.com/health
|
||||
|
||||
# Check database
|
||||
docker-compose -f docker-compose.prod.yml exec postgres psql -U apac_ops_bot -c "SELECT 1"
|
||||
```
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
# View all logs
|
||||
docker-compose -f docker-compose.prod.yml logs -f
|
||||
|
||||
# View specific service logs
|
||||
docker-compose -f docker-compose.prod.yml logs -f backend
|
||||
docker-compose -f docker-compose.prod.yml logs -f frontend
|
||||
docker-compose -f docker-compose.prod.yml logs -f postgres
|
||||
```
|
||||
|
||||
## Backup & Restore
|
||||
|
||||
### Database Backup
|
||||
|
||||
```bash
|
||||
# Create backup
|
||||
docker-compose -f docker-compose.prod.yml exec postgres pg_dump -U apac_ops_bot apac_ops_bot > backup_$(date +%Y%m%d_%H%M%S).sql
|
||||
|
||||
# Automated daily backups (add to crontab)
|
||||
0 2 * * * docker-compose -f /path/to/apac-ops-bot/docker-compose.prod.yml exec -T postgres pg_dump -U apac_ops_bot apac_ops_bot > /path/to/backups/backup_$(date +\%Y\%m\%d).sql
|
||||
```
|
||||
|
||||
### Database Restore
|
||||
|
||||
```bash
|
||||
# Restore from backup
|
||||
docker-compose -f docker-compose.prod.yml exec -T postgres psql -U apac_ops_bot apac_ops_bot < backup_20250127.sql
|
||||
```
|
||||
|
||||
## Updates
|
||||
|
||||
```bash
|
||||
# Pull latest changes
|
||||
git pull origin main
|
||||
|
||||
# Rebuild and restart services
|
||||
docker-compose -f docker-compose.prod.yml up -d --build
|
||||
|
||||
# Run new migrations
|
||||
docker-compose -f docker-compose.prod.yml exec backend alembic upgrade head
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Backend not starting
|
||||
|
||||
```bash
|
||||
# Check backend logs
|
||||
docker-compose -f docker-compose.prod.yml logs backend
|
||||
|
||||
# Check environment variables
|
||||
docker-compose -f docker-compose.prod.yml exec backend env | grep -E '(DATABASE|OPENAI|AZURE)'
|
||||
|
||||
# Test database connection
|
||||
docker-compose -f docker-compose.prod.yml exec backend python -c "from app.database import engine; print('DB OK')"
|
||||
```
|
||||
|
||||
### Frontend not loading
|
||||
|
||||
```bash
|
||||
# Check frontend logs
|
||||
docker-compose -f docker-compose.prod.yml logs frontend
|
||||
|
||||
# Check if backend is accessible
|
||||
curl http://backend:8000/health
|
||||
```
|
||||
|
||||
### SSL certificate issues
|
||||
|
||||
```bash
|
||||
# Renew certificate manually
|
||||
sudo certbot renew
|
||||
|
||||
# Check certificate
|
||||
sudo certbot certificates
|
||||
```
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [ ] Strong SECRET_KEY generated
|
||||
- [ ] Strong database password set
|
||||
- [ ] Strong Redis password set
|
||||
- [ ] Azure AD production app registered
|
||||
- [ ] CORS origins properly configured
|
||||
- [ ] SSL certificate installed and auto-renewal configured
|
||||
- [ ] Firewall configured (only 80, 443, SSH)
|
||||
- [ ] Regular database backups scheduled
|
||||
- [ ] Log rotation configured
|
||||
- [ ] Rate limiting enabled
|
||||
- [ ] Monitoring and alerts setup
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Database
|
||||
|
||||
```bash
|
||||
# Optimize PostgreSQL for production
|
||||
# Edit postgresql.conf in container or mount custom config
|
||||
docker-compose -f docker-compose.prod.yml exec postgres psql -U apac_ops_bot -c "ALTER SYSTEM SET shared_buffers = '256MB';"
|
||||
docker-compose -f docker-compose.prod.yml exec postgres psql -U apac_ops_bot -c "ALTER SYSTEM SET effective_cache_size = '1GB';"
|
||||
docker-compose -f docker-compose.prod.yml restart postgres
|
||||
```
|
||||
|
||||
### Backend
|
||||
|
||||
- Backend already runs with 4 workers (production Dockerfile)
|
||||
- Adjust worker count based on CPU cores: `--workers $(nproc)`
|
||||
|
||||
### Redis
|
||||
|
||||
- Redis persistence enabled by default
|
||||
- Consider using Redis Cluster for high availability
|
||||
|
||||
## Scaling
|
||||
|
||||
For high-traffic scenarios:
|
||||
|
||||
1. **Horizontal Scaling**: Run multiple backend instances behind load balancer
|
||||
2. **Database**: Use managed PostgreSQL (AWS RDS, Azure Database)
|
||||
3. **Redis**: Use managed Redis (AWS ElastiCache, Azure Cache)
|
||||
4. **CDN**: Use CloudFlare or AWS CloudFront for frontend
|
||||
5. **Monitoring**: Set up Prometheus + Grafana for metrics
|
||||
|
||||
## Support
|
||||
|
||||
For production issues, contact the development team.
|
||||
19
README.md
19
README.md
|
|
@ -50,12 +50,25 @@ cp .env.example .env
|
|||
# Edit .env with your actual values
|
||||
```
|
||||
|
||||
**Required environment variables:**
|
||||
**Frontend:**
|
||||
```bash
|
||||
cd ../frontend
|
||||
cp .env.example .env
|
||||
# Edit .env with your actual values
|
||||
```
|
||||
|
||||
**Required Backend environment variables:**
|
||||
- `SECRET_KEY` - Generate with: `openssl rand -hex 32`
|
||||
- `AZURE_TENANT_ID` - Your Azure AD tenant ID
|
||||
- `AZURE_CLIENT_ID` - Your Azure AD application client ID
|
||||
- `AZURE_CLIENT_SECRET` - Your Azure AD application client secret
|
||||
- `OPENAI_API_KEY` - Your OpenAI API key
|
||||
- `OPENAI_VECTOR_STORE_ID` - Your Vector Store ID (default: vs_QkOKiQCqzCHS4iFT5lP9qUxc)
|
||||
|
||||
**Required Frontend environment variables:**
|
||||
- `REACT_APP_AZURE_CLIENT_ID` - Same as backend Azure client ID
|
||||
- `REACT_APP_AZURE_TENANT_ID` - Same as backend Azure tenant ID
|
||||
- `REACT_APP_API_URL` - Backend API URL (default: http://localhost:8000/api/v1)
|
||||
|
||||
### 3. Start services with Docker Compose
|
||||
|
||||
|
|
@ -68,9 +81,13 @@ This will start:
|
|||
- PostgreSQL (port 5432)
|
||||
- Redis (port 6379)
|
||||
- Backend API (port 8000)
|
||||
- Frontend (port 3000)
|
||||
|
||||
**First run:** The backend will automatically create database tables on startup.
|
||||
|
||||
### 4. Access the application
|
||||
|
||||
- **Frontend Application:** http://localhost:3000
|
||||
- **API Documentation:** http://localhost:8000/docs
|
||||
- **Health Check:** http://localhost:8000/health
|
||||
|
||||
|
|
|
|||
|
|
@ -63,28 +63,28 @@ services:
|
|||
- apac_network
|
||||
restart: unless-stopped
|
||||
|
||||
# React frontend (will be configured later)
|
||||
# frontend:
|
||||
# build:
|
||||
# context: ./frontend
|
||||
# dockerfile: Dockerfile
|
||||
# target: development
|
||||
# container_name: apac_ops_bot_frontend
|
||||
# volumes:
|
||||
# - ./frontend:/app
|
||||
# - /app/node_modules
|
||||
# ports:
|
||||
# - "3000:3000"
|
||||
# env_file:
|
||||
# - ./frontend/.env
|
||||
# environment:
|
||||
# - REACT_APP_API_URL=http://localhost:8000/api/v1
|
||||
# - REACT_APP_WS_URL=ws://localhost:8000/ws
|
||||
# depends_on:
|
||||
# - backend
|
||||
# networks:
|
||||
# - apac_network
|
||||
# restart: unless-stopped
|
||||
# React frontend
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
target: development
|
||||
container_name: apac_ops_bot_frontend
|
||||
volumes:
|
||||
- ./frontend:/app
|
||||
- /app/node_modules
|
||||
ports:
|
||||
- "3000:3000"
|
||||
env_file:
|
||||
- ./frontend/.env
|
||||
environment:
|
||||
- REACT_APP_API_URL=http://localhost:8000/api/v1
|
||||
- REACT_APP_WS_URL=ws://localhost:8000/ws
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- apac_network
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue