12 KiB
12 KiB
ADR-041: Single-Host Server Consolidation
Status: Proposed Date: 2026-06-20 Related Documents:
- ADR-040: OCR Sidecar Refactor
- ADR-016: Security & Authentication
- ADR-023A: Unified AI Architecture
- ADR-034: AI Model Change
- CONTEXT.md
🎯 Context and Problem Statement
Current Architecture
ปัจจุบัน LCBP3-DMS กระจาย services ไว้บนหลายเครื่อง:
| Service | Host | Hardware | Network |
|---|---|---|---|
| Ollama (np-dms-ai, np-dms-ocr, nomic-embed) | Desk-5439 | RTX 4060 Ti 16GB | VLAN 10 (192.168.10.100) |
| OCR Sidecar (FastAPI) | Desk-5439 | Same as above | VLAN 10 (192.168.10.100) |
| Backend (NestJS) | QNAP NAS | - | VLAN 10 (192.168.10.8) |
| Frontend (Next.js) | QNAP NAS | - | VLAN 10 (192.168.10.8) |
| Redis | QNAP NAS | - | VLAN 10 (192.168.10.8) |
| MariaDB | QNAP NAS | - | VLAN 10 (192.168.10.8) |
| Elasticsearch | QNAP NAS | - | VLAN 10 (192.168.10.8) |
| File Storage | QNAP NAS | - | CIFS share np-dms-as |
Problems Identified
- Cross-Host Trust Boundary: Backend ↔ sidecar/Ollama ผ่าน LAN (VLAN 10) — ต้องพึ่ง VLAN/firewall ACL สำหรับ isolation (ADR-040 §4)
- Management Complexity: Services กระจายบน 2 hosts → deployment, monitoring, troubleshooting ซับซ้อน
- GPU Resource Fragmentation: Desk-5439 มี GPU แต่ CPU/RAM น้อย → ไม่สามารถรัน backend ได้
- Network Latency: Backend ↔ Ollama ผ่าน LAN เพิ่ม latency สำหรับ AI inference
- Hardware Underutilization: QNAP NAS มี CPU/RAM แต่ไม่มี GPU → ไม่สามารถรัน AI models ได้
New Hardware
มีเซิร์ฟเวอร์ใหม่พร้อมใช้งาน:
- CPU: Ryzen 5 5600 (6 cores / 12 threads)
- RAM: 32GB DDR4
- GPU: RTX 5060 Ti 16GB
- Storage: SSD (OS) + HDD (data)
⚙️ Decision Drivers
- Simplify Architecture: ลดจำนวน hosts จาก 2 → 1
- Enable Docker-Internal Isolation: Sidecar + backend อยู่บน Docker bridge เดียวกัน → network auth จริง (ADR-040 D5)
- Better Resource Utilization: Single host มีทั้ง CPU, RAM, GPU ในเครื่องเดียว
- Reduce Network Latency: Backend ↔ Ollama ผ่าน localhost แทน LAN
- Maintain Data Separation: QNAP ยังคงเป็น NAS สำหรับ file storage
🏛️ Decisions
D1: Co-locate All Services on Single Docker Host
ย้าย services ทั้งหมดไปรันบนเซิร์ฟเวอร์ใหม่:
- Ollama (np-dms-ai, np-dms-ocr, nomic-embed)
- OCR Sidecar (FastAPI)
- Backend (NestJS)
- Frontend (Next.js)
- Redis
- MariaDB
- Elasticsearch
Retire Desk-5439 หลัง cutover สำเร็จ
D2: ASUSTOR as Primary NAS, QNAP as Backup
QNAP (192.168.10.8) ลดบทบาทเป็น backup server เท่านั้น
ASUSTOR (192.168.10.9) เป็น Primary NAS สำหรับ:
- Upload temp storage (
/data/uploads/temp) - Permanent file storage (
/data/uploads/permanent) - CIFS share
np-dms-asถูก mount บน new host ผ่าน:/mnt/uploads/temp→//192.168.10.9/np-dms-as/data/uploads/temp/mnt/uploads/permanent→//192.168.10.9/np-dms-as/data/uploads/permanent
D3: Docker-Internal Network Only for Sidecar/Ollama
- Sidecar และ Ollama ไม่ publish ports ไป LAN (ใช้
exposeแทนports) - Services อยู่บน internal Docker bridge network (
dms-internal) - Backend ติดต่อ sidecar/Ollama ผ่าน
http://sidecar:8765และhttp://ollama:11434(service names) - Frontend ติดต่อ backend ผ่าน
http://backend:3000 - เฉพาะ Frontend และ Backend เท่านั้นที่ publish ports ไป LAN (80, 443, 3000)
Enables ADR-040 D5: Network isolation ผ่าน Docker-internal bridge → ลบ X-API-Key ได้จริง
D4: GPU VRAM Management Reinforced
RTX 5060 Ti 16GB ต้องรองรับ:
np-dms-ai(Typhoon-2.5 ~7–8B) ~6–8GBnp-dms-ocr(Typhoon OCR) ~5GBnomic-embed-text~0.5GB- BGE-M3 + Reranker (ถ้า GPU-resident) ~4.5GB
- CUDA overhead ~1.5GB
Total ≈ 15.5GB → OOM risk หาก load พร้อมกันทั้งหมด
Mandatory:
- ADR-040 D3 (Adaptive OCR Residency via
calculate_ocr_residency()) - ADR-040 D4 (CPU Fallback Retrieval for embed/rerank)
- LLM-First GPU Ownership (CONTEXT.md)
- ไม่บังคับ BGE+Reranker GPU-resident ถาวร
D5: RAM Budget Considerations
32GB RAM ต้องรองรับ:
- Node.js (Frontend) ~500MB
- NestJS (Backend) ~1–2GB
- MariaDB ~4–8GB (ขึ้นกับ dataset size)
- Redis ~500MB
- Elasticsearch ~2–4GB (ขึ้นกับ index size)
- Python (Sidecar) ~500MB
- Ollama ~1–2GB
- BGE/Reranker CPU-fallback tensors ~2–4GB
Action Items:
- Size DB/ES/Redis memory limits ก่อน cutover
- Monitor RAM usage หลัง cutover
- พิจารณา swap space ถ้าจำเป็น
D6: Single Point of Failure (SPOF) Mitigation
Single host = SPOF risk
Mitigation:
- Regular backup ของ database และ file storage (QNAP)
- Disaster recovery plan สำหรับ hardware failure
- พิจารณา cold standby หรือ failover strategy ในอนาคต
📋 Implementation Tasks
| Task ID | Phase | Summary | Status |
|---|---|---|---|
| T001 | Provision | Install Docker + Docker Compose on new host | Pending |
| T002 | Provision | Mount CIFS share from ASUSTOR to /mnt/uploads |
Pending |
| T003 | Deploy | Create docker-compose.yml for new host topology |
Pending |
| T004 | Deploy | Configure internal bridge network (dms-internal) |
Pending |
| T005 | Deploy | Deploy services (Ollama, sidecar, backend, frontend, Redis, DB, ES) | Pending |
| T006 | Migrate | Migrate MariaDB data from QNAP to new host | Pending |
| T007 | Migrate | Migrate Elasticsearch indices from QNAP to new host | Pending |
| T008 | Cutover | Update DNS/load balancer to point to new host | Pending |
| T009 | Cutover | Run smoke tests on new host | Pending |
| T010 | ADR-040 | Remove X-API-Key from sidecar + backend (ADR-040 D5) |
Pending |
| T011 | Cleanup | Stop services on QNAP (QNAP becomes backup server) | Pending |
| T012 | Cleanup | Retire Desk-5439 | Pending |
📋 Target docker-compose Layout (Draft)
version: '3.8'
networks:
dms-internal:
driver: bridge
dms-frontend:
driver: bridge
services:
# GPU Services (internal-only, no LAN publish)
ollama:
image: ollama/ollama:latest
container_name: lcbp3-ollama
restart: unless-stopped
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
volumes:
- ollama_models:/root/.ollama
networks:
- dms-internal
expose:
- "11434"
environment:
- OLLAMA_KEEP_ALIVE=-1
ocr-sidecar:
build:
context: ./specs/04-Infrastructure-OPS/04-00-docker-compose/New-Host/ocr-sidecar
container_name: lcbp3-ocr-sidecar
restart: unless-stopped
volumes:
- asustor_uploads:/mnt/uploads:ro # Read-only CIFS mount from ASUSTOR
networks:
- dms-internal
expose:
- "8765"
depends_on:
- ollama
environment:
- OLLAMA_API_URL=http://ollama:11434
- OCR_SIDECAR_UPLOAD_BASE=/mnt/uploads
# Backend Services (internal-only)
backend:
build:
context: ./backend
container_name: lcbp3-backend
restart: unless-stopped
volumes:
- asustor_uploads:/app/uploads:ro
networks:
- dms-internal
- dms-frontend
expose:
- "3000"
depends_on:
- ollama
- ocr-sidecar
- redis
- mariadb
- elasticsearch
environment:
- OCR_API_URL=http://ocr-sidecar:8765
- OLLAMA_API_URL=http://ollama:11434
# Frontend (LAN publish)
frontend:
build:
context: ./frontend
container_name: lcbp3-frontend
restart: unless-stopped
networks:
- dms-frontend
ports:
- "3000:3000"
depends_on:
- backend
# Data Services
redis:
image: redis:7-alpine
container_name: lcbp3-redis
restart: unless-stopped
networks:
- dms-internal
volumes:
- redis_data:/data
mariadb:
image: mariadb:10.11
container_name: lcbp3-mariadb
restart: unless-stopped
networks:
- dms-internal
volumes:
- mariadb_data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_DATABASE=lcbp3
elasticsearch:
image: elasticsearch:8.11.0
container_name: lcbp3-elasticsearch
restart: unless-stopped
networks:
- dms-internal
volumes:
- es_data:/usr/share/elasticsearch/data
environment:
- discovery.type=single-node
- xpack.security.enabled=false
volumes:
ollama_models:
asustor_uploads:
driver: local
driver_opts:
type: cifs
o: "username=${ASUSTOR_USER},password=${ASUSTOR_PASS},vers=3.0,uid=0,gid=0"
device: "//192.168.10.9/np-dms-as/data/uploads"
redis_data:
mariadb_data:
es_data:
📋 Consequences
Positive
- Simplified Architecture: Single host → easier deployment, monitoring, troubleshooting
- True Network Isolation: Docker-internal bridge enables ADR-040 D5 (network-only auth)
- Reduced Latency: Backend ↔ Ollama ผ่าน localhost
- Better Resource Utilization: Single host มีทั้ง CPU, RAM, GPU
- Data Separation Maintained: ASUSTOR เป็น Primary NAS → data แยกจาก compute; QNAP เป็น backup server
Negative
- SPOF Risk: Single host = single point of failure
- RAM Pressure: 32GB ต้องรองรับ services ทั้งหมด + CPU-fallback tensors
- Migration Complexity: ต้อง migrate DB + ES + file paths
- GPU VRAM Pressure: 16GB ต้องอาศัย adaptive residency + CPU fallback
🔄 Rollback Plan
- Stop services บน new host
- Restore services บน QNAP (backend, frontend, Redis, DB, ES)
- Restore services บน Desk-5439 (Ollama, sidecar)
- Revert DNS/load balancer ไป QNAP
- Update CIFS mount กลับไป ASUSTOR (192.168.10.9) บน QNAP
- Restore
X-API-Keyใน sidecar + backend (ADR-040 rollback)
📝 Verification Plan
- Smoke tests บน new host:
- Backend health check
- Frontend accessible via LAN
- OCR endpoint functional
- AI inference functional
- File upload/download via CIFS
- Monitor RAM/VRAM usage 24–48 hours หลัง cutover
- Verify ADR-040 D5 (network-only auth) ทำงานได้จริง
- Verify ADR-040 D3/D4 (adaptive residency + CPU fallback) ทำงานได้จริง