feat(runtime): 정적 프런트엔드와 격리 빌더를 구성한다

Caddy가 불변 아티팩트를 직접 제공하고 runtime과 builder의 자원·비밀 경계를 분리한다.

Gateway active release ref를 재기동에 복원하고 production/development/smoke 모델 검증을 확장한다.
This commit is contained in:
2026-08-22 09:32:54 +00:00
parent d4af23c0d7
commit 6461ee5773
18 changed files with 759 additions and 38 deletions
+60 -9
View File
@@ -67,18 +67,18 @@ Core2026 clone, frozen-lockfile install, build와 Gateway migration 때문에
`Host` header를 보존해야 합니다.
runtime은 기본적으로 memory/swap 각각 4 GiB, CPU 4개, PID 512개 상한을
가집니다. Gateway와 여러 profile의 PM2 process가 실행 중인 상태에서도 release
build의 Node/Rolldown thread를 수용하기 위한 값입니다. 호스트 용량에 맞춰
가집니다. 이 상한은 Gateway와 여러 profile의 PM2 backend process에만 적용되며
release build는 별도 builder service의 상한을 사용합니다. 호스트 용량에 맞춰
`RUNTIME_*_LIMIT`을 조정할 수 있지만 0 또는 무제한으로 두지 않습니다. PM2
process/restart 수가 예상보다 증가하면 로그 수집보다 runtime 중지가 우선입니다.
초기 빌드와 profile worktree 빌드는 기본 Node heap 1536 MiB,
`RAYON_NUM_THREADS=1` 안에서 실행됩니다. `RUNTIME_NODE_OPTIONS`의 heap 상한은
runtime Node process는 기본 heap 1536 MiB, `RAYON_NUM_THREADS=1` 안에서
실행됩니다. `RUNTIME_NODE_OPTIONS`의 heap 상한은
512~2048 MiB, `RUNTIME_RAYON_NUM_THREADS`는 1~4만 허용됩니다. 턴 데몬만 더 큰
heap이 필요하면 `TURN_DAEMON_NODE_OPTIONS`를 512~4096 MiB 범위에서 지정합니다.
이 값은 Gateway orchestrator가 생성하는 turn-daemon PM2 process에만
`NODE_OPTIONS`로 적용되며 API·frontend·worker와 build는 계속
`RUNTIME_NODE_OPTIONS`사용합니다. 큰 값을 쓰기 전에 `RUNTIME_MEMORY_LIMIT`
`NODE_OPTIONS`로 적용되며 API·worker는 계속 `RUNTIME_NODE_OPTIONS`
사용합니다. 큰 값을 쓰기 전에 `RUNTIME_MEMORY_LIMIT`
동일한 swap 상한을 함께 늘리고 실제 container 총사용량을 확인합니다.
`scripts/check.sh`는 example placeholder, 짧은 비밀값, 잘못된 domain/email,
@@ -142,7 +142,7 @@ mode 0600 secret 파일에만 둡니다.
```sh
docker compose ps
docker compose logs --tail=200 runtime caddy
docker compose logs --tail=200 builder runtime caddy
docker compose exec runtime pnpm --filter @sammo-ts/release-controller status
```
@@ -160,12 +160,63 @@ Gateway와 profile frontend의 Vite build는 `/gateway/assets/`,
재사용할 수 있습니다.
`index.html`, router fallback, `terms.*.html`처럼 URL이 고정된 HTML은 이 정책에
포함하지 않습니다. 이 응답은 Vite preview의 `Cache-Control: no-cache`와 ETag
유지하여 저장은 허용하되 사용할 때마다 변경 여부를 재검증합니다. content hash가
포함하지 않습니다. Caddy는 이 응답에 `Cache-Control: no-cache` 적용하여
저장은 허용하되 사용할 때마다 변경 여부를 재검증합니다. content hash가
없는 `/assets/` 파일과 별도 운영 자산인 `/image/*``immutable`로 취급하지
않습니다. 따라서 public directory에 장기 cache할 파일을 추가할 때는 먼저
content-hashed URL로 옮겨야 합니다.
## 정적 frontend와 격리 builder
운영 frontend는 `vite preview`로 제공하지 않습니다. Gateway와 profile build는
`frontend-artifacts` named volume의 commit/digest 기반 불변 디렉터리에 복사되고,
runtime은 검증된 release의 `current` 심볼릭 링크만 원자적으로 교체합니다. Caddy는
이 volume을 read-only로 mount하여 직접 제공합니다. 첫 전환과 명시적 개발 모드를
위한 preview reverse-proxy fallback은 남아 있지만 운영 PM2에는 Vite process가
없습니다.
`builder` service는 Core source/worktree volume만 공유하고 release build를 한 번에
하나씩 실행합니다. DB, Redis, game token, OAuth 비밀값과 Docker socket은 받지
않으며, runtime은 공개 `VITE_*`와 heap/thread/cache 설정만 build 요청에
전달합니다. Migration, profile seed, Redis mutation, PM2 전환, readiness와 release
상태 게시는 계속 runtime이 소유합니다.
release 순서는 builder build → 불변 frontend stage → migration/seed → backend PM2
전환 → `current` 활성화 → API/Caddy readiness → 상태 게시입니다. 실패하면 이전
backend와 artifact를 함께 복구합니다. `DEPLOY`는 DB와 현 시즌을 보존하며,
`RESET`은 명시적으로 시나리오를 초기화한 뒤 같은 선택 commit의 artifact를
게시합니다. `STOPPED`와 가오픈 전 `RESERVED``current`를 제거하여 오래된 SPA가
계속 노출되지 않게 합니다.
성공한 Gateway release는 Core clone의 `refs/sammo/active-gateway`도 compare-and-swap으로
갱신합니다. Runtime container가 재생성되면 tracked 변경이 없는지 확인한 뒤 이 ref를
detached checkout하여, release worktree에서 성공한 UPDATE가 예전 bootstrap HEAD로
되돌아가지 않게 합니다. Ref가 아직 없는 최초 설치만 `CORE_BOOTSTRAP_REF` checkout을
사용합니다. DB 상태 게시가 실패하면 ref도 이전 commit으로 복구합니다.
기존 preview stack을 처음 전환할 때는 Core main 반영 뒤 현재 runtime 안에서
release-controller를 먼저 새 commit으로 self-upgrade하고, 그 controller로 Gateway
UPDATE를 완료하여 위 persistent ref가 생성된 것을 확인한 다음 Docker stack을
재생성합니다. 이 순서를 건너뛰고 새 Compose부터 적용하면 기존 Core bootstrap
checkout에는 artifact publisher가 없을 수 있습니다. 이후 일반 Gateway UPDATE와
rollback은 controller가 ref를 함께 관리하므로 같은 사전 절차를 반복하지 않습니다.
```sh
docker compose exec \
-e GATEWAY_ACTIVE_RELEASE_GIT_REF=refs/sammo/active-gateway \
runtime pnpm --filter @sammo-ts/release-controller self-upgrade BRANCH main
```
이 명령 뒤 관리자 Gateway UPDATE가 성공하고 ref와 active commit이 같은지 확인합니다.
현재 RUNNING/PREOPEN profile은 Docker 재생성 전에 같은 commit으로 DB 보존 DEPLOY하여
`.release-dist`를 준비합니다. RESET은 DB 폐기 의도가 있는 dev 대상에서 별도로
검증하며, 정적 제공 전환 자체를 위해 운영 DB를 RESET하지 않습니다.
`frontend-artifacts``builder-cache`, Core clone 안의 active release ref도 일반
`down`에서는 보존됩니다.
`down --volumes`는 DB뿐 아니라 이 release 상태도 삭제하므로 별도 폐기 권한과
backup 없이는 실행하지 않습니다.
## 개발 bind 모드
로컬 Core2026 checkout을 container에 bind하고 DB/Redis/Caddy는 같은 구성으로