vignette/docs/DEPLOYMENT.md
2026-06-27 11:20:24 +09:00

4.8 KiB

Vignette 배포 가이드 — "아무 데나 설치 가능한" Docker 구성

목표: 어떤 호스트(네 PC / 학교 서버 / 클라우드 VM)든 docker compose up -d 한 방으로 뜨고, 옮길 때 환경변수 몇 줄만 바꾼다.

1. 한 방 실행

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 / .envinfra/.envENGINE_URL·ENGINE_MODE (기본·운영)
  2. docker composedocker 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으로 강제
FRONTEND_ORIGIN_MAP API callback host → frontend origin 매핑(JSON 객체). 기본 api-vignette.chanpaca.net → vignette.chanpaca.net, api-vnet.18ka.net → vnet.18ka.net
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가 인증서 자동 발급.

4.1 vnet.18ka.net 공개 경로

vnet.18ka.net을 공개 Google OAuth로 쓰려면 세 조건이 같이 맞아야 한다.

  1. DNS: vnet.18ka.net은 웹, api-vnet.18ka.net은 API로 라우팅되어야 한다.
  2. API 설정: CORS_ORIGINShttps://vnet.18ka.net, FRONTEND_ORIGIN_MAPapi-vnet.18ka.net=https://vnet.18ka.net이 있어야 한다.
  3. Google Cloud Console: Authorized redirect URI에 https://api-vnet.18ka.net/auth/callback을 추가해야 한다.

현재 이 저장소의 코드와 scripts/start-public-runtime.ps1은 2번을 회귀 테스트와 기본값으로 고정한다. 1번과 3번은 외부 콘솔/도메인 소유자 설정이다.

  • 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) 채워지면 그대로 빌드된다.