doc: prod deploy

This commit is contained in:
Oleg Proskurin 2025-12-03 00:57:11 +07:00
parent 56c6ba536e
commit 5d28f6e6ee
3 changed files with 292 additions and 55 deletions

234
docs/deployment-plan.md Normal file
View File

@ -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
```

View File

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

View File

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