Files
hyungi_document_server/docs/storage_layout.md
T
Hyungi Ahn 768fc36746 docs(storage): Storage PR-1 — read-only inventory + 정책 문서
Storage Backbone NAS 트랙의 첫 PR. plan v6 명시대로 read-only inventory PR
— 운영 변경 / mount 변경 / file_path 갱신 / asset 이동 모두 0건. 문서만.

산출물:
- docs/storage_layout.md  영구 정책 문서 (정책 / 마운트 매트릭스 / NFS 옵션 baseline)
- reports/storage_inventory_2026-05-11.md  측정 결과 snapshot

핵심 인사이트:
1. NAS binary layer 는 이미 잘 분리되어 있음 — PKM/extracted_images/
   study_question_images 모두 이미 NAS. 추가 이관 PR-3/4 작업량 거의 없음.
2. 현 GPU NFS mount = plan v6 권고안 baseline 과 정확히 같음
   (soft, vers=4.1, timeo=10, retrans=3) — PR-2 는 mount 옵션 변경 아닌
   애플리케이션 layer (정규화 wrapper / 장애 처리 / uid 매핑) 에 집중.
3. fastapi 만 NAS rw, worker 는 ro — 원본 안전 분리 OK.
4. Postgres pgdata = 1.1GB (DB 본체 이관 안 함, plan 결정 = GPU 잔류).
5. PR-4 도입 시 extracted_emails/ 신규 디렉토리 추가 예정 (Storage PR-5 합류).

실측 명령: SSH 100.111.160.84 → df/mount/du/docker volume ls/docker run
-v ... alpine du. 모두 read-only. 운영 영향 0.
2026-05-11 07:23:28 +09:00

143 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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) 와 합류