# Storage Layout — Document Server **버전**: 2026-05-11 (Storage PR-1 read-only inventory 결과 반영) **역할 분리 원칙**: `feedback_knowledge_in_db_blob_in_nas.md` — 지식·검색 대상 = DB, binary·source = NAS. **Plan 정합**: `~/.claude/plans/beszel-tingly-sloth.md` v6 의 Storage Backbone 트랙. ## 전제 - DB 본체 (Postgres) = GPU 서버 잔류 (CPU/RAM 우위, 결정 라운드 9). - NAS = bulk binary / source / 변경 적은 자료 backbone. - AI 호출은 GPU/맥미니로 라우팅 (Document Server 와 별 채널). ## 머신 / 마운트 토폴로지 (2026-05-11 실측) | 머신 | 역할 | IP | 디스크 | |---|---|---|---| | GPU 서버 (RTX 4070 Ti Super, Ubuntu) | Docker 호스트 — fastapi / postgres / worker / Caddy / frontend | LAN 192.168.1.186 / Tailscale 100.111.160.84 | rootfs 936G / 182G used / 715G free | | DS1525+ (Synology) | NFS export + Hyper Backup + DR Tier 1 | LAN 192.168.1.227 / Tailscale 100.101.79.37 | `/volume4` 3.5TB / 401GB used / 3.1TB free | | Mac mini M4 Pro | MLX inference (Document Server 외부) | LAN 192.168.1.x / Tailscale 100.76.254.116 | (Document Server storage 영역 0) | GPU 서버의 NFS mount (`/proc/mounts` 실측): ``` 192.168.1.227:/volume4/Document_Server /mnt/nas/Document_Server nfs4 rw,relatime,vers=4.1,rsize=1048576,wsize=1048576, soft,proto=tcp,timeo=10,retrans=3,_netdev ``` **의미**: - 이미 **soft mount + timeo=10 + retrans=3** 적용 중 → plan v6 Storage PR-2 의 "실측 후 mount 전략 결정" 의 *현 baseline 이 곧 권고안*. 변경 필요 X. - vers=4.1, NFSv4 (NFSv3 아님 → grace period / lock 처리 OK). - LAN 직결 (Tailscale 경유 아님) — D10 (DS1525+ Tailscale TCP 차단) 영향 없음. ## NAS volume 안 Document Server layout `/mnt/nas/Document_Server/` (= NAS `/volume4/Document_Server/`): | 경로 | 용도 | 크기 | 변경 빈도 | 분류 | |---|---|---|---|---| | `PKM/` | 사용자 컨텐츠 원본 (knowledge + 입력 inbox + references) | **38GB** | 일 단위 | 이미 NAS ✓ | | `PKM/Knowledge/` | 정리된 지식 자료 (압력용기, ASME, 가스, 등) | 31GB | 일 단위 | 이미 NAS ✓ | | `PKM/References/` | 레퍼런스 PDF | 5.5GB | 주 단위 | 이미 NAS ✓ | | `PKM/Inbox/` | 업로드 진입 (drag-drop / file_watcher) | 1.5GB | 시간 단위 | 이미 NAS ✓ | | `PKM/.preview/` | LibreOffice PDF 변환 cache | (소량, 6 entry) | 신규 업로드 시 | 이미 NAS ✓ (검토: GPU 로 이동 가치 작음) | | `PKM/Recordings/` | 음성 녹음 | 20KB | 거의 X | 이미 NAS ✓ | | `PKM/Videos/` | 비디오 | 116KB | 거의 X | 이미 NAS ✓ | | `PKM/Archive/` | 아카이브 | 0 | — | 이미 NAS ✓ | | `Main/` | 별 디렉토리 (의미 확인 필요) | 14GB | ? | 이미 NAS ✓ | | `extracted_images/` | Markdown Phase 1B.5 추출 이미지 (`document_images`) | 1.2MB | 변환 시 | 이미 NAS ✓ | | `study_question_images/` | 가스기사 문제 첨부 이미지 (PR-8) | 12MB | 회차 업로드 시 | 이미 NAS ✓ | | `프로그래밍 전 참고 자료/` | (소량) | 56KB | 거의 X | 이미 NAS ✓ | **결론**: NAS 의 binary/source layer 는 **이미 잘 구성되어 있음**. 추가 이관 후보 (Storage PR-3/4 candidate) 는 사실상 없음 — 단, **PR-4 MailPlus ingest** 도입 시 `extracted_emails/` 신규 디렉토리 추가 예정. ## GPU 서버 local storage (Postgres + worker 모델 + 로그) | 경로 / Volume | 용도 | 크기 | 분류 | |---|---|---|---| | Postgres data dir (`pgdata` named volume) | DB 본체 | **1.1GB** | GPU 잔류 (plan 결정) | | `hyungi_document_server_ollama_data` | Ollama 모델 weight (embedding / 분류 / 임베딩 등) | **11.9GB** | GPU 잔류 (GPU 인접 필수) | | `hyungi_document_server_stt_models` | STT (faster-whisper 등) 모델 | 3.0GB | GPU 잔류 | | `hyungi_document_server_reranker_cache` | bge-reranker-v2-m3 cache | 2.1GB | GPU 잔류 | | `hyungi_document_server_ocr_models` | OCR 모델 | 1.4GB | GPU 잔류 | | `hyungi_document_server_marker_models` | marker-pdf cache | 4KB (외부 HF cache 사용 추정) | GPU 잔류 | | `hyungi_document_server_caddy_data` | Caddy ACME / log | (작음) | GPU 잔류 | | `~/Documents/code/hyungi_Document_Server/logs/` | fastapi 로그 (rotation) | 21MB | GPU 잔류 | **합계**: AI 모델 캐시 ~18.5GB + Postgres 1.1GB + 로그/Caddy < 100MB ≈ GPU local 20GB 사용 중. ## 컨테이너 마운트 매트릭스 (`docker-compose.yml` 발췌) | 컨테이너 | 마운트 | 모드 | 비고 | |---|---|---|---| | postgres | `pgdata:/var/lib/postgresql/data` + `./migrations:/docker-entrypoint-initdb.d` | rw | DB 본체 named volume | | kordoc-service | `${NAS}/Document_Server:/documents` | **ro** | PDF/HWP parse | | ocr-service | `${NAS}/Document_Server:/documents` + `ocr_models:/root/.cache` | **ro** + rw | | | marker-service | `${NAS}/Document_Server:/documents` + `marker_models:/models` | **ro** + rw | PDF→markdown | | stt-service | `${NAS}/Document_Server:/documents` + `stt_models:/root/.cache` | **ro** + rw | | | ai-gateway / ollama | `ollama_data:/root/.ollama` | rw | GPU 인접 | | reranker | `reranker_cache:/data` | rw | | | fastapi | `${NAS}/Document_Server:/documents` + `./config.yaml ./domain_policy.yaml ./scripts ./logs ./migrations` | **rw** + ro/rw | NAS 쓰기 권한은 fastapi 만 | | caddy | `./Caddyfile + caddy_data:/data` | rw | | **관찰**: - worker 컨테이너 (kordoc/ocr/marker/stt) 는 모두 NAS **read-only** 마운트 → 원본 안전. - fastapi 만 NAS **rw** → 업로드/preview/extracted_images 쓰기 단일 책임. - `./migrations` 이 postgres 의 `docker-entrypoint-initdb.d` 와 fastapi 의 `/app/migrations` 양쪽에 마운트. 단 실제 migration runner 는 fastapi `init_db()` 만 사용 (postgres init scripts 는 첫 생성 시만 실행 → 효과 X, 안전). ## 정책 정리 ### NAS 에 두는 것 (binary / source / 변경 적음) - 사용자 컨텐츠 원본 (`PKM/*`) - 변환 cache (`PKM/.preview/`, `extracted_images/`) - 학습 자료 첨부 (`study_question_images/`) - 향후: `extracted_emails/` (PR-4) ### GPU 서버 local 에 두는 것 (latency 민감 / hot path / 큰 caching) - Postgres data dir (DB 본체) - HNSW index, pgvector index (Postgres 내부) - AI 모델 weight cache (embedding / OCR / STT / reranker / marker / ollama) - worker 임시 캐시 (각 컨테이너 named volume) - 로그 (`./logs`) ### 명확하게 어디에 두지 않음 - 검색 인덱스: Postgres 안 (pgvector + pg_trgm) — NAS X - chunk text: Postgres 안 (`document_chunks.text_content`) — NAS X - 메모 본문: Postgres 안 (`documents.extracted_text`) — NAS X - AI 분류 결과: Postgres 안 (`documents.ai_domain/ai_tags`) — NAS X ## NFS mount 옵션 권고 (Storage PR-2 baseline) 현 GPU 서버 mount = 이미 권고안과 같음: ``` 192.168.1.227:/volume4/Document_Server /mnt/nas/Document_Server nfs4 rw,relatime,vers=4.1,rsize=1048576,wsize=1048576, soft,proto=tcp,timeo=10,retrans=3,_netdev ``` **근거**: - `soft` — NAS down 시 process 영구 hang 회피 (plan v6 Storage PR-2 핵심 요구). 단 쓰기 도중 부분 실패 위험 → fastapi 의 `upload` / `extract` / `marker` worker 가 fsync + rename(atomic) + DB row 작성 ordering 필수 (코드 검토는 PR-2 영역). - `timeo=10/retrans=3` — 1초 단위 timeout × 3회. 30초 안 NAS 응답 없으면 EIO. 운영 1년+ 안정 (별 incident 없음). - `vers=4.1` — NFSv3 의 lock daemon 불필요, idempotent. - `_netdev` — boot 시 network 이후 마운트. ### Storage PR-2 시 점검 항목 (read-only PR-1 scope 외) - 한글 NFC/NFD 정규화 wrapper (`feedback_nfs_korean_path_normalize.md`) — 기존 `document_images` 경로 처리 코드 일반화 - uid/gid 매핑 (Synology user ↔ Docker container user) - NAS 장애 시뮬레이션 gate (운영 mount 아닌 isolated test mount 부터) - 쓰기 경로 fsync + rename(atomic) + 실패 명시 처리 ## 백업 정책 - NAS = Synology Hyper Backup (DR Tier 1, 별 트랙 `project_db_backup.md` 참조) - Postgres = pkm DB → Tier 0 GPU + Tier 1 DS1525+ Vol3 + Tier 2 C2 Hyper Backup (별 트랙) - AI 모델 캐시 = 백업 불필요 (재다운로드 가능) ## 다음 단계 - Storage PR-2 (NFS 표준화 + 정규화 wrapper + 장애 시뮬 gate) — 운영 변경 동반 → 별 PR - Storage PR-3/4 (원본 이관) — **현 상태 이미 NAS, 추가 작업 0** - Storage PR-5 (events `raw_metadata` 첨부 = extracted_emails) — events PR-4 (MailPlus ingest) 와 합류