# 배포 파이프라인 (Deployment Pipeline) > 작성: 2026-08-31 · 2026-09-08 현행화 · 문서 소유: docs/ops > 상태: **2026-09-08 NAS Production & Cloudflare Pages 실배포 완료 (SHA `a0311c59`)**. ## 목표 아키텍처 (소유자 지시) ``` 이 PC(개발 워크스테이션) │ push (master) ▼ Forgejo git.chanpaca.net ────── 소스 SSOT (이관 완료 2026-08-31) │ ├─ 미러(백업) ── github.com (private 유지) [현행 origin] │ └─ 배포 ──▶ NAS Production (vignette-prod) │ docker-compose.nas.yml (SSOT-host=nas, runtime-class=production) │ Cloudflare tunnel은 소유자 지시에 따라 제거 대상이다. 다만 현재 NAS ingress가 이를 경유하므로, 대체 ingress의 보안·가용성·OAuth 경계를 검증하기 전에는 제거하지 않는다. ``` - 나는 이 PC를 **개발 전용**으로만 사용한다. 배포 대상은 반드시 **NAS Production**이다. - github.com은 **private**로 백업/미러만 유지한다 (제거 아님). - git 관리·배포 파이프라인은 **git.chanpaca.net (Forgejo)** 이 소유한다. ## 1. 저장소 (소스 SSOT) | 원격 | URL | 역할 | 상태 | |---|---|---|---| | `origin` | https://github.com/yunchan8804-blip/vignette.git | private 백업/미러 | 유지 | | `forgejo` | ssh://git@git.chanpaca.net:2222/yunchan/vignette.git | **소스 SSOT·배포** | ✅ 이관 완료 | - Forgejo 레포: `yunchan/vignette` (id 14, public, default master) - 2026-09-01 배포 당시 기준 커밋: `dce85620`. 현재 master/로컬 HEAD는 실행 전에 별도로 확인한다. - Forgejo 접근: SSH 키(`~/.ssh/id_ed25519`, Host `git.chanpaca.net` → `192.168.0.38:2222`) · API 토큰(`infra/.env.deploy`의 `FORGEJO_API_TOKEN`, gitignore) ## 2. 로컬 브랜치 규칙 - `master` = Forgejo `master`와 동기화 (배포 기준선) - 기능/scoped 브랜치 = 개발용, 검증 후 master로 합침 ## 3. NAS Production 배포 흐름 (수동 절차 실증, 자동화 구성 예정) ``` [Forgejo] 승인된 master 후보 SHA ▼ [NAS] git 컨테이너로 해당 SHA clone·검증 ▼ [NAS] API/Web build → online migration → docker compose 재생성 ▼ vignette-prod (api/web/engine/db/proxy) readiness·데이터 보존 검증 ▼ [Cloudflare] 현재 공개 서빙 경로 유지 (대체 ingress 검증 뒤 tunnel retirement) ``` NAS 호스트에는 git이 없으므로 git 컨테이너로 Forgejo에서 clone한다. 2026-09-01 수동 실배포는 이 흐름으로 `dce85620`을 검증했다. 자동배포 hook과 tunnel retirement는 별도 열린 게이트다. ## 4. 검증 게이트 (배포 전) - `apps/web`: `npm run typecheck`, `npm run build` - `apps/api`: 관련 test suite - SSOT checker `scripts/test_dev_dashboard_ssot.py` - Forgejo master == 로컬 master (배포 무결성) - NAS: `docker compose ps` healthy, `/api/health` ok·db/engine true ## 5. 실증된 배포 절차 (2026-09-01 1차 실배포 완료, SHA `dce85620`) NAS 호스트에는 git이 없으므로 git 컨테이너로 Forgejo에서 clone한다. ```sh # NAS에서 (ssh yunchan@192.168.0.38) cd /volume1/docker/vignette-prod # 0) 백업: pg_dump custom + env.nas 사본 + 구 이미지 SHA 기록 docker exec vignette-prod-db-1 pg_dump -U vignette_owner -d vignette -Fc > backups/pre-deploy--.dump cp private/env.nas private/env.nas.before-deploy- # 1) Forgejo master clone (git 컨테이너) + HEAD 검증 docker run --rm -v $PWD:/work alpine/git clone --depth 1 --branch master \ http://192.168.0.38:3000/yunchan/vignette.git /work/src- # 2) 이미지 빌드 (API 컨텍스트는 저장소 루트) cd src- && docker build -f apps/api/Dockerfile -t vignette-nas-api: . docker build --build-arg VITE_API_BASE=/api -t vignette-nas-web: apps/web # 3) 새 마이그레이션은 psql로 온라인 적용(initdb 스크립트는 재실행되지 않음), # 구 API가 의존하는 제약을 바꾸는 마이그레이션은 stop api 후 적용 # 4) private/env.nas의 VIGNETTE_NAS_API_IMAGE/WEB_IMAGE를 새 이미지 ID로 교체 docker compose -f docker-compose.nas.yml --env-file private/env.nas config --quiet docker compose -f docker-compose.nas.yml --env-file private/env.nas up -d # 5) readiness: public health ok·db/engine true, OpenAPI 200, /auth/me 401, 데이터 집계 전후 동일 ``` 공개 웹(Cloudflare Pages)은 별도 배포한다: `apps/web`에서 `npm run build` → `node ../../scripts/preserve-assets.mjs`(이전 세대 자산 보존) → `npx wrangler pages deploy dist --project-name vignette --branch main`. ## 6. 남은 구성 작업 (TODO) - [ ] Forgejo master 기준 NAS 자동배포 후크(수동 절차는 §5로 실증 완료) - [x] Forgejo가 SSOT임을 문서에 반영, github은 private 미러 유지 - [ ] cloudflare tunnel 제거 또는 유지 결정 — 현재 `api-vignette`는 NAS 호스트 상주 cloudflared 터널 경유다. 대체 ingress의 보안·가용성·OAuth 경계가 검증되기 전에는 제거하지 않는다. - [ ] 배포 시 NAS 접근 자격(SSH 키/배포 계정)을 `.env.deploy`/시크릿으로 관리 ```