From b38839666c9ef3c28e429dced842f0500804849e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 02:31:32 +0000 Subject: [PATCH] =?UTF-8?q?docs(deploy):=20Portainer=20cutover=20guide=20a?= =?UTF-8?q?nd=20server-up=20script=20(N2-B8=E2=80=9312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit msn.iykyka.com still serves legacy Express; agent lacks SSH/Portainer credentials so cutover is documented and marked blocked pending access. Co-authored-by: okuma --- README.md | 6 ++- docs/deploy-docker.md | 3 +- docs/deploy-portainer.md | 95 ++++++++++++++++++++++++++++++++++++++++ scripts/server-up.sh | 43 ++++++++++++++++++ 4 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 docs/deploy-portainer.md create mode 100755 scripts/server-up.sh diff --git a/README.md b/README.md index 7e94cb4..0907f72 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,9 @@ cd mobile && flutter run --dart-define=CORE_API_BASE=http://10.0.2.2:8080 ## Docker (N2-B / `msn.iykyka.com`) -절차·구성: [`docs/deploy-docker.md`](./docs/deploy-docker.md) +- 구성: [`docs/deploy-docker.md`](./docs/deploy-docker.md) +- Portainer·컷오버: [`docs/deploy-portainer.md`](./docs/deploy-portainer.md) +- 서버 기동: `./scripts/server-up.sh` (호스트에서 `.env` 채운 뒤) ```bash cp .env.example .env # ADMIN_API_TOKEN, POSTGRES_PASSWORD 필수 @@ -66,4 +68,4 @@ docker compose up -d --build curl -sS http://localhost:8088/health ``` -Postgres·AI는 내부망만. 엣지 프록시는 `web:80`만 공개. +Postgres·AI는 내부망만. 엣지 프록시는 `web:80`(또는 `WEB_HOST_PORT`)만 공개. diff --git a/docs/deploy-docker.md b/docs/deploy-docker.md index fa52881..25169c0 100644 --- a/docs/deploy-docker.md +++ b/docs/deploy-docker.md @@ -27,7 +27,8 @@ curl -sS http://localhost:8088/health curl -sS http://localhost:8088/demo ``` -Portainer: 이 저장소의 `docker-compose.yml` + 스택 env로 동일하게 Up. +Portainer·OpenResty 컷오버 상세: [`deploy-portainer.md`](./deploy-portainer.md). +서버 원샷: `./scripts/server-up.sh` ## 시크릿 diff --git a/docs/deploy-portainer.md b/docs/deploy-portainer.md new file mode 100644 index 0000000..c4a8f03 --- /dev/null +++ b/docs/deploy-portainer.md @@ -0,0 +1,95 @@ +# Portainer 배포 · 컷오버 (N2-B8~B12) + +대상: `https://msn.iykyka.com` +스택 정의: 저장소 root [`docker-compose.yml`](../docker-compose.yml) +개요: [`deploy-docker.md`](./deploy-docker.md) + +## 현재 프로브 (에이전트, 2026-07-31) + +| URL | 결과 | +|-----|------| +| `https://msn.iykyka.com/` | Express `Cannot GET /` (404) — 구 Node MSN으로 추정 | +| `https://msn.iykyka.com/health` | `{"ok":true}` — 구 스택 | +| `https://portainer.iykyka.com/` | 200, API `Version 2.39.2` | +| 엣지 | `Server: openresty` | + +에이전트 VM에는 **SSH 키/`iykyka@iykyka.com:7788` 권한이 없음** → 실제 Up·컷오버는 +Master가 Portainer 또는 SSH로 수행(또는 키/토큰을 에이전트에 제공). + +새 스택 헬스 응답은 `{"status":"ok"}` (구스택 `{"ok":true}` 과 구분). + +--- + +## A. Portainer 스택 생성 + +1. Portainer → **Stacks** → **Add stack** +2. Build method: **Repository** (권장) + - Repository URL: `https://gitea.iykyka.com/oh/iykyka.git` (또는 GitHub `o0kuma/hikikomori`) + - Compose path: `docker-compose.yml` + - Branch: `main` +3. **Environment variables** (시크릿 — git 금지): + +| Name | 예 | +|------|-----| +| `ADMIN_API_TOKEN` | 긴 랜덤 | +| `POSTGRES_PASSWORD` | 긴 랜덤 | +| `POSTGRES_USER` | `ykavu` | +| `POSTGRES_DB` | `ykavu` | +| `GEMINI_API_KEY` | (있으면) | +| `ALLOW_DEMO_INVITE` | `1` | +| `PUBLIC_API_BASE` | `https://msn.iykyka.com` | +| `WEB_HOST_PORT` | `8088` (컷오버 전 임시) → 전환 후 openresty가 가리키는 포트에 맞춤 | +| `FCM_SERVER_KEY` | (없으면 비움) | + +4. Deploy the stack → 빌드 완료까지 대기 (Flutter multi-stage는 수 분~십수 분) +5. 호스트에서 확인: + +```bash +curl -sS http://127.0.0.1:8088/health # → {"status":"ok"} +curl -sS http://127.0.0.1:8088/demo # → DEMO-YKAVU +``` + +### SSH로 올릴 때 + +```bash +git clone https://gitea.iykyka.com/oh/iykyka.git ykavu && cd ykavu +cp .env.example .env # 값 채움 +./scripts/server-up.sh +``` + +--- + +## B. OpenResty 컷오버 (N2-B11) + +1. **병렬 기동**: 새 스택을 `WEB_HOST_PORT=8088`(또는 빈 포트)로 Up, 구 MSN은 유지 +2. 로컬 스모크: `/health` → `status`, `/demo` → `DEMO-YKAVU`, 브라우저로 `http://HOST:8088/` 가입 +3. OpenResty upstream을 **구 Express → `127.0.0.1:8088`(web)** 로 변경 후 reload +4. 공개 URL 스모크: + - `https://msn.iykyka.com/health` → `{"status":"ok"}` + - `https://msn.iykyka.com/demo` → demo JSON + - 회원가입 `DEMO-YKAVU` +5. 구 Node MSN 컨테이너/프로세스 중지 +6. **롤백**: upstream을 구 MSN으로 되돌리고 openresty reload + +WebSocket: openresty에서 `/ws` 에 `Upgrade` / `Connection` 헤더 전달 필요 +(nginx `proxy_set_header Upgrade $http_upgrade` 와 동일). + +--- + +## C. 배포 스모크 체크 (N2-B12) + +- [ ] `GET /health` → `{"status":"ok"}` +- [ ] `GET /demo` → `demo_invite_code=DEMO-YKAVU` +- [ ] 브라우저 가입 → 말투 온보딩 → 대화 목록 +- [ ] (가능 시) draft/L1 1회 +- [ ] AI·Postgres 호스트 포트 미노출 확인 + +--- + +## Master에게 필요한 것 (에이전트 대행 시) + +아래 중 **하나**만 있으면 N2-B8~B12를 에이전트가 이어서 실행할 수 있다. + +1. SSH: `iykyka@iykyka.com:7788` 용 **private key** (또는 일시 비밀번호 — 채팅 대신 시크릿 채널 권장) +2. Portainer **API access token** + endpoint id +3. Master가 A~C를 직접 수행한 뒤 결과만 공유 diff --git a/scripts/server-up.sh b/scripts/server-up.sh new file mode 100755 index 0000000..a856a1e --- /dev/null +++ b/scripts/server-up.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Run ON the iykyka host (SSH or Portainer host shell) after cloning this repo. +# Does not commit secrets. Requires Docker Compose v2 + .env filled. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +if [[ ! -f .env ]]; then + echo "Missing .env — copy from .env.example and set ADMIN_API_TOKEN, POSTGRES_PASSWORD, GEMINI_API_KEY" + exit 1 +fi + +# shellcheck disable=SC1091 +set -a +source .env +set +a + +: "${ADMIN_API_TOKEN:?ADMIN_API_TOKEN required}" +: "${POSTGRES_PASSWORD:?POSTGRES_PASSWORD required}" +: "${PUBLIC_API_BASE:=https://msn.iykyka.com}" +: "${WEB_HOST_PORT:=8088}" + +export PUBLIC_API_BASE WEB_HOST_PORT + +echo "==> Building & starting stack (PUBLIC_API_BASE=$PUBLIC_API_BASE WEB_HOST_PORT=$WEB_HOST_PORT)" +docker compose pull postgres || true +docker compose up -d --build --remove-orphans + +echo "==> Waiting for web health on localhost:${WEB_HOST_PORT}" +for i in $(seq 1 60); do + if curl -fsS "http://127.0.0.1:${WEB_HOST_PORT}/health" >/dev/null 2>&1; then + echo "OK health" + curl -fsS "http://127.0.0.1:${WEB_HOST_PORT}/health"; echo + curl -fsS "http://127.0.0.1:${WEB_HOST_PORT}/demo"; echo + exit 0 + fi + sleep 5 +done + +echo "Timed out waiting for /health — check: docker compose ps && docker compose logs" +docker compose ps +exit 1