chore(deploy): prod deploy config
This commit is contained in:
5
deploy/.env.example
Normal file
5
deploy/.env.example
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Database
|
||||||
|
POSTGRES_PASSWORD=your_secure_password_here
|
||||||
|
|
||||||
|
# Cloudflare Tunnel token from Zero Trust dashboard
|
||||||
|
CLOUDFLARE_TUNNEL_TOKEN=your_tunnel_token_here
|
||||||
77
deploy/docker-compose.yml
Normal file
77
deploy/docker-compose.yml
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: codetutor-db
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: codetutor
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
||||||
|
POSTGRES_DB: codetutor
|
||||||
|
volumes:
|
||||||
|
- ./data/postgres:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U codetutor"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: ../backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: codetutor-backend
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql+asyncpg://codetutor:${POSTGRES_PASSWORD}@db:5432/codetutor
|
||||||
|
CORS_ORIGINS: '["https://codetutor-demo.kschappell.com", "https://kschappell.com"]'
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ../frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: codetutor-frontend
|
||||||
|
environment:
|
||||||
|
NEXT_PUBLIC_API_URL: https://codetutor-demo.kschappell.com
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: codetutor-nginx
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
depends_on:
|
||||||
|
- frontend
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- public-network
|
||||||
|
|
||||||
|
cloudflared:
|
||||||
|
image: cloudflare/cloudflared:latest
|
||||||
|
container_name: codetutor-cloudflared
|
||||||
|
command: tunnel run
|
||||||
|
environment:
|
||||||
|
TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN:?CLOUDFLARE_TUNNEL_TOKEN is required}
|
||||||
|
depends_on:
|
||||||
|
- nginx
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- public-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
internal:
|
||||||
|
driver: bridge
|
||||||
|
public-network:
|
||||||
|
external: true
|
||||||
50
deploy/nginx.conf
Normal file
50
deploy/nginx.conf
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
upstream frontend {
|
||||||
|
server frontend:3000;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream backend {
|
||||||
|
server backend:8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Allow iframe embedding from portfolio
|
||||||
|
add_header Content-Security-Policy "frame-ancestors 'self' https://kschappell.com https://*.kschappell.com" always;
|
||||||
|
|
||||||
|
# API routes to backend
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Everything else to 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;
|
||||||
|
|
||||||
|
# WebSocket support for Next.js HMR (dev) and any real-time features
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check endpoint
|
||||||
|
location /health {
|
||||||
|
return 200 'OK';
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
114
deploy/readme.md
Normal file
114
deploy/readme.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# CodeTutor Deployment
|
||||||
|
|
||||||
|
Production deployment configuration using Docker Compose and Cloudflare Tunnel.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet -> Cloudflare Tunnel -> nginx -> frontend (Next.js)
|
||||||
|
\-> backend (FastAPI)
|
||||||
|
\-> PostgreSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker and Docker Compose
|
||||||
|
- Cloudflare account with Zero Trust access
|
||||||
|
- External Docker network: `public-network`
|
||||||
|
|
||||||
|
Create the external network if it doesn't exist:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker network create public-network
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### 1. Configure Environment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `.env` with:
|
||||||
|
|
||||||
|
- `POSTGRES_PASSWORD`: Strong password for the database
|
||||||
|
- `CLOUDFLARE_TUNNEL_TOKEN`: Token from Cloudflare Zero Trust dashboard
|
||||||
|
|
||||||
|
### 2. Create Cloudflare Tunnel
|
||||||
|
|
||||||
|
1. Go to [Cloudflare Zero Trust Dashboard](https://one.dash.cloudflare.com/)
|
||||||
|
2. Navigate to Networks > Tunnels
|
||||||
|
3. Create a new tunnel named `codetutor-demo`
|
||||||
|
4. Add public hostname:
|
||||||
|
- Subdomain: `codetutor-demo`
|
||||||
|
- Domain: `kschappell.com`
|
||||||
|
- Service: `http://nginx:80`
|
||||||
|
5. Copy the tunnel token to your `.env` file
|
||||||
|
|
||||||
|
### 3. Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Verify
|
||||||
|
|
||||||
|
- Check service health: `docker compose ps`
|
||||||
|
- View logs: `docker compose logs -f`
|
||||||
|
- Test endpoint: `curl https://codetutor-demo.kschappell.com/health`
|
||||||
|
|
||||||
|
## Database Management
|
||||||
|
|
||||||
|
### Initial Setup
|
||||||
|
|
||||||
|
The database is automatically created on first run. To seed with initial data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec backend python -m alembic upgrade head
|
||||||
|
docker compose exec backend python -m scripts.seed_db
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backups
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec db pg_dump -U codetutor codetutor > backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec -T db psql -U codetutor codetutor < backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Services not starting
|
||||||
|
|
||||||
|
Check logs for specific service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs backend
|
||||||
|
docker compose logs frontend
|
||||||
|
docker compose logs nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database connection issues
|
||||||
|
|
||||||
|
Ensure the database is healthy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps db
|
||||||
|
docker compose logs db
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tunnel not connecting
|
||||||
|
|
||||||
|
Verify the tunnel token is correct and the tunnel is active in the Cloudflare dashboard.
|
||||||
Reference in New Issue
Block a user