180 lines
9.7 KiB
Markdown
180 lines
9.7 KiB
Markdown
# Sammo Core2026 Docker deployment
|
|
|
|
이 저장소는 새 호스트에서 `.env` 하나로 Core2026 Gateway, PostgreSQL, Redis,
|
|
Caddy와 Git/PM2 release-controller를 기동하기 위한 공개 배포 골격입니다. 게임
|
|
profile은 관리자 화면에서 각각 다른 branch 또는 commit을 선택해 DB 유지 배포,
|
|
DB 초기화 배포와 rollback을 수행합니다.
|
|
|
|
## 현재 도메인 배치
|
|
|
|
`dev-sam2026.hided.net`은 실제 외부 Core2026 서비스입니다. 이 저장소로 띄운
|
|
로컬 E2E Docker stack은 `dev-sam-e2e.hided.net`을 사용하며, 외부 Caddy가 TLS를
|
|
종료한 뒤 `172.30.1.54:14999`의 HTTP listener로 호스트 전체를 전달합니다.
|
|
Docker Caddy가 `/gateway/`, `/che/`, `/hwe/` 및 `/image/*`를 내부 서비스와
|
|
자산으로 분기합니다.
|
|
|
|
E2E `.env`의 비밀이 아닌 ingress 값은 다음과 같습니다.
|
|
|
|
```dotenv
|
|
DOMAIN=dev-sam-e2e.hided.net
|
|
PUBLIC_SCHEME=https
|
|
CADDY_SITE_ADDRESS=http://dev-sam-e2e.hided.net
|
|
HTTP_PORT=14999
|
|
```
|
|
|
|
외부 Caddy는 원래 `Host` header와 path prefix를 보존해야 합니다. 외부 확인은
|
|
`https://dev-sam-e2e.hided.net/gateway/api/healthz`를 사용합니다. `HTTPS_PORT`는
|
|
외부 proxy가 사용하지 않지만 Compose port 충돌을 피할 별도 값으로 둡니다.
|
|
|
|
Compose는 `DOMAIN`과 `PUBLIC_SCHEME`으로 `GATEWAY_PUBLIC_URL`과
|
|
`KAKAO_REDIRECT_URI`를 만듭니다. 외부 route만 바꾸면 이미 실행 중인 runtime의
|
|
process 환경은 바뀌지 않으므로, 도메인을 변경한 뒤 runtime과 Caddy container를
|
|
재생성하고 OAuth 시작 응답이
|
|
`https://dev-sam-e2e.hided.net/gateway/oauth/callback`을 사용하는지 확인합니다.
|
|
|
|
## 빠른 시작
|
|
|
|
Docker Engine과 Compose plugin이 설치된 Linux 호스트에서 다음을 실행합니다.
|
|
|
|
```sh
|
|
cp .env.example .env
|
|
# .env의 domain, Core2026 URL, Kakao key와 모든 비밀값을 교체합니다.
|
|
./scripts/check.sh
|
|
docker compose up -d --build --wait
|
|
docker compose ps
|
|
```
|
|
|
|
DNS의 `DOMAIN` A/AAAA record가 호스트를 가리키고 80/443 TCP 및 443 UDP가
|
|
열려 있으면 Caddy가 인증서를 자동 발급합니다. Kakao Developers에는
|
|
`https://DOMAIN/gateway/oauth/callback`을 redirect URI로 등록합니다. 첫 기동은
|
|
Core2026 clone, frozen-lockfile install, build와 Gateway migration 때문에 수 분이
|
|
걸릴 수 있습니다.
|
|
|
|
외부 reverse proxy가 TLS를 종료하고 이 stack의 HTTP port로 전달한다면
|
|
`PUBLIC_SCHEME=https`, `CADDY_SITE_ADDRESS=http://DOMAIN`을 함께 설정합니다.
|
|
예를 들어 host `14999`로 전달할 때는 `HTTP_PORT=14999`로 두고, 사용하지 않는
|
|
`HTTPS_PORT`는 충돌하지 않는 별도 host port로 지정합니다. 외부 proxy는 원래
|
|
`Host` header를 보존해야 합니다.
|
|
|
|
runtime은 기본적으로 memory/swap 각각 4 GiB, CPU 4개, PID 256개 상한을
|
|
가집니다. 호스트 용량에 맞춰 `RUNTIME_*_LIMIT`을 조정할 수 있지만 0 또는
|
|
무제한으로 두지 않습니다. PM2 process/restart 수가 예상보다 증가하면 로그 수집보다
|
|
runtime 중지가 우선입니다.
|
|
|
|
초기 빌드와 profile worktree 빌드는 기본 Node heap 1536 MiB,
|
|
`RAYON_NUM_THREADS=1` 안에서 실행됩니다. `RUNTIME_NODE_OPTIONS`의 heap 상한은
|
|
512~2048 MiB, `RUNTIME_RAYON_NUM_THREADS`는 1~4만 허용됩니다. 이는 빌드 중
|
|
container hard limit에 먼저 닿는 것을 막고 실행 process에도 같은 상한을
|
|
적용합니다.
|
|
|
|
`scripts/check.sh`는 example placeholder, 짧은 비밀값, 잘못된 domain/email,
|
|
repository credential 조합을 실제 값 출력 없이 거부합니다. 최소 길이는 DB·Redis
|
|
password 24자, game/bootstrap token 32자, 최초 관리자 password 16자입니다.
|
|
배포 전 무작위 값은 예를 들어 `openssl rand -base64 36`으로 생성합니다.
|
|
|
|
`INITIAL_ADMIN_*`은 사용자 table이 비어 있을 때만 superuser를 한 번 생성합니다.
|
|
그 뒤 `/gateway/admin/server-operations`에서 `che`, `kwe`, `pwe`, `twe`, `nya`,
|
|
`pya`, `hwe`마다 source branch/commit을 독립 선택합니다. `DB 유지 배포`는 현
|
|
시즌을 보존하고, `DB 초기화 배포`는 scenario를 다시 seed합니다. Gateway 자체의
|
|
배포와 이전 commit rollback은 같은 화면의 별도 release 영역에서 처리됩니다.
|
|
초기 profile의 `<profile>:default`에서 `default`는 현재 scenario가 아니라 변경되지
|
|
않는 인스턴스 키입니다. 현재 scenario는 DB 초기화 배포가 성공한 뒤 Gateway DB의
|
|
별도 필드에 기록됩니다.
|
|
|
|
이미지 서비스 연동에는 서버 간 접근 URL `IMAGE_SERVICE_URL`, 브라우저 공개 URL
|
|
`IMAGE_PUBLIC_URL`과 서로 다른 두 secret 파일이 필요합니다. 업로드 secret은
|
|
Gateway 전용 아이콘과 game-api 편집기 첨부에만 사용하고, sync secret은 Gitea
|
|
webhook 누락 시 `docker compose exec runtime pnpm sync:image`로 현재 이미지
|
|
branch의 fast-forward를 요청할 때만 사용합니다. 두 파일은 runtime에 read-only로
|
|
mount되며 원문은 환경 변수나 브라우저 bundle에 들어가지 않습니다.
|
|
|
|
`IMAGE_PUBLIC_URL`의 기본값은 `https://sam-image.hided.net`입니다. runtime은
|
|
이를 Gateway·game frontend의 공개 이미지 origin과 사용자 아이콘 URL에 함께
|
|
주입합니다. 정적 게임 이미지는 `/game`, 공용 아이콘은 `/icons`, Core2026
|
|
사용자 아이콘은 서버 발급 경로 `users/core2026/<파일>`을 `/icons` base 아래에
|
|
붙여 읽습니다. 업로드 권한값은
|
|
URL query나 `VITE_*`가 아니라 `IMAGE_UPLOAD_CORE2026_SECRET_FILE`이 가리키는
|
|
mode 0600 secret 파일에만 둡니다.
|
|
|
|
## 데이터와 복구 경계
|
|
|
|
- PostgreSQL, Redis, Core clone/worktree, PM2 상태와 Caddy 인증서는 named
|
|
volume에 보존됩니다. 새 user icon은 외부 이미지 서비스가 보존하며 runtime의
|
|
user icon volume은 기존 설치 호환용입니다.
|
|
- `/image/*`는 앱 artifact가 아니며 `data/image/`의 별도 운영 자산을 제공합니다.
|
|
- 일반 `docker compose down`은 volume을 보존합니다. `down --volumes`는 DB와
|
|
release 상태를 삭제하는 파괴적 명령이므로 backup 없이 실행하지 않습니다.
|
|
- 앱 rollback은 Prisma migration을 되돌리지 않습니다. 이전 앱과 새 schema의
|
|
호환성을 배포 전에 확인합니다.
|
|
- Core2026 저장소가 비공개라면 credential을 Git에 추가하지 않습니다. HTTPS는
|
|
`.env`의 `CORE_REPOSITORY_USERNAME`과 read-only
|
|
`CORE_REPOSITORY_TOKEN`을 함께 설정합니다. URL에 credential을 포함하지 않으며
|
|
runtime은 0600 파일로 옮긴 뒤 원래 env를 제거하고 `GIT_ASKPASS`로만
|
|
전달합니다.
|
|
- SSH deploy key를 사용할 때는 `CORE_SSH_PRIVATE_KEY_BASE64`와
|
|
`CORE_SSH_KNOWN_HOSTS_BASE64`를 함께 설정합니다. 각각 `base64 -w0`로 인코딩한
|
|
read-only key와 검증한 known_hosts 내용입니다. HTTPS와 SSH mode를 동시에
|
|
설정할 수 없습니다. 원래 base64 env는 파일 생성 직후 제거됩니다. 생성된
|
|
credential/key 파일은 runtime volume에서 0600으로 관리되고 해당 mode를
|
|
해제한 다음 기동하면 제거됩니다.
|
|
|
|
상태 확인과 로그:
|
|
|
|
```sh
|
|
docker compose ps
|
|
docker compose logs --tail=200 runtime caddy
|
|
docker compose exec runtime pnpm --filter @sammo-ts/release-controller status
|
|
```
|
|
|
|
Caddy의 runtime logger는 reverse proxy 오류에도 요청 정보를 남길 수 있으므로
|
|
`X-Session-Token` 값을 encoder 단계에서 삭제합니다. 장애 로그를 수집할 때도
|
|
인증 헤더, cookie, token과 secret을 보고서나 채팅에 복사하지 않습니다.
|
|
|
|
## 개발 bind 모드
|
|
|
|
로컬 Core2026 checkout을 container에 bind하고 DB/Redis/Caddy는 같은 구성으로
|
|
사용할 수 있습니다. `.env`에 `CORE_DEV_PATH=/absolute/path/to/core2026`,
|
|
host 사용자의 `DEV_UID`/`DEV_GID`, `PUBLIC_SCHEME=http`, `DOMAIN=localhost`를
|
|
추가한 뒤 실행합니다. 기본 UID/GID는 `1000:1000`입니다.
|
|
|
|
```sh
|
|
docker compose -f compose.yaml -f compose.dev.yaml up -d --build
|
|
docker compose exec runtime pnpm --filter @sammo-ts/gateway-api dev
|
|
docker compose exec runtime pnpm --filter @sammo-ts/gateway-frontend dev --host 0.0.0.0
|
|
```
|
|
|
|
개발 override의 runtime은 의존성과 Prisma client를 준비한 뒤 대기합니다. 필요한
|
|
watch process를 별도 shell에서 실행합니다. 운영 release-controller를 시험하려면
|
|
override 없이 production mode를 사용해야 하며, bind checkout의 Git metadata에
|
|
container worktree 경로를 등록하지 않도록 주의합니다.
|
|
|
|
`compose.dev.yaml`은 `RUNTIME_MODE=development`와 모든 service의
|
|
`restart: no`를 literal로 고정합니다. `.env`의 `RUNTIME_MODE`로 이를 덮어쓸 수
|
|
없습니다. production PM2 검증에 개발 override를 섞지 않습니다. 개발 runtime에도
|
|
기본 4 GiB/4 CPU/256 PID 상한이 있으며 `DEV_RUNTIME_*_LIMIT`으로 더 낮출 수
|
|
있습니다.
|
|
|
|
운영 entrypoint와 PM2를 검증할 때는 개발 override 대신 전용 smoke override를
|
|
사용합니다. 이 override는 clone mode를 유지하면서 모든 service를 `restart: no`로
|
|
고정하고 runtime 상한을 최대 4 GiB/4 CPU/256 PID로 제한합니다.
|
|
|
|
```sh
|
|
docker compose -f compose.yaml -f compose.smoke.yaml up -d --build --wait
|
|
```
|
|
|
|
검증 shell에는 EXIT/INT/TERM trap과 전체 timeout을 두고, 정상·실패 어느 경우든
|
|
`docker compose stop`으로 끝냅니다. `down --volumes`는 사용하지 않습니다.
|
|
|
|
## 설정 검증
|
|
|
|
`.env`를 채운 뒤 실제 값을 출력하지 않는 검사를 실행합니다.
|
|
|
|
```sh
|
|
./scripts/check.sh
|
|
```
|
|
|
|
검사는 production/development Compose model과 Caddyfile 구문을 확인합니다.
|
|
다른 env 파일은 `ENV_FILE=/path/to/file ./scripts/check.sh`로 검사합니다.
|
|
`docker compose config` 전체 출력에는 펼쳐진 비밀값이 포함될 수 있으므로 CI
|
|
artifact나 이슈에 그대로 첨부하지 않습니다.
|