doc: prod deploy
This commit is contained in:
parent
56c6ba536e
commit
5d28f6e6ee
|
|
@ -0,0 +1,234 @@
|
||||||
|
# Banatie Production Deployment Plan
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Isolated Banatie Stack (5 containers):
|
||||||
|
```
|
||||||
|
banatie-landing - Landing Page (node:20-alpine)
|
||||||
|
banatie-api - API Service (node:20-alpine)
|
||||||
|
banatie-postgres - PostgreSQL 15 (isolated from VPS PostgreSQL)
|
||||||
|
banatie-minio - MinIO storage (SNMD mode)
|
||||||
|
banatie-minio-init - Initialization (one-shot)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Domains:
|
||||||
|
```
|
||||||
|
banatie.app → banatie-landing:3000
|
||||||
|
api.banatie.app → banatie-api:3000
|
||||||
|
storage.banatie.app → banatie-minio:9001 (MinIO Console)
|
||||||
|
```
|
||||||
|
|
||||||
|
### File Structure:
|
||||||
|
```
|
||||||
|
~/workspace/projects/banatie-service/ # Git repository (code + config)
|
||||||
|
├── apps/
|
||||||
|
│ ├── api-service/Dockerfile
|
||||||
|
│ └── landing/Dockerfile
|
||||||
|
├── packages/database/
|
||||||
|
└── prod-env/
|
||||||
|
├── docker-compose.yml # Production compose (run from here!)
|
||||||
|
├── .env # Config without secrets (in git)
|
||||||
|
└── secrets.env # Secrets (NOT in git)
|
||||||
|
|
||||||
|
/opt/services/data/banatie/ # Runtime data only
|
||||||
|
├── postgres/ # PostgreSQL data
|
||||||
|
├── storage/drive{1-4}/ # MinIO SNMD
|
||||||
|
├── logs/ # API logs
|
||||||
|
├── results/ # Generated images
|
||||||
|
└── uploads/ # Uploaded files
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker Images
|
||||||
|
|
||||||
|
| Container | Image | Internal Port |
|
||||||
|
|-----------|-------|---------------|
|
||||||
|
| banatie-landing | Built from apps/landing/Dockerfile | 3000 |
|
||||||
|
| banatie-api | Built from apps/api-service/Dockerfile | 3000 |
|
||||||
|
| banatie-postgres | postgres:15-alpine | 5432 |
|
||||||
|
| banatie-minio | quay.io/minio/minio:latest | 9000 (API), 9001 (Console) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Steps
|
||||||
|
|
||||||
|
### Step 1: Prepare VPS Directories
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create runtime data directory
|
||||||
|
sudo mkdir -p /opt/services/data/banatie/{postgres,logs,results,uploads}
|
||||||
|
sudo mkdir -p /opt/services/data/banatie/storage/drive{1,2,3,4}
|
||||||
|
sudo chown -R $USER:$USER /opt/services/data/banatie
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Configure Repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/workspace/projects/banatie-service
|
||||||
|
|
||||||
|
# Create secrets.env (not in git)
|
||||||
|
cp prod-env/secrets.env.example prod-env/secrets.env
|
||||||
|
nano prod-env/secrets.env
|
||||||
|
# Fill in: GEMINI_API_KEY, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Configure Caddy
|
||||||
|
|
||||||
|
Add to `/opt/services/configs/caddy/Caddyfile`:
|
||||||
|
```caddy
|
||||||
|
banatie.app, www.banatie.app {
|
||||||
|
reverse_proxy banatie-landing:3000
|
||||||
|
encode gzip
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||||
|
X-Content-Type-Options "nosniff"
|
||||||
|
X-Frame-Options "DENY"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.banatie.app {
|
||||||
|
reverse_proxy banatie-api:3000
|
||||||
|
encode gzip
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000"
|
||||||
|
X-Content-Type-Options "nosniff"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.banatie.app {
|
||||||
|
reverse_proxy banatie-minio:9001
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: First Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/workspace/projects/banatie-service/prod-env
|
||||||
|
|
||||||
|
# Start stack
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# Restart Caddy to apply config
|
||||||
|
docker restart caddy
|
||||||
|
|
||||||
|
# Create master key
|
||||||
|
curl -X POST https://api.banatie.app/api/bootstrap/initial-key
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Update Process
|
||||||
|
|
||||||
|
### Update Landing:
|
||||||
|
```bash
|
||||||
|
cd ~/workspace/projects/banatie-service
|
||||||
|
git pull origin main
|
||||||
|
cd prod-env
|
||||||
|
docker compose up -d --build banatie-landing
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update API:
|
||||||
|
```bash
|
||||||
|
cd ~/workspace/projects/banatie-service
|
||||||
|
git pull origin main
|
||||||
|
cd prod-env
|
||||||
|
docker compose up -d --build banatie-api
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Both:
|
||||||
|
```bash
|
||||||
|
git pull origin main
|
||||||
|
cd prod-env
|
||||||
|
docker compose up -d --build banatie-landing banatie-api
|
||||||
|
```
|
||||||
|
|
||||||
|
### Full Restart:
|
||||||
|
```bash
|
||||||
|
cd ~/workspace/projects/banatie-service/prod-env
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
**Downtime:** ~2-3 minutes for image build
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DNS Records (add in GoDaddy)
|
||||||
|
|
||||||
|
| Record | Type | Value |
|
||||||
|
|--------|------|-------|
|
||||||
|
| banatie.app | A | 62.146.239.118 |
|
||||||
|
| www | CNAME | banatie.app |
|
||||||
|
| api | A | 62.146.239.118 |
|
||||||
|
| storage | A | 62.146.239.118 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration from Static Landing
|
||||||
|
|
||||||
|
After successful Docker deployment:
|
||||||
|
1. Remove `/var/www/banatie.app/` (old static files)
|
||||||
|
2. Replace banatie.app section in Caddyfile (static → reverse_proxy)
|
||||||
|
3. `docker restart caddy`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Network Architecture
|
||||||
|
|
||||||
|
### Internal Network (banatie-internal)
|
||||||
|
- All containers communicate internally
|
||||||
|
- No external port exposure
|
||||||
|
- PostgreSQL and MinIO API only accessible internally
|
||||||
|
|
||||||
|
### External Network (proxy-network)
|
||||||
|
- Shared with Caddy reverse proxy
|
||||||
|
- Landing, API, and MinIO Console exposed to Caddy
|
||||||
|
- SSL termination at Caddy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
1. **No exposed ports** - All traffic goes through Caddy with SSL
|
||||||
|
2. **Isolated PostgreSQL** - Separate from VPS main database
|
||||||
|
3. **Secrets management** - `secrets.env` not tracked in git
|
||||||
|
4. **MinIO SNMD mode** - Erasure coding for data protection
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Check container status:
|
||||||
|
```bash
|
||||||
|
cd ~/workspace/projects/banatie-service/prod-env
|
||||||
|
docker compose ps
|
||||||
|
docker compose logs banatie-api
|
||||||
|
docker compose logs banatie-landing
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restart specific service:
|
||||||
|
```bash
|
||||||
|
docker compose restart banatie-api
|
||||||
|
```
|
||||||
|
|
||||||
|
### Full rebuild:
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
docker compose build --no-cache
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Caddy logs:
|
||||||
|
```bash
|
||||||
|
docker logs caddy
|
||||||
|
```
|
||||||
|
|
@ -5,29 +5,29 @@
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
PORT=3000
|
PORT=3000
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
API_BASE_URL=http://localhost:3000
|
API_BASE_URL=https://api.banatie.app
|
||||||
|
|
||||||
# CORS Configuration
|
# CORS Configuration
|
||||||
CORS_ORIGIN=*
|
CORS_ORIGIN=*
|
||||||
|
|
||||||
# Database Configuration (Docker internal network)
|
# Database Configuration (Docker internal network)
|
||||||
DB_HOST=postgres
|
DB_HOST=banatie-postgres
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
DB_NAME=banatie_db
|
DB_NAME=banatie_db
|
||||||
DB_USER=banatie_user
|
DB_USER=banatie_user
|
||||||
DB_PASSWORD=banatie_secure_password
|
DB_PASSWORD=banatie_secure_password
|
||||||
DATABASE_URL=postgresql://banatie_user:banatie_secure_password@postgres:5432/banatie_db
|
DATABASE_URL=postgresql://banatie_user:banatie_secure_password@banatie-postgres:5432/banatie_db
|
||||||
|
|
||||||
# MinIO Storage Configuration (Docker internal network)
|
# MinIO Storage Configuration (Docker internal network)
|
||||||
MINIO_ROOT_USER=banatie_admin
|
MINIO_ROOT_USER=banatie_admin
|
||||||
MINIO_ROOT_PASSWORD=banatie_storage_secure_key_2024
|
MINIO_ROOT_PASSWORD=banatie_storage_secure_key_2024
|
||||||
STORAGE_TYPE=minio
|
STORAGE_TYPE=minio
|
||||||
MINIO_ENDPOINT=minio:9000
|
MINIO_ENDPOINT=banatie-minio:9000
|
||||||
MINIO_ACCESS_KEY=banatie_service
|
MINIO_ACCESS_KEY=banatie_service
|
||||||
MINIO_SECRET_KEY=banatie_service_key_2024
|
MINIO_SECRET_KEY=banatie_service_key_2024
|
||||||
MINIO_USE_SSL=false
|
MINIO_USE_SSL=false
|
||||||
MINIO_BUCKET_NAME=banatie
|
MINIO_BUCKET_NAME=banatie
|
||||||
MINIO_PUBLIC_URL=http://localhost:9000
|
MINIO_PUBLIC_URL=https://storage.banatie.app
|
||||||
|
|
||||||
# Multi-tenancy Configuration
|
# Multi-tenancy Configuration
|
||||||
DEFAULT_ORG_ID=default
|
DEFAULT_ORG_ID=default
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,27 @@
|
||||||
|
# Banatie Production Docker Compose
|
||||||
|
# Run from: ~/workspace/projects/banatie-service/prod-env/
|
||||||
|
# Data stored in: /opt/services/data/banatie/
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# API Service
|
# API Service
|
||||||
app:
|
banatie-api:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
dockerfile: apps/api-service/Dockerfile
|
dockerfile: apps/api-service/Dockerfile
|
||||||
target: production
|
target: production
|
||||||
container_name: banatie-app
|
container_name: banatie-api
|
||||||
ports:
|
# No ports exposed - access through Caddy reverse proxy
|
||||||
- "3000:3000"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ../apps/api-service/logs:/app/apps/api-service/logs
|
- /opt/services/data/banatie/logs:/app/apps/api-service/logs
|
||||||
- ../data/results:/app/results
|
- /opt/services/data/banatie/results:/app/results
|
||||||
- ../data/uploads:/app/uploads
|
- /opt/services/data/banatie/uploads:/app/uploads
|
||||||
networks:
|
networks:
|
||||||
- banatie-network
|
- banatie-internal
|
||||||
|
- proxy-network
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
banatie-postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
minio:
|
banatie-minio:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
|
@ -28,17 +32,17 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# Landing Page
|
# Landing Page
|
||||||
landing:
|
banatie-landing:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
dockerfile: apps/landing/Dockerfile
|
dockerfile: apps/landing/Dockerfile
|
||||||
container_name: banatie-landing
|
container_name: banatie-landing
|
||||||
ports:
|
# No ports exposed - access through Caddy reverse proxy
|
||||||
- "3001:3000"
|
|
||||||
networks:
|
networks:
|
||||||
- banatie-network
|
- banatie-internal
|
||||||
|
- proxy-network
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- banatie-postgres
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
- secrets.env
|
- secrets.env
|
||||||
|
|
@ -47,21 +51,20 @@ services:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# PostgreSQL Database
|
# PostgreSQL Database (isolated for Banatie)
|
||||||
postgres:
|
banatie-postgres:
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
container_name: banatie-postgres
|
container_name: banatie-postgres
|
||||||
ports:
|
# No ports exposed - internal access only
|
||||||
- "5460:5432"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ../data/postgres:/var/lib/postgresql/data
|
- /opt/services/data/banatie/postgres:/var/lib/postgresql/data
|
||||||
- ../scripts/init-db.sql:/docker-entrypoint-initdb.d/01-init.sql
|
- ../scripts/init-db.sql:/docker-entrypoint-initdb.d/01-init.sql
|
||||||
networks:
|
networks:
|
||||||
- banatie-network
|
- banatie-internal
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: banatie_db
|
POSTGRES_DB: banatie_db
|
||||||
POSTGRES_USER: banatie_user
|
POSTGRES_USER: banatie_user
|
||||||
POSTGRES_PASSWORD: banatie_secure_password
|
POSTGRES_PASSWORD: ${DB_PASSWORD:-banatie_secure_password}
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U banatie_user -d banatie_db"]
|
test: ["CMD-SHELL", "pg_isready -U banatie_user -d banatie_db"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
|
|
@ -71,26 +74,25 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# MinIO Object Storage - Production Ready with SNMD
|
# MinIO Object Storage - Production Ready with SNMD
|
||||||
minio:
|
banatie-minio:
|
||||||
image: quay.io/minio/minio:latest
|
image: quay.io/minio/minio:latest
|
||||||
container_name: banatie-storage
|
container_name: banatie-minio
|
||||||
ports:
|
# No ports exposed - Console through Caddy, API internal only
|
||||||
- "9000:9000" # S3 API
|
|
||||||
- "9001:9001" # Web Console
|
|
||||||
volumes:
|
volumes:
|
||||||
# SNMD: 4 drives for full S3 compatibility and erasure coding
|
# SNMD: 4 drives for full S3 compatibility and erasure coding
|
||||||
- ../data/storage/drive1:/data1
|
- /opt/services/data/banatie/storage/drive1:/data1
|
||||||
- ../data/storage/drive2:/data2
|
- /opt/services/data/banatie/storage/drive2:/data2
|
||||||
- ../data/storage/drive3:/data3
|
- /opt/services/data/banatie/storage/drive3:/data3
|
||||||
- ../data/storage/drive4:/data4
|
- /opt/services/data/banatie/storage/drive4:/data4
|
||||||
networks:
|
networks:
|
||||||
- banatie-network
|
- banatie-internal
|
||||||
|
- proxy-network # For MinIO Console access via Caddy
|
||||||
environment:
|
environment:
|
||||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
MINIO_BROWSER_REDIRECT_URL: http://localhost:9001
|
# Production URLs (through Caddy)
|
||||||
MINIO_SERVER_URL: http://localhost:9000
|
MINIO_BROWSER_REDIRECT_URL: https://storage.banatie.app
|
||||||
MINIO_DOMAIN: localhost
|
MINIO_SERVER_URL: https://api.banatie.app
|
||||||
# CRITICAL: SNMD command for full S3 compatibility
|
# CRITICAL: SNMD command for full S3 compatibility
|
||||||
command: server /data{1...4} --console-address ":9001"
|
command: server /data{1...4} --console-address ":9001"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
|
@ -102,29 +104,32 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# MinIO Storage Initialization
|
# MinIO Storage Initialization
|
||||||
storage-init:
|
banatie-minio-init:
|
||||||
image: minio/mc:latest
|
image: minio/mc:latest
|
||||||
container_name: banatie-storage-init
|
container_name: banatie-minio-init
|
||||||
networks:
|
networks:
|
||||||
- banatie-network
|
- banatie-internal
|
||||||
depends_on:
|
depends_on:
|
||||||
minio:
|
banatie-minio:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
entrypoint:
|
entrypoint:
|
||||||
- /bin/sh
|
- /bin/sh
|
||||||
- -c
|
- -c
|
||||||
- |
|
- |
|
||||||
echo 'Setting up MinIO alias...'
|
echo 'Setting up MinIO alias...'
|
||||||
mc alias set storage http://minio:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD}
|
mc alias set storage http://banatie-minio:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD}
|
||||||
|
|
||||||
echo 'Creating main bucket...'
|
echo 'Creating main bucket...'
|
||||||
mc mb --ignore-existing storage/banatie
|
mc mb --ignore-existing storage/banatie
|
||||||
|
|
||||||
echo 'Creating service user...'
|
echo 'Creating service user...'
|
||||||
mc admin user add storage banatie_service banatie_service_key_2024
|
mc admin user add storage banatie_service banatie_service_key_2024 || true
|
||||||
|
|
||||||
echo 'Attaching readwrite policy to service user...'
|
echo 'Attaching readwrite policy to service user...'
|
||||||
mc admin policy attach storage readwrite --user=banatie_service
|
mc admin policy attach storage readwrite --user=banatie_service || true
|
||||||
|
|
||||||
echo 'Setting up lifecycle policy...'
|
echo 'Setting up lifecycle policy...'
|
||||||
cat > /tmp/lifecycle.json <<'LIFECYCLE'
|
cat > /tmp/lifecycle.json <<'LIFECYCLE'
|
||||||
|
|
@ -143,7 +148,7 @@ services:
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
LIFECYCLE
|
LIFECYCLE
|
||||||
mc ilm import storage/banatie < /tmp/lifecycle.json
|
mc ilm import storage/banatie < /tmp/lifecycle.json || true
|
||||||
|
|
||||||
echo 'Storage initialization completed!'
|
echo 'Storage initialization completed!'
|
||||||
echo 'Bucket: banatie'
|
echo 'Bucket: banatie'
|
||||||
|
|
@ -153,11 +158,9 @@ services:
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
banatie-network:
|
# Internal network for service-to-service communication
|
||||||
|
banatie-internal:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
# External network shared with Caddy reverse proxy
|
||||||
volumes:
|
proxy-network:
|
||||||
postgres-data:
|
external: true
|
||||||
driver: local
|
|
||||||
storage-data:
|
|
||||||
driver: local
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue