OpsMate Local 공개 데모 운영 Runbook
문서 상태
- 상태: 운영 자산
implemented, 실제 양 호스트 rehearsalunverified - 목적: 필요한 기간에만 서비스를 열고, 개발·검증 뒤 안전하게 닫았다가 같은 artifact로 다시 여는 절차
- 기본 상태:
CLOSED - 공개 금지: credential, host/IP, 내부 URL, VPN 정보와 승인 문서 원문
현재 저장소에는 앱·모델 호스트의 open, normal close, emergency close와 closed verification 스크립트가 있습니다. 2026-08-04 전체 clean verify는 54개 테스트 성공, 실패·오류·건너뜀 0개였습니다. 그러나 승인된 실제 모델 호스트, 공개 URL과 양 호스트를 사용한 전체 rehearsal은 아직 수행하지 않았습니다. 스크립트 존재를 실제 운영 완료로 해석하지 않습니다.
두 호스트의 책임
flowchart LR
USER["External browser"] -->|"HTTPS"| APPHOST["App host: Caddy + app + PostgreSQL"]
APPHOST -->|"Approved private path"| MODELHOST["Model host: authenticated proxy + Ollama + one GPU"]
애플리케이션 호스트와 모델 호스트는 별도 수명주기를 가집니다.
| 대상 | open | normal close | emergency close | closed 확인 |
|---|---|---|---|---|
| 애플리케이션 호스트 | deploy/open-demo.sh |
deploy/close-demo.sh |
deploy/emergency-close.sh [project] |
deploy/verify-closed.sh |
| 모델 호스트 | deploy/model-host/open-model.sh |
deploy/model-host/close-model.sh |
deploy/model-host/emergency-close.sh [project] |
deploy/model-host/verify-private.sh --closed |
앱 close만으로 GPU가 해제되지 않고, 모델 close만으로 public edge와 DB가 닫히지 않습니다. 두 호스트의 close와 verification이 모두 성공해야 전체 서비스를 CLOSED로 판정합니다.
모든 Compose service는 restart: "no"입니다. Docker daemon이나 호스트 재시작만으로 공개 서비스가 자동 복구되지 않으며, 항상 명시적인 open gate를 다시 통과해야 합니다.
비밀값과 환경 파일
각 호스트에서 예시 파일을 추적되지 않는 .env로 복사합니다.
deploy/.env.example
deploy/model-host/.env.example
환경 파일에는 다음 유형의 값이 필요하지만 실제 값은 Git, 문서, issue, CI log와 shell history에 남기지 않습니다.
- 앱 image full digest와 공개 domain
- DB admin·migration·runtime 역할과 서로 다른 비밀번호
- 승인된 private model endpoint와 임시 Bearer token
- Ollama/Caddy image full digest와 승인 모델 tag·content ID
- 선택한 한 GPU와 승인된 VPN interface
.env*는 .env.example을 제외하고 ignore합니다. backup 이름을 포함한 비밀 파일도 커밋하지 않습니다.
공개 전 필수 gate
승인된 사설 GPU 모델 호스트
- 외부 포트폴리오 트래픽 처리 목적과 기간이 명시적으로 승인됨
- GPU·VRAM·driver·NVIDIA container runtime을 실측함
- 선택 모델의 license와 사용 범위를 검토함
- 모델 proxy는 승인된 private/VPN IPv4에만 bind함
- Ollama 자체 host port는 노출하지 않음
- Ollama image full digest, 모델 tag와 실제 content ID를 고정함
- 한 GPU만 선택하고 다른 workload를 변경하지 않음
모델 open-model.sh는 승인 flag가 정확히 YES인지 Docker, GPU, network 또는 model 작업보다 먼저 확인합니다. 승인이 없으면 여기서 중단하고 유료 API나 다른 모델로 자동 전환하지 않습니다.
애플리케이션과 외부 네트워크
- 같은 소스에서 빌드·검증한 앱 image full digest를 registry에서 사용할 수 있음
- DB admin·migration·runtime 역할과 비밀번호가 서로 분리됨
- public domain, DNS와 ACME 조건이 준비됨
- app host firewall/egress가 승인 모델 목적지만 허용한다는 증거를 보관함
- edge/WAF가 익명 요청 rate limit을 적용한다는 증거를 보관함
- 최신
clean verify, container build와 구성 검사가 통과함
open-demo.sh는 egress allowlist와 edge rate limit의 증거 flag가 모두 YES가 아니면 실패합니다. flag는 실제 정책의 대체물이 아니므로 운영자가 외부 구성을 별도로 확인해야 합니다.
모델 호스트 열기
모델 호스트에서 실행합니다.
./deploy/model-host/open-model.sh
스크립트는 다음 순서를 지킵니다.
- 승인 flag, 필수 명령, Docker/NVIDIA runtime과 환경값을 검사합니다.
- VPN interface에 승인한 private IPv4가 실제로 할당됐는지 확인합니다.
- GPU selector가 정확히 한 GPU를 가리키는지
nvidia-smi로 확인합니다. - Ollama·Caddy image가 full SHA-256 digest인지 검사합니다.
- 모델에 명시적 tag와 승인 content ID가 있는지 검사합니다.
- 기존 proxy를 먼저 멈춰 검증 중 endpoint 노출을 막습니다.
- 선택한 한 GPU로 Ollama를 시작하고 승인 모델을 준비합니다.
- 실제 model inventory의 content ID가 승인값과 같은지 확인합니다.
- VPN-bound 인증 proxy를 시작합니다.
- private bind, token 없는 요청의
401, 인증 health200, Ollama host port 미노출과 model volume을 확인합니다.
중간 실패 시 proxy와 Ollama를 멈추고 closed verification을 시도합니다. 자동 정리가 완전하지 않으면 앱 호스트를 열지 말고 모델 호스트를 먼저 점검합니다.
애플리케이션 호스트 열기
모델 호스트 검증 뒤 애플리케이션 호스트에서 실행합니다.
./deploy/open-demo.sh
preflight는 다음을 확인합니다.
- 앱 image가 full SHA-256 digest로 고정됨
- DB 역할 이름이 안전한 서로 다른 식별자이고 비밀번호 길이 기준을 만족함
- model proxy token이 길이·문자 기준을 만족함
- model base URL은 경로가 없는 승인된 private/VPN IPv4 한 개와 port로 구성됨
- allowed hosts는 해당 IPv4 하나와 정확히 같음
- private model health가 Bearer 인증으로 성공함
- host egress와 edge rate limit 증거 flag가
YES임 - Compose 구성이 유효함
시작 순서는 다음과 같습니다.
- 기존 public Caddy를 중단합니다.
- PostgreSQL을 시작하고 health를 기다립니다.
- 검증한 immutable 앱 image digest를 pull·inspect합니다.
- 같은 image의 one-shot
migrate가 Flyway를 완료합니다. - migration credential이 없는 runtime 앱을 시작하고 readiness를 기다립니다.
- Caddy를 시작합니다.
- 외부 HTTPS smoke test를 실행합니다.
open 중 실패하면 public edge와 앱을 멈추고, 쓰기 주체가 실제로 중단된 경우에만 합성 workspace 삭제를 시도한 뒤 DB를 멈춥니다. 자동 정리 실패 메시지가 나오면 재시도 전에 emergency close와 수동 상태 확인을 수행합니다.
공개 smoke 기준
deploy/smoke-test.sh는 실제 HTTPS와 실제 모델 경로에서 다음을 확인합니다.
- public root 응답과
X-OpsMate-Demo: livemarker - HTTP→HTTPS redirect
- 공개
/api/**의 정확한403과 Basic challenge 부재 - XSRF/JSESSIONID의
Secure,HttpOnly,SameSite=Lax /demo/sessions로 합성 workspace 시작/demo/drafts에서 실제 모델 기반 서버 검증 초안 생성/demo/requests/{id}/submit/demo/requests/{id}/decisions/demo/orders- AUDITOR 화면의
ORDER_CREATED /demo/end를 통한 smoke workspace 삭제
이 smoke가 성공하지 않으면 공개 open은 완료된 것이 아닙니다. 서로 다른 두 외부 세션의 cross-workspace 격리, 외부 DB/model port 차단과 모바일 네트워크 확인은 별도 운영 검수로 추가합니다.
정상 애플리케이션 호스트 닫기
애플리케이션 호스트에서 실행합니다.
./deploy/close-demo.sh
실제 순서는 다음과 같습니다.
- public Caddy를 먼저 중단합니다.
- 앱을 graceful shutdown으로 중단해 신규 쓰기와 진행 중 트랜잭션을 끝냅니다.
- Caddy와 앱이 모두 실제로 중단됐는지 확인합니다.
- PostgreSQL을 시작하거나 유지하고 admin 역할로
demo_workspaces를 truncate cascade 합니다. - 남은 workspace 수가
0인지 확인합니다. - one-shot migration service와 PostgreSQL을 중단합니다.
- app, migrate, DB와 live Caddy가 모두 중단됐고 PostgreSQL volume이 남아 있는지
verify-closed.sh로 확인합니다.
앱보다 먼저 합성 데이터를 삭제하면 늦게 완료되는 트랜잭션과 경합할 수 있으므로 이 순서를 바꾸지 않습니다. 삭제 검증이 실패해도 서비스 중단은 유지하고, 원인을 해결하기 전에는 reopen하지 않습니다.
정상 모델 호스트 닫기
모델 호스트에서 별도로 실행합니다.
./deploy/model-host/close-model.sh
스크립트는 VPN-bound proxy를 먼저 멈추고 Ollama를 멈춥니다. 이후 proxy·Ollama container 중단, 11434 listener 부재, 승인 private endpoint의 접근 불가와 model volume 보존을 확인합니다. 이 절차가 끝나야 데모용 GPU가 해제됩니다.
앱 close와 모델 close 중 하나만 실행한 상태를 전체 CLOSED라고 기록하지 않습니다.
환경 파일 없이 긴급 닫기
credential 노출, 환경 파일 손실 또는 정상 Compose 명령 실패 시 emergency close를 사용합니다.
./deploy/emergency-close.sh opsmate-demo
./deploy/model-host/emergency-close.sh opsmate-model-host
두 스크립트는 .env를 읽지 않습니다. 안전한 project 이름을 검사하고 Docker Compose label로 정확한 service container만 찾은 뒤 다음 순서로 중단합니다.
- 앱 호스트: Caddy → app → migrate → DB
- 모델 호스트: proxy → Ollama, 가능한 경우 11434 listener 부재 확인
emergency close는 volume을 삭제하지 않습니다. 앱 호스트에서는 DB credential이 없으므로 합성 workspace도 purge하지 않습니다. 환경을 복구한 뒤 정상 close를 실행해 합성 데이터 삭제와 closed verification을 완료해야 합니다.
같은 artifact로 다시 열기
reopen은 이전 session을 복구하는 작업이 아니라 새 공개 기간을 여는 작업입니다.
- 사설 GPU 모델 호스트 승인과 사용 기간이 아직 유효한지 확인합니다.
- 이전에 승인한 Ollama image digest, 모델 tag·content ID와 GPU 조건을 확인합니다.
- 이전 검증에서 기록한 앱 image의 정확한 full digest를
.env에 둡니다. - emergency close 뒤라면 정상 close로 합성 workspace 삭제를 먼저 확인합니다.
- 모델 호스트의
open-model.sh와 private verification을 실행합니다. - 앱 호스트의
open-demo.sh를 실행해 migration, readiness와 전체 HTTPS smoke를 다시 통과합니다. - 외부 모바일 네트워크에서 전체 흐름과 외부 DB/model port 차단을 확인합니다.
Compose는 tag가 아니라 OPSMATE_APP_IMAGE=...@sha256:<digest>를 요구합니다. reopen 시 앱 digest가 달라지면 same-artifact reopen이 아니며 새 release 검증이 필요합니다. 모델 content ID, driver, migration 또는 주요 dependency가 달라져도 이전 E2E 결과를 재사용하지 않습니다.
장애 대응
모델 장애
- 모델에 의존하는 초안 결과를 저장하지 않고 해당 생성 경로를 fail-closed 상태로 유지합니다. 이미 제출된 요청의 승인·반려·발주는 모델과 분리되어 있지만, 공개 데모 전체의 검증 경계를 보수적으로 유지하기 위해 edge도 닫습니다.
- 다른 모델이나 유료 API로 자동 fallback하지 않습니다.
- public edge를 닫고 앱·모델 호스트 close를 실행합니다.
- 복구 뒤 실제 모델 E2E와 public smoke 전에는 다시 열지 않습니다.
workspace 간 데이터 노출
- 즉시 app emergency close로 public edge와 쓰기 주체를 중단합니다.
- 모델 호스트도 닫습니다.
- 환경을 복구해 모든 합성 workspace를 삭제합니다.
- 관련 repository query와 service guard를 검토하고 cross-workspace 회귀 테스트를 추가합니다.
- 전체 검증 전에는 reopen하지 않습니다.
credential 노출
- 두 호스트를 닫고 노출된 DB, model proxy, VPN과 edge credential을 회수·교체합니다.
- Git history, image layer, artifact와 log 범위를 확인합니다.
- 파일 삭제만으로 사고 대응을 종료하지 않습니다.
승인 철회 또는 자산 변경
- 신규 공개를 중단하고 두 호스트 normal close를 실행합니다.
- 모델 호스트 credential과 승인 경로를 회수합니다.
- 사용자의 별도 결정 없이 다른 공급자나 유료 API로 전환하지 않습니다.
검증 기록 양식
공개 가능한 일반화 정보만 기록합니다.
검증 시각과 timezone:
소스 commit:
애플리케이션 image digest:
DB migration version:
공개 가능한 모델 식별자:
최신 clean verify: PASS / FAIL / PENDING
실제 모델 E2E: PASS / FAIL / NOT RUN
public HTTPS smoke: PASS / FAIL / NOT RUN
cross-workspace 격리: PASS / FAIL / NOT RUN
외부 DB/model 차단: PASS / FAIL / NOT RUN
host egress allowlist 증거: PASS / FAIL / NOT RUN
edge/WAF rate limit 증거: PASS / FAIL / NOT RUN
앱 호스트 close: PASS / FAIL / NOT RUN
모델 호스트 close: PASS / FAIL / NOT RUN
same-digest reopen: PASS / FAIL / NOT RUN
합성 workspace 삭제: PASS / FAIL / NOT RUN
알려진 제한:
host, IP, 계정, 내부 URL, VPN route, token과 승인 문서 원문은 기록하지 않습니다.
운영 완료 기준
다음 항목이 모두 충족돼야 개발·배포·검증 완료로 기록할 수 있습니다.
- 최신 자동화 테스트와 회귀 테스트의
clean verify성공 - PostgreSQL migration과 runtime 최소 권한 검증
- 승인된 실제 모델 E2E 성공
- public URL에서 전체 persona 흐름과 외부 smoke 성공
- host egress allowlist와 edge/WAF rate limit 적용 증거 확인
- 앱·모델 양쪽 호스트의 normal/emergency close rehearsal 성공
- 같은 앱 image digest와 승인 모델 content ID의 reopen 성공
- close 뒤 합성 workspace 삭제와 외부 접근 차단 확인
- 공개 문서와 evidence label이 실제 결과와 일치
현재는 배포 자산까지 구현했지만 실제 승인 모델·공개 URL·외부 정책·양 호스트 rehearsal이 남아 있습니다. 따라서 서비스가 실제로 열려 있거나 운영 검증이 끝났다고 표시하지 않습니다.