Files
hyungi_document_server/docs/storage_layout.md
T
hyungi d030a2b7b0 fix(deploy): fresh-DB/DR 부팅 — postgres initdb.d 마운트 제거
빈 볼륨 첫 기동 시 postgres 가 migrations/*.sql 을 psql autocommit 으로 실행해
스키마는 만들되 schema_migrations 스탬프를 안 남김 → fastapi init_db 가 documents
존재로 'fresh' 오판해 baseline 로드를 건너뛰고 001 부터 재replay → CREATE TABLE
users(IF NOT EXISTS 없음) 충돌 → DR/신규환경 부팅 크래시.

fresh-boot 을 init_db 의 baseline + migration runner 단일 경로로 일원화.
기존 prod 볼륨은 비어있지 않아 init scripts 미발동 = 무영향. 관련 docs 정정.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 13:22:36 +09:00

143 lines
8.6 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` | rw | DB 본체 named volume (initdb.d 마운트는 2026-06-29 제거 — 아래 관찰) |
| 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` 은 fastapi 의 `/app/migrations` 에만 마운트. migration runner 는 fastapi `init_db()` 단일 경로. (~2026-06-29: postgres `docker-entrypoint-initdb.d` 마운트 제거. 기존엔 "첫 생성 시만 실행 → 효과 X" 로 봤으나, 빈 볼륨 첫 기동 시 postgres 가 migrations/*.sql 을 실제 실행해 스키마는 만들되 schema_migrations 스탬프를 안 남겨 → init_db 의 baseline fresh 판정을 깨고 부팅 크래시 유발. fresh-DB/DR 부팅을 init_db+baseline 단일 경로로 일원화.)
## 정책 정리
### 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) 와 합류