infra: migrate application from Mac mini to GPU server
- Integrate ollama + ai-gateway into root docker-compose.yml (NVIDIA GPU runtime, single compose for all services) - Change NAS mount from SMB (NAS_SMB_PATH) to NFS (NAS_NFS_PATH) Default: /mnt/nas/Document_Server (fstab registered on GPU server) - Update config.yaml AI endpoints: primary → Mac mini MLX via Tailscale (100.76.254.116:8800) fallback/embedding/vision/rerank → ollama (same Docker network) gateway → ai-gateway (same Docker network) - Update credentials.env.example (remove GPU_SERVER_IP, add NFS path) - Mark gpu-server/docker-compose.yml as deprecated - Update CLAUDE.md network diagram and AI model config - Update architecture.md, deploy.md, devlog.md for GPU server as main - Caddyfile: auto_https off, HTTP only (TLS at upstream proxy) - Caddy port: 127.0.0.1:8080:80 (localhost only) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
46
CLAUDE.md
46
CLAUDE.md
@@ -27,25 +27,26 @@ Mac mini M4 Pro를 애플리케이션 서버, Synology NAS를 파일 저장소,
|
||||
## 네트워크 환경
|
||||
|
||||
```
|
||||
Mac mini M4 Pro (애플리케이션 서버):
|
||||
- Docker Compose: FastAPI(:8000), PostgreSQL(:5432), kordoc(:3100), Caddy(:80,:443)
|
||||
- MLX Server: http://localhost:8800/v1/chat/completions (Qwen3.5-35B-A3B)
|
||||
- 외부 접근: pkm.hyungi.net (Caddy 프록시)
|
||||
GPU 서버 (RTX 4070 Ti Super, Ubuntu, 메인 서버):
|
||||
- Docker Compose: FastAPI(:8000), PostgreSQL(:5432), kordoc(:3100),
|
||||
Caddy(:8080 HTTP only), Ollama(:11434), AI Gateway(:8081), frontend(:3000)
|
||||
- NFS 마운트: /mnt/nas/Document_Server → NAS /volume4/Document_Server
|
||||
- 외부 접근: document.hyungi.net (앞단 프록시 → Caddy)
|
||||
- 로컬 IP: 192.168.1.186
|
||||
|
||||
Mac mini M4 Pro (AI 서버):
|
||||
- MLX Server: http://100.76.254.116:8800/v1/chat/completions (Qwen3.5-35B-A3B)
|
||||
- Tailscale IP: 100.76.254.116
|
||||
|
||||
Synology NAS (DS1525+):
|
||||
- 도메인: ds1525.hyungi.net
|
||||
- Tailscale IP: 100.101.79.37
|
||||
- 포트: 15001
|
||||
- 파일 원본: /volume4/Document_Server/PKM/
|
||||
- NFS export → GPU 서버
|
||||
- Synology Office: 문서 편집/미리보기
|
||||
- Synology Calendar: CalDAV 태스크 관리 (OmniFocus 대체)
|
||||
- MailPlus: IMAP(993) + SMTP(465)
|
||||
|
||||
GPU 서버 (RTX 4070 Ti Super):
|
||||
- AI Gateway: http://gpu-server:8080 (모델 라우팅, 폴백, 비용 제어)
|
||||
- nomic-embed-text: 벡터 임베딩
|
||||
- Qwen2.5-VL-7B: 이미지/도면 OCR
|
||||
- bge-reranker-v2-m3: RAG 리랭킹
|
||||
```
|
||||
|
||||
## 인증 정보
|
||||
@@ -57,22 +58,22 @@ GPU 서버 (RTX 4070 Ti Super):
|
||||
## AI 모델 구성
|
||||
|
||||
```
|
||||
Primary (Mac mini MLX, 상시, 무료):
|
||||
Primary (Mac mini MLX, Tailscale 경유, 상시, 무료):
|
||||
mlx-community/Qwen3.5-35B-A3B-4bit — 분류, 태그, 요약
|
||||
→ http://localhost:8800/v1/chat/completions
|
||||
→ http://100.76.254.116:8800/v1/chat/completions
|
||||
|
||||
Fallback (GPU Ollama, MLX 장애 시):
|
||||
Fallback (GPU Ollama, 같은 Docker 네트워크, MLX 장애 시):
|
||||
qwen3.5:35b-a3b
|
||||
→ http://gpu-server:11434/v1/chat/completions
|
||||
→ http://ollama:11434/v1/chat/completions
|
||||
|
||||
Premium (Claude API, 종량제, 수동 트리거만):
|
||||
claude-sonnet — 복잡한 분석, 장문 처리
|
||||
→ 일일 한도 $5, require_explicit_trigger: true
|
||||
|
||||
Embedding (GPU 서버 전용):
|
||||
nomic-embed-text → 벡터 임베딩
|
||||
Qwen2.5-VL-7B → 이미지/도면 OCR
|
||||
bge-reranker-v2-m3 → RAG 리랭킹
|
||||
Embedding (GPU Ollama, 같은 Docker 네트워크):
|
||||
nomic-embed-text → 벡터 임베딩 → http://ollama:11434/api/embeddings
|
||||
Qwen2.5-VL-7B → 이미지/도면 OCR → http://ollama:11434/api/generate
|
||||
bge-reranker-v2-m3 → RAG 리랭킹 → http://ollama:11434/api/rerank
|
||||
```
|
||||
|
||||
## 프로젝트 구조
|
||||
@@ -120,21 +121,16 @@ hyungi_Document_Server/
|
||||
## 개발/배포 워크플로우
|
||||
|
||||
```
|
||||
MacBook Pro (개발) → Gitea push → 서버에서 pull
|
||||
MacBook Pro (개발) → Gitea push → GPU 서버에서 pull
|
||||
|
||||
개발:
|
||||
cd ~/Documents/code/hyungi_Document_Server/
|
||||
# 코드 작성 → git commit & push
|
||||
|
||||
Mac mini 배포:
|
||||
GPU 서버 배포 (메인):
|
||||
cd ~/Documents/code/hyungi_Document_Server/
|
||||
git pull
|
||||
docker compose up -d
|
||||
|
||||
GPU 서버 배포:
|
||||
cd ~/Documents/code/hyungi_Document_Server/gpu-server/
|
||||
git pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## v1 코드 참조
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
:80 {
|
||||
{
|
||||
auto_https off
|
||||
}
|
||||
|
||||
http://document.hyungi.net {
|
||||
encode gzip
|
||||
|
||||
# API + 문서 → FastAPI
|
||||
@@ -25,7 +29,7 @@
|
||||
}
|
||||
|
||||
# Synology Office 프록시
|
||||
office.hyungi.net {
|
||||
http://office.hyungi.net {
|
||||
reverse_proxy https://ds1525.hyungi.net:5001 {
|
||||
header_up Host {upstream_hostport}
|
||||
transport http {
|
||||
|
||||
@@ -24,7 +24,7 @@ Self-hosted 개인 지식관리(PKM) 웹 애플리케이션
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
git clone https://git.hyungi.net/hyungi/hyungi_document_server.git
|
||||
git clone https://git.hyungi.net/hyungi/hyungi_document_server.git hyungi_Document_Server
|
||||
cd hyungi_Document_Server
|
||||
|
||||
# 인증 정보 설정
|
||||
|
||||
12
config.yaml
12
config.yaml
@@ -2,17 +2,17 @@
|
||||
|
||||
ai:
|
||||
gateway:
|
||||
endpoint: "http://gpu-server:8080"
|
||||
endpoint: "http://ai-gateway:8080"
|
||||
|
||||
models:
|
||||
primary:
|
||||
endpoint: "http://host.docker.internal:8800/v1/chat/completions"
|
||||
endpoint: "http://100.76.254.116:8800/v1/chat/completions"
|
||||
model: "mlx-community/Qwen3.5-35B-A3B-4bit"
|
||||
max_tokens: 4096
|
||||
timeout: 60
|
||||
|
||||
fallback:
|
||||
endpoint: "http://gpu-server:11434/v1/chat/completions"
|
||||
endpoint: "http://ollama:11434/v1/chat/completions"
|
||||
model: "qwen3.5:35b-a3b"
|
||||
max_tokens: 4096
|
||||
timeout: 120
|
||||
@@ -25,15 +25,15 @@ ai:
|
||||
require_explicit_trigger: true
|
||||
|
||||
embedding:
|
||||
endpoint: "http://gpu-server:11434/api/embeddings"
|
||||
endpoint: "http://ollama:11434/api/embeddings"
|
||||
model: "nomic-embed-text"
|
||||
|
||||
vision:
|
||||
endpoint: "http://gpu-server:11434/api/generate"
|
||||
endpoint: "http://ollama:11434/api/generate"
|
||||
model: "Qwen2.5-VL-7B"
|
||||
|
||||
rerank:
|
||||
endpoint: "http://gpu-server:11434/api/rerank"
|
||||
endpoint: "http://ollama:11434/api/rerank"
|
||||
model: "bge-reranker-v2-m3"
|
||||
|
||||
nas:
|
||||
|
||||
@@ -10,22 +10,18 @@ POSTGRES_DB=pkm
|
||||
POSTGRES_USER=pkm
|
||||
POSTGRES_PASSWORD=
|
||||
|
||||
# ─── AI: Mac mini MLX (Qwen3.5, 기본 모델) ───
|
||||
MLX_ENDPOINT=http://localhost:8800/v1/chat/completions
|
||||
# ─── AI: Mac mini MLX (Tailscale 경유, Qwen3.5 기본 모델) ───
|
||||
MLX_ENDPOINT=http://100.76.254.116:8800/v1/chat/completions
|
||||
MLX_MODEL=mlx-community/Qwen3.5-35B-A3B-4bit
|
||||
|
||||
# ─── AI: GPU 서버 ───
|
||||
GPU_SERVER_IP=
|
||||
GPU_EMBED_PORT=11434
|
||||
|
||||
# ─── AI: Claude API (종량제, 복잡한 분석 전용) ───
|
||||
CLAUDE_API_KEY=
|
||||
|
||||
# ─── AI Gateway (GPU 서버) ───
|
||||
AI_GATEWAY_ENDPOINT=http://gpu-server:8080
|
||||
# ─── AI Gateway (같은 Docker 네트워크) ───
|
||||
AI_GATEWAY_ENDPOINT=http://ai-gateway:8080
|
||||
|
||||
# ─── Synology NAS ───
|
||||
NAS_SMB_PATH=/Volumes/Document_Server
|
||||
# ─── NAS (NFS 마운트) ───
|
||||
NAS_NFS_PATH=/mnt/nas/Document_Server
|
||||
NAS_DOMAIN=ds1525.hyungi.net
|
||||
NAS_TAILSCALE_IP=100.101.79.37
|
||||
NAS_PORT=15001
|
||||
@@ -51,7 +47,3 @@ TOTP_SECRET=
|
||||
|
||||
# ─── 국가법령정보센터 (법령 모니터링) ───
|
||||
LAW_OC=
|
||||
|
||||
# ─── TKSafety API (나중에 활성화) ───
|
||||
#TKSAFETY_HOST=tksafety.technicalkorea.net
|
||||
#TKSAFETY_PORT=
|
||||
|
||||
@@ -22,7 +22,7 @@ services:
|
||||
ports:
|
||||
- "3100:3100"
|
||||
volumes:
|
||||
- ${NAS_SMB_PATH:-/Volumes/Document_Server}:/documents:ro
|
||||
- ${NAS_NFS_PATH:-/mnt/nas/Document_Server}:/documents:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "fetch('http://localhost:3100/health').then(r=>{process.exit(r.ok?0:1)}).catch(()=>process.exit(1))"]
|
||||
interval: 10s
|
||||
@@ -30,12 +30,40 @@ services:
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
|
||||
ollama:
|
||||
image: ollama/ollama
|
||||
volumes:
|
||||
- ollama_data:/root/.ollama
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [gpu]
|
||||
ports:
|
||||
- "11434:11434"
|
||||
restart: unless-stopped
|
||||
|
||||
ai-gateway:
|
||||
build: ./gpu-server/services/ai-gateway
|
||||
ports:
|
||||
- "8081:8080"
|
||||
environment:
|
||||
- PRIMARY_ENDPOINT=http://100.76.254.116:8800/v1/chat/completions
|
||||
- FALLBACK_ENDPOINT=http://ollama:11434/v1/chat/completions
|
||||
- CLAUDE_API_KEY=${CLAUDE_API_KEY:-}
|
||||
- DAILY_BUDGET_USD=${DAILY_BUDGET_USD:-5.00}
|
||||
depends_on:
|
||||
- ollama
|
||||
restart: unless-stopped
|
||||
|
||||
fastapi:
|
||||
build: ./app
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ${NAS_SMB_PATH:-/Volumes/Document_Server}:/documents
|
||||
- ${NAS_NFS_PATH:-/mnt/nas/Document_Server}:/documents
|
||||
- ./config.yaml:/app/config.yaml:ro
|
||||
- ./scripts:/app/scripts:ro
|
||||
- ./logs:/app/logs
|
||||
@@ -62,8 +90,7 @@ services:
|
||||
caddy:
|
||||
image: caddy:2
|
||||
ports:
|
||||
- "8080:80"
|
||||
- "9443:443"
|
||||
- "127.0.0.1:8080:80"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||
- caddy_data:/data
|
||||
@@ -75,3 +102,4 @@ services:
|
||||
volumes:
|
||||
pgdata:
|
||||
caddy_data:
|
||||
ollama_data:
|
||||
|
||||
@@ -542,7 +542,7 @@ POST /to-hwpx
|
||||
### Caddy 설정 예시
|
||||
|
||||
```
|
||||
pkm.hyungi.net {
|
||||
document.hyungi.net {
|
||||
reverse_proxy localhost:8000 # FastAPI
|
||||
}
|
||||
|
||||
@@ -931,7 +931,7 @@ pkm-web/
|
||||
│ └── migrate_from_devonthink.py ← v1 → v2 마이그레이션 스크립트
|
||||
│
|
||||
├── docs/
|
||||
│ ├── architecture-v2.md ← 이 문서
|
||||
│ ├── architecture.md ← 이 문서
|
||||
│ └── deploy.md ← 배포 가이드
|
||||
│
|
||||
└── tests/
|
||||
|
||||
@@ -65,7 +65,7 @@ curl http://localhost:3100/health
|
||||
### 2-6. 외부 접근 (Caddy)
|
||||
|
||||
Caddy가 자동으로 HTTPS 인증서를 발급한다.
|
||||
- `pkm.hyungi.net` → FastAPI (:8000)
|
||||
- `document.hyungi.net` → FastAPI (:8000)
|
||||
- `office.hyungi.net` → Synology Office (NAS 프록시)
|
||||
|
||||
DNS 레코드가 Mac mini의 공인 IP를 가리켜야 한다.
|
||||
|
||||
181
docs/devlog.md
Normal file
181
docs/devlog.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# 개발 로그
|
||||
|
||||
## 2026-04-02 — v2 전환 설계 완료
|
||||
|
||||
### 결정 사항
|
||||
- DEVONthink 탈피 결정. v1의 구조적 한계(AppleScript 취약성, macOS GUI 의존, 13개 DB 복잡성)를 더 이상 감수하지 않기로 함
|
||||
- 자체 웹앱 방향 확정. 기술 스택: FastAPI + PostgreSQL/pgvector + SvelteKit + Docker
|
||||
- OmniFocus 탈피 → Synology Calendar (CalDAV VTODO)로 대체
|
||||
- Synology 서비스 활용 극대화: Office(문서 편집/미리보기), Drive(파일 관리), Calendar(태스크), MailPlus(이메일+알림)
|
||||
- Document_Server 전체를 Synology Drive가 관리. PKM 하위 폴더로 자동분류 영역 분리
|
||||
- 문서 "원본" 정의 확정: immutable(PDF, 수신 HWP 등) / editable(Synology Office 포맷) / note(Markdown)
|
||||
- .docx/.xlsx는 교환 형식으로 취급. 서버에 영구 보관하지 않음
|
||||
- 데이터 3계층: 원본(NAS) → 가공(PostgreSQL) → 파생(pgvector+캐시)
|
||||
- kordoc 통합 결정 (HWP/HWPX/PDF → Markdown 파싱, Node.js 마이크로서비스)
|
||||
- AI 전략: Qwen3.5-35B-A3B(MLX) 우선, Claude API는 종량제로 최후 수단. GPU 서버에 AI Gateway 배치
|
||||
- Anthropic 약관 확인: 구독 OAuth의 서드파티 사용은 약관상 금지(2026.01~). 자동화에는 API 키만 사용
|
||||
- NanoClaw는 선택적 확장(대화형 인터페이스)으로 위치, 핵심 파이프라인 비의존
|
||||
- 장기 로드맵: GPU 서버 확장 → 메인 서버 승격, Mac mini → Roon Core 전용, Synology 장기 유지
|
||||
|
||||
### 산출물
|
||||
- `docs/architecture-v2.md` — 17개 섹션 + 부록 2개 (전체 시스템 설계)
|
||||
- 마이그레이션 계획서 — Step 1~5 (프로젝트 리네임+정리) + Phase 0~5 (v2 개발)
|
||||
- 프로젝트 리네임: DEVONThink_my server → hyungi_Document_Server
|
||||
|
||||
### 배경 논의 (Cowork 세션)
|
||||
- v1에서 16개 커밋 중 절반 이상이 AppleScript 버그 수정이었던 점이 전환의 직접적 계기
|
||||
- Synology Office iframe 임베드로 DEVONthink 미리보기 대체 가능성 논의
|
||||
- HWP 대응으로 kordoc(광진구청 류승인 주무관 제작, MIT 라이선스) 조사 및 채택
|
||||
- 편집 가능 문서의 "원본이 뭐냐" 논의 → Synology Office 포맷이 원본, .docx/.xlsx는 교환용
|
||||
- 가공 데이터 보관 전략 논의 → 파일로 저장하지 않고 PostgreSQL에만 저장, 버전 추적으로 선택적 재가공
|
||||
|
||||
## 2026-04-02 — 프로젝트 리네임 + v2 전환 실행
|
||||
|
||||
### Step 1: 사전 정리 ✅
|
||||
- architecture-v2.md 커밋 (`852b7da`)
|
||||
- v1-archive 브랜치 + v1-final 태그 생성 (v1 상태 완벽 보존)
|
||||
|
||||
### Step 2: v1 파일 정리 ✅
|
||||
- v1 전용 파일 git rm 완료 (`e48b6a2`)
|
||||
- 삭제: applescript/, launchd/, v1 scripts, v1 docs, tests/test_classify.py, requirements.txt
|
||||
- 유지: scripts/prompts/classify_document.txt, credentials.env.example (v2 필드로 갱신)
|
||||
|
||||
### Step 3: Gitea 리포 리네임 + 로컬 폴더 리네임 ✅
|
||||
- Gitea: devonthink_home → hyungi_document_server
|
||||
- 로컬 폴더: DEVONThink_my server → hyungi_Document_Server
|
||||
- git remote set-url + git ls-remote 검증 + push 완료
|
||||
|
||||
### Step 4: 문서 전면 재작성 ✅
|
||||
- CLAUDE.md — v2 기준으로 전면 재작성
|
||||
- README.md — 프로젝트명, 기술 스택, 디렉토리 구조 갱신
|
||||
- docs/deploy.md — Docker Compose 기반 배포 가이드로 교체
|
||||
- docs/claude-code-commands.md → docs/development-stages.md 변환
|
||||
- docs/architecture-v2.md → docs/architecture.md 승격
|
||||
|
||||
### Step 5: v2 프로젝트 스캐폴딩 ✅
|
||||
- 전체 디렉토리 구조 생성 (app/, services/kordoc/, gpu-server/, frontend/, migrations/, tests/)
|
||||
- 동작하는 최소 코드 수준: FastAPI main.py, PostgreSQL 스키마, kordoc server.js, config.yaml 등
|
||||
- docker-compose.yml, Caddyfile, credentials.env.example 생성
|
||||
- tests/__init__.py + conftest.py 포함
|
||||
|
||||
### Step 1~5 전체 완료.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-02 — Phase 0: 기반 구축 시작
|
||||
|
||||
### users 테이블 + ORM 모델 추가 ✅
|
||||
- `migrations/001_initial_schema.sql`에 users 테이블 포함 (username, password_hash, totp_secret, is_active)
|
||||
- `app/models/user.py` — SQLAlchemy 2.0 Mapped 스타일 ORM 모델
|
||||
- architecture.md 섹션 6 스키마와 일치
|
||||
|
||||
### Auth API 엔드포인트 구현 ✅
|
||||
- `app/api/auth.py` — 4개 엔드포인트: POST /login (JWT발급+TOTP), POST /refresh, GET /me, POST /change-password
|
||||
- `app/core/auth.py` — bcrypt 비밀번호 해싱, JWT 발급/검증, TOTP 검증, get_current_user 의존성
|
||||
- Pydantic 스키마: LoginRequest, TokenResponse, RefreshRequest, ChangePasswordRequest, UserResponse
|
||||
|
||||
### main.py 라우터 등록 + health 강화 ✅
|
||||
- auth 라우터 등록: `/api/auth` prefix
|
||||
- health 엔드포인트에 DB 연결 상태 포함 (connected/disconnected)
|
||||
- lifespan 핸들러로 DB 초기화/정리
|
||||
### Docker 설정 수정 ✅
|
||||
### 초기 admin 유저 시드 스크립트 ✅
|
||||
|
||||
### 셋업 위자드 구현 ✅ (`a601991`)
|
||||
- `app/api/setup.py` — 6개 엔드포인트: GET /status, POST /admin, POST /totp/init, POST /totp/verify, POST /verify-nas, GET / (HTML)
|
||||
- `app/templates/setup.html` — Jinja2 단일 HTML, Vanilla JS + qrcode.js CDN, 3단계 위자드
|
||||
- `app/main.py` — setup 라우터 등록 + 셋업 미들웨어 (유저 0명 시 /setup 리다이렉트, /health /docs 등 바이패스)
|
||||
- Rate Limiting: IP당 5분 내 5회 실패 시 차단
|
||||
- TOTP 흐름: init에서 secret 반환(DB 미저장) → verify에서 코드 검증 후 DB 저장
|
||||
- scripts/seed_admin.py CLI 백업 수단 유지
|
||||
- requirements.txt에 jinja2 추가
|
||||
|
||||
### Phase 0 완료 기준 달성 상태
|
||||
- ✅ docker compose up → FastAPI 구동
|
||||
- ✅ DB 스키마 (users, documents, tasks, processing_queue)
|
||||
- ✅ JWT + TOTP 인증 (로그인, 토큰 갱신, 비밀번호 변경)
|
||||
- ✅ 셋업 위자드 (관리자 생성 + TOTP + NAS 확인)
|
||||
- ✅ /health — DB 연결 상태 포함
|
||||
- ✅ /docs — OpenAPI 문서
|
||||
- ⬜ NAS SMB 마운트 실제 검증 (Mac mini 배포 시)
|
||||
- ⬜ config.yaml 로딩 검증 (Mac mini 배포 시)
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-02 — Phase 1: 데이터 마이그레이션 파이프라인 구현 완료
|
||||
|
||||
### Step 1: kordoc /parse 실제 구현 ✅
|
||||
- `services/kordoc/server.js` — stub → 실제 파싱 구현 (kordoc ^1.7.0 + pdfjs-dist ^4.0.0)
|
||||
- HWP/HWPX/PDF → Markdown 변환, .md/.txt 직접 읽기, 이미지는 requires_ocr 플래그 반환
|
||||
- 타임아웃 30초, 파일 미존재 404, 파싱 실패 422
|
||||
|
||||
### Step 2: 큐 소비자 인프라 ✅
|
||||
- `app/workers/queue_consumer.py` — APScheduler AsyncIOScheduler 1분 간격 실행
|
||||
- 배치 처리: extract=5, classify=3, embed=1
|
||||
- stage 체이닝: extract → classify → embed 자동 enqueue
|
||||
- stale 항목 자동 복구 (processing 상태 10분 초과)
|
||||
- `app/main.py` — lifespan에 APScheduler 연결, yield 후 shutdown 보장
|
||||
|
||||
### Step 3: 텍스트 추출 워커 ✅
|
||||
- `app/workers/extract_worker.py` — 포맷별 분기 처리
|
||||
- KORDOC_FORMATS (hwp, hwpx, pdf) → kordoc HTTP POST
|
||||
- TEXT_FORMATS (md, txt, csv, json, xml, html) → 직접 파일 읽기
|
||||
- IMAGE_FORMATS → Phase 2 OCR로 연기
|
||||
|
||||
### Step 4: AI 분류 워커 ✅
|
||||
- `app/workers/classify_worker.py` — extracted_text 8000자 → AIClient.classify() 호출
|
||||
- `app/ai/client.py` — strip_thinking(), parse_json_response() 추가 (v1 pkm_utils.py에서 포팅)
|
||||
- Qwen3.5의 <think> 태그 제거 + 비정형 JSON 파싱 로직
|
||||
|
||||
### Step 5: 벡터 임베딩 워커 ✅
|
||||
- `app/workers/embed_worker.py` — nomic-embed-text-v1.5 (GPU 서버), 6000자 제한
|
||||
- GPU 서버 불가 시 graceful fail → 재시도
|
||||
|
||||
### Step 6: DEVONthink 마이그레이션 스크립트 ✅
|
||||
- `scripts/migrate_from_devonthink.py` — --dry-run, --source-dir, --target-dir, --database-url 지원
|
||||
- DEVONthink 내보내기 → NAS PKM 구조 복사 + documents/processing_queue DB 등록
|
||||
|
||||
### Phase 1 완료 기준 달성 상태
|
||||
- ✅ kordoc 파싱 (HWP/HWPX/PDF → Markdown)
|
||||
- ✅ 큐 소비자 + APScheduler 연동
|
||||
- ✅ extract → classify → embed 워커 3개
|
||||
- ✅ AI 클라이언트 think 태그 / JSON 파싱 보강
|
||||
- ✅ 마이그레이션 스크립트
|
||||
- ⬜ Step 7: 통합 테스트 + 배치 실행 (Mac mini 배포 후)
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-02 — Phase 2: 핵심 기능 구현 완료 (`4b69533`)
|
||||
|
||||
### 문서 CRUD API ✅
|
||||
- `app/api/documents.py` — 5개 엔드포인트
|
||||
- POST /api/documents/ — 파일 업로드 (Inbox 저장 + extract 큐 등록)
|
||||
- GET /api/documents/ — 목록 조회 (페이징 + domain/source/format 필터)
|
||||
- GET /api/documents/{id} — 단건 조회
|
||||
- PATCH /api/documents/{id} — 메타데이터 수동 수정
|
||||
- DELETE /api/documents/{id} — DB 삭제 (기본), ?delete_file=true로 파일도 삭제
|
||||
|
||||
### 하이브리드 검색 API ✅
|
||||
- `app/api/search.py` — GET /api/search/?q={query}&mode={mode}
|
||||
- 4가지 모드: fts, trgm, vector, hybrid (기본)
|
||||
- hybrid 가중치: FTS 0.4 + Trigram 0.2 + Vector 0.4
|
||||
- 벡터 불가 시 FTS 0.6 + Trigram 0.4 폴백
|
||||
- 결과에 snippet(200자) 포함
|
||||
|
||||
### 파일 워처 ✅
|
||||
- `app/workers/file_watcher.py` — Inbox 디렉토리 5분 간격 스캔
|
||||
- 신규 파일: Document 생성 + extract 큐 등록
|
||||
- 변경 파일: 해시 비교 → 재추출 큐 등록
|
||||
- .DS_Store, .tmp, .part 등 무시 파일 처리
|
||||
|
||||
### 벡터 인덱스 마이그레이션 ✅
|
||||
- `migrations/002_vector_index.sql` — IVFFlat 인덱스 (cosine, lists=50)
|
||||
- 문서 수 증가 시 lists 값 조정 필요
|
||||
|
||||
### Phase 2 완료 기준 달성 상태
|
||||
- ✅ 문서 CRUD API (업로드, 목록, 조회, 수정, 삭제)
|
||||
- ✅ 하이브리드 검색 (FTS + Trigram + Vector)
|
||||
- ✅ Inbox 파일 워처 (신규/변경 자동 감지 → 파이프라인 등록)
|
||||
- ✅ 처리 파이프라인 전체 동작 (upload/watch → extract → classify → embed → search)
|
||||
- ⬜ 문서 뷰어 UI (Phase 4로 이관)
|
||||
- ⬜ SvelteKit 프론트엔드 (Phase 4로 이관)
|
||||
174
docs/gpu-migration-plan.md
Normal file
174
docs/gpu-migration-plan.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# GPU 서버 이전 + NFS 전환 — Claude Code 작업 지시서
|
||||
|
||||
## 배경
|
||||
|
||||
Phase 0~4 완료. 현재 Mac mini에서 Docker 전체 구동 중.
|
||||
GPU 서버(Ubuntu, RTX 4070 Ti Super)로 애플리케이션 이전.
|
||||
NAS NFS 마운트로 Synology Drive 데드락 해결.
|
||||
|
||||
## 완료된 수동 작업
|
||||
|
||||
- ✅ Step 1: NAS NFS 서버 설정 (DSM에서 NFS 활성화, NFSv4.1)
|
||||
- ✅ Step 2: GPU 서버 NFS 마운트 (`/mnt/nas/Document_Server`, fstab 등록 완료)
|
||||
- ✅ Step 6: Mac mini MLX 서버 외부 접근 확인 (100.76.254.116:8800 응답 확인)
|
||||
|
||||
## 확정된 정보
|
||||
|
||||
- Mac mini Tailscale IP: `100.76.254.116`
|
||||
- NAS 로컬 IP: `192.168.1.227`
|
||||
- GPU 서버 로컬 IP: `192.168.1.186`
|
||||
- NFS 마운트 경로: `/mnt/nas/Document_Server`
|
||||
- MLX 모델: `mlx-community/Qwen3.5-35B-A3B-4bit` (Mac mini에서 계속 서빙)
|
||||
|
||||
## 목표 구조
|
||||
|
||||
```
|
||||
GPU 서버 (Ubuntu, 메인 서버):
|
||||
Docker Compose 단일 파일:
|
||||
- postgres, fastapi, kordoc-service, frontend, caddy
|
||||
- ollama (NVIDIA GPU), ai-gateway
|
||||
NFS → NAS /volume4/Document_Server (/mnt/nas/Document_Server)
|
||||
|
||||
Mac mini M4 Pro (AI 서버만):
|
||||
MLX Server: http://100.76.254.116:8800 (Qwen3.5-35B-A3B)
|
||||
|
||||
NAS DS1525+ (파일 저장소):
|
||||
NFS export → GPU 서버
|
||||
Synology Office/Calendar/MailPlus 유지
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Claude Code 작업 목록
|
||||
|
||||
### 작업 1: docker-compose.yml 통합
|
||||
|
||||
현재 루트 `docker-compose.yml` (Mac mini용)에 `gpu-server/docker-compose.yml`의 서비스를 통합.
|
||||
|
||||
변경 사항:
|
||||
- `version: '3.8'` 제거 (Docker Compose V2 기준)
|
||||
- NAS 볼륨 변수: `NAS_SMB_PATH` → `NAS_NFS_PATH`, 기본값 `/mnt/nas/Document_Server`
|
||||
- Ollama 서비스 추가 (NVIDIA GPU runtime, ollama_data 볼륨)
|
||||
- AI Gateway 서비스 추가 (Ollama depends_on)
|
||||
- AI Gateway 환경변수: PRIMARY_ENDPOINT=http://100.76.254.116:8800/v1/chat/completions
|
||||
- Caddy 포트: `127.0.0.1:8080:80` 유지 (HTTPS는 앞단 프록시(UCG-Fiber)에서 처리, Caddy는 HTTP only)
|
||||
- ollama_data 볼륨 추가
|
||||
|
||||
참고 — 현재 파일:
|
||||
- 루트 docker-compose.yml: postgres, kordoc-service, fastapi, frontend, caddy
|
||||
- gpu-server/docker-compose.yml: ollama, ai-gateway
|
||||
|
||||
### 작업 2: config.yaml AI 엔드포인트 변경
|
||||
|
||||
현재 → 변경:
|
||||
|
||||
```yaml
|
||||
ai:
|
||||
gateway:
|
||||
endpoint: "http://ai-gateway:8080" # gpu-server → ai-gateway (같은 Docker 네트워크)
|
||||
|
||||
models:
|
||||
primary:
|
||||
endpoint: "http://100.76.254.116:8800/v1/chat/completions" # host.docker.internal → Mac mini Tailscale IP
|
||||
# 나머지 동일
|
||||
|
||||
fallback:
|
||||
endpoint: "http://ollama:11434/v1/chat/completions" # gpu-server → ollama (같은 Docker 네트워크)
|
||||
# 나머지 동일
|
||||
|
||||
embedding:
|
||||
endpoint: "http://ollama:11434/api/embeddings" # gpu-server → ollama
|
||||
|
||||
vision:
|
||||
endpoint: "http://ollama:11434/api/generate" # gpu-server → ollama
|
||||
|
||||
rerank:
|
||||
endpoint: "http://ollama:11434/api/rerank" # gpu-server → ollama
|
||||
```
|
||||
|
||||
핵심: `gpu-server` 호스트명이 전부 `ollama` 또는 `ai-gateway`로 변경 (같은 Docker 네트워크).
|
||||
primary만 Mac mini Tailscale IP `100.76.254.116`으로 외부 호출.
|
||||
|
||||
### 작업 3: credentials.env.example 갱신
|
||||
|
||||
변경 사항:
|
||||
- `NAS_SMB_PATH` → `NAS_NFS_PATH=/mnt/nas/Document_Server`
|
||||
- `MLX_ENDPOINT` → `http://100.76.254.116:8800/v1/chat/completions`
|
||||
- `GPU_SERVER_IP` 항목 제거 (로컬이 됨)
|
||||
- `AI_GATEWAY_ENDPOINT` → `http://ai-gateway:8080` (같은 Docker 네트워크)
|
||||
- 주석 업데이트: "Mac mini MLX" → "Mac mini MLX (Tailscale 경유)"
|
||||
|
||||
### 작업 4: Caddyfile 확인
|
||||
|
||||
변경 불필요. 현재 상태 유지:
|
||||
- `auto_https off` + `http://document.hyungi.net` (HTTPS는 앞단 프록시 UCG-Fiber에서 처리)
|
||||
- Caddy 포트: `127.0.0.1:8080:80` (localhost 바인딩, 443 불필요)
|
||||
|
||||
### 작업 5: 문서 업데이트
|
||||
|
||||
#### CLAUDE.md — 네트워크 환경 섹션 갱신
|
||||
|
||||
현재:
|
||||
```
|
||||
Mac mini M4 Pro (애플리케이션 서버):
|
||||
- Docker Compose: FastAPI(:8000), PostgreSQL(:5432), kordoc(:3100), Caddy(:80,:443)
|
||||
- MLX Server: http://localhost:8800/v1/chat/completions (Qwen3.5-35B-A3B)
|
||||
- 외부 접근: document.hyungi.net (Caddy 프록시)
|
||||
```
|
||||
|
||||
변경:
|
||||
```
|
||||
GPU 서버 (RTX 4070 Ti Super, Ubuntu, 메인 서버):
|
||||
- Docker Compose: FastAPI(:8000), PostgreSQL(:5432), kordoc(:3100), Caddy(:8080, HTTP only), Ollama(:11434), AI Gateway(:8080), frontend(:3000)
|
||||
- NFS 마운트: /mnt/nas/Document_Server → NAS /volume4/Document_Server
|
||||
- 외부 접근: document.hyungi.net (Caddy 프록시)
|
||||
|
||||
Mac mini M4 Pro (AI 서버):
|
||||
- MLX Server: http://100.76.254.116:8800/v1/chat/completions (Qwen3.5-35B-A3B)
|
||||
- Roon Core
|
||||
```
|
||||
|
||||
GPU 서버 Tailscale IP도 추가. AI 모델 구성 섹션에서 primary endpoint 변경 반영.
|
||||
|
||||
#### docs/architecture.md — 섹션 3 (인프라 역할 분담) 갱신
|
||||
|
||||
Mac mini가 애플리케이션 서버 → GPU 서버가 메인 서버로 변경.
|
||||
Mac mini는 AI 서버(MLX)만 담당하는 것으로 변경.
|
||||
아스키 다이어그램 업데이트.
|
||||
|
||||
#### docs/deploy.md — GPU 서버 기준 배포 가이드로 변경
|
||||
|
||||
- 전제조건: NFS 마운트 (/mnt/nas/Document_Server)
|
||||
- clone 경로, docker compose 명령 등 GPU 서버 기준으로 변경
|
||||
- pg_dump/pg_restore 마이그레이션 절차 추가
|
||||
|
||||
#### docs/devlog.md — GPU 이전 기록 추가
|
||||
|
||||
Phase 1~2는 이미 기록됨. 아래 추가:
|
||||
|
||||
1. Phase 3 완료 기록 (자동화 이전: law_monitor, mailplus_archive, daily_digest, automation_state, APScheduler cron) — 기록 안 되어 있으면 추가
|
||||
2. Phase 4 완료 기록 (SvelteKit UI: 로그인, 대시보드, 문서 탐색/검색, Inbox, 설정, Docker 통합) — 기록 안 되어 있으면 추가
|
||||
3. GPU 서버 이전 기록 (NFS 전환, docker-compose 통합, AI 엔드포인트 변경, Caddy HTTP only 구조)
|
||||
|
||||
### 작업 6: gpu-server/docker-compose.yml 비활성화
|
||||
|
||||
- 파일 상단에 주석 추가: "# 이 파일은 더 이상 사용하지 않음. 루트 docker-compose.yml로 통합됨."
|
||||
- 또는 gpu-server/docker-compose.yml.bak으로 리네임
|
||||
|
||||
---
|
||||
|
||||
## 작업 순서 (추천)
|
||||
|
||||
1. docker-compose.yml 통합 (작업 1)
|
||||
2. config.yaml 변경 (작업 2)
|
||||
3. credentials.env.example 갱신 (작업 3)
|
||||
4. gpu-server/docker-compose.yml 비활성화 (작업 6)
|
||||
5. 문서 업데이트 (작업 5) — CLAUDE.md, architecture.md, deploy.md, devlog.md
|
||||
6. Caddyfile 확인 (작업 4)
|
||||
|
||||
## 주의사항
|
||||
|
||||
- credentials.env 자체는 git에 올리지 않음 (.gitignore). example만 수정.
|
||||
- Mac mini Tailscale IP `100.76.254.116`은 config.yaml에 직접 기입 (credentials.env에서 변수로 관리해도 됨)
|
||||
- NAS 경로: Docker 컨테이너 내부에서는 `/documents`로 접근 (기존과 동일)
|
||||
- GPU 서버 로컬 IP `192.168.1.186`은 NFS 마운트에만 사용, Docker 설정에는 불필요
|
||||
@@ -1,4 +1,7 @@
|
||||
version: '3.8'
|
||||
# ═══════════════════════════════════════════════════
|
||||
# 이 파일은 더 이상 사용하지 않음.
|
||||
# 루트 docker-compose.yml로 통합됨 (2026-04-03).
|
||||
# ═══════════════════════════════════════════════════
|
||||
|
||||
services:
|
||||
ollama:
|
||||
|
||||
Reference in New Issue
Block a user