# Vignette 배포 가이드 — "아무 데나 설치 가능한" Docker 구성 목표: 어떤 호스트(네 PC / 학교 서버 / 클라우드 VM)든 `docker compose up -d` 한 방으로 뜨고, **옮길 때 환경변수 몇 줄만** 바꾼다. ## 1. 한 방 실행 ```bash cd infra cp .env.example .env # 값 채우기(아래 표) docker compose up -d # web/api/db/rag/proxy 컨테이너 기동. http://localhost:8080 ``` 전부 컨테이너: `web`(React/nginx) · `api`(FastAPI) · `db`(postgres16+pgvector) · `rag`(BGE-M3+reranker) · `proxy`(Caddy, TLS·SSE·WSS). ## 2. 핵심 — "엔진"만 컨테이너 밖 (이식성의 열쇠) 우리 엔진은 로컬 `claude -p`(네 PC의 claude CLI/OAuth에 묶임)라, 컨테이너에 가두면 못 옮긴다. 그래서 **엔진은 어댑터 뒤에 두고 `ENGINE_URL`/`ENGINE_MODE`로 가리킨다.** 옮길 때 이 두 줄만 바꾸면 됨 — 코드 무수정. 설정 경로 **3가지(사용자 선택)**: 1. **yml / .env** — `infra/.env`의 `ENGINE_URL`·`ENGINE_MODE` (기본·운영) 2. **docker compose** — `docker compose run -e ENGINE_MODE=messages_api ...` 즉석 오버라이드 3. **설정 페이지(관리자 UI)** — 런타임에 엔진/모델 전환 (DB에 저장, 재기동 불필요) | 시나리오 | ENGINE_MODE | ENGINE_URL | |---|---|---| | **네 PC 운영** (요구사항: 로컬 claude -p) | `claude_p` | `http://host.docker.internal:9099` (호스트의 claude -p 게이트웨이) | | **클라우드/타호스트 이전** | `messages_api` | (불필요, ANTHROPIC_API_KEY 사용) | | **원격 엔진 PC** | `claude_p` | `http://<엔진PC-IP>:9099` | > 로컬 claude -p 게이트웨이 = 호스트에서 도는 작은 프로세스(상주 멀티턴 풀, `claude -p --input-format stream-json`). 컨테이너 api 가 `host.docker.internal:9099`로 호출. 이 게이트웨이는 `apps/api/engine_gateway/`에 둔다(호스트 실행, 컨테이너 밖). ## 3. 환경변수 (.env) | 변수 | 설명 | |---|---| | `POSTGRES_PASSWORD` | DB 비밀번호(필수) | | `SESSION_SECRET` | 세션 서명키(필수, 랜덤) | | `AUTH_ALLOWED_EMAIL_DOMAINS` | Google 로그인 허용 이메일 도메인(JSON 배열). 기본 `["hs.ac.kr","twentyoz.kr"]`. Google Console authorized domains가 아니라 서버에서 `email`/`hd` claim으로 강제 | | `OPENAI_API_KEY` | 음성(STT/TTS) | | `ANTHROPIC_API_KEY` | 엔진 폴백/클라우드 모드 | | `ENGINE_MODE` / `ENGINE_URL` | 엔진 위치(위 표) | | `SITE_ADDRESS` | 외부노출 도메인(예: `vignette.chanpaca.net`). 비우면 로컬 :80 | | `ACME_EMAIL` | 외부노출 시 Let's Encrypt 자동 TLS용 | | `HTTP_PORT`/`HTTPS_PORT` | 호스트 포트 매핑 | ## 4. 외부 공유 (chanpaca.net, 교수 테스트) 두 가지 — 호스트 상황에 맞게: - **Caddy 자동 TLS**: 공인 IP·도메인 있으면 `SITE_ADDRESS=vignette.chanpaca.net` + `ACME_EMAIL` → Caddy가 인증서 자동 발급. - **Cloudflare/Tailscale 터널**: 공인 IP 없을 때(네 PC). 단 **음성 SSE/WSS는 버퍼링 충돌**이 있어 Caddyfile에서 `flush_interval -1`로 흘려보냄(red team F-07 반영). 터널도 SSE 무버퍼 경로 분리 권장. ## 5. 스케일업 / 이전 - **수직**: compose 그대로, 호스트 사양만 키움. - **수평**: `docker compose up --scale api=3` (api 무상태 설계 전제, 세션은 DB/Redis). proxy가 라운드로빈. - **이전**: `pgdata`·`ragmodels` 볼륨만 새 호스트로 옮기고 `.env` 채워 `up`. (rag 모델 캐시 볼륨 덕에 재다운로드 0) - **클라우드**: 동일 compose. 단 엔진을 `messages_api`로 바꾸거나 엔진 PC를 `ENGINE_URL`로 원격 지정. ## 6. 데이터 주권 / 보안 - DB는 NAS PostgreSQL(국내). 미성년 사례데이터는 repo·이미지에 절대 미포함(`.gitignore`). - 외부 LLM 전송 전 PII 마스킹 미들웨어 통과(red team F-03). - proxy 인증 게이트 뒤에서만 SSE/WSS 노출(무인증 IDOR 차단, F-20). > 현 단계: 구성 골격(compose·Dockerfile·Caddy) 완비. 앱 코드(apps/web, apps/api) 채워지면 그대로 빌드된다.