Skip to content

Commit bfd7e6c

Browse files
authored
docs(prod): N100 prod migration guide (#42)
1 parent 9190eb9 commit bfd7e6c

2 files changed

Lines changed: 249 additions & 0 deletions

File tree

docs/wiki/Home.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
- [회고 · Week 1](Retrospective-Week1)
1919
- [회고 · Week 2](Retrospective-Week2)
2020

21+
### 운영
22+
- [N100 Prod 마이그레이션](N100-Prod-Migration)
23+
2124
## 3주 로드맵 요약
2225

2326
| 주차 | 목표 | 산출물 |

docs/wiki/N100-Prod-Migration.md

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# N100 Prod Migration (staging → prod 단일)
2+
3+
Week 3 부터 N100 에서는 **prod 만** 운영한다. dev push 는 GHCR 이미지 빌드까지만, N100 SSH 는 main push 일 때만. 이 문서는 호스트(나)가 N100 콘솔에서 한 번 하는 절차를 정리한다.
4+
5+
기준 시점: PR #36 이후. v0.2.0 태그가 main 에 찍힌 상태.
6+
7+
---
8+
9+
## 0. 사전 준비
10+
11+
- [ ] DuckDNS: `murun.duckdns.org` 등록 + 현재 N100 공인 IP 매핑 (확인: `dig +short murun.duckdns.org`)
12+
- [ ] 공유기: 외부 80/443 → N100 내부 IP 80/443 포트포워딩
13+
- [ ] Google Cloud Console: 기존 `Murun Staging` OAuth client 이름을 `Murun Prod` 로 바꾸고 (보안키 그대로 재사용), Authorized redirect URI 에 아래 추가:
14+
- `https://murun.duckdns.org/api/auth/callback/google`
15+
- [ ] GitHub repo Secrets 등록 상태 확인 (`gh secret list` on N100 또는 로컬):
16+
- `N100_HOST` = 공인 IP 또는 `murun.duckdns.org`
17+
- `N100_USER`
18+
- `N100_SSH_KEY`
19+
20+
---
21+
22+
## 1. 기존 staging 컨테이너 폐기
23+
24+
테스트 시점에 떴던 `murun-staging` 은 그냥 버린다. 데이터(테스트용 가입 1건 등)는 보존 안 함.
25+
26+
```bash
27+
cd ~/murun-peterabcd/deploy
28+
29+
# 1) staging 컨테이너 down + 볼륨 제거
30+
docker compose -p murun-staging --env-file .env.staging down -v
31+
32+
# 2) 혹시 남은 dangling volume 정리
33+
docker volume ls | grep murun-staging # 비어있는지 확인
34+
docker volume prune -f # (선택) 다른 dangling volume 정리
35+
36+
# 3) staging 용 env 파일 제거 (host-only, gitignore 됨)
37+
rm -f .env.staging
38+
```
39+
40+
확인:
41+
42+
```bash
43+
docker compose ls # murun-staging 없어야 함
44+
docker volume ls | grep murun # 비어있어야 함
45+
```
46+
47+
---
48+
49+
## 2. main 브랜치로 repo 동기화
50+
51+
deploy workflow 는 N100 의 `~/murun-peterabcd` 가 main branch 를 가리킨다는 가정. 첫 셋업 때 `dev` 였으면 main 으로 바꿔둔다.
52+
53+
```bash
54+
cd ~/murun-peterabcd
55+
git fetch origin
56+
git checkout main
57+
git pull origin main
58+
```
59+
60+
이후 자동 배포가 SSH 로 들어와서 `git fetch + checkout -B main FETCH_HEAD` 로 갱신한다.
61+
62+
---
63+
64+
## 3. `.env.prod` 작성
65+
66+
```bash
67+
cd ~/murun-peterabcd/deploy
68+
cp .env.example .env.prod
69+
```
70+
71+
`.env.prod` 안의 값:
72+
73+
```env
74+
# --- compose / image ---
75+
IMAGE_TAG=prod # GH Actions main push 가 prod-<sha> 로 덮어씀
76+
77+
# --- Caddy ---
78+
DOMAIN=murun.duckdns.org
79+
CADDY_HTTP_PORT=80
80+
CADDY_HTTPS_PORT=443
81+
82+
# --- App ---
83+
NODE_ENV=production
84+
DATABASE_URL=file:/app/data/murun.db
85+
NEXT_PUBLIC_APP_URL=https://murun.duckdns.org
86+
UPLOADS_DIR=/app/uploads
87+
88+
# --- Auth.js v5 ---
89+
AUTH_URL=https://murun.duckdns.org
90+
AUTH_SECRET=<openssl rand -base64 32 결과>
91+
AUTH_TRUST_HOST=true
92+
93+
# --- Google OAuth ---
94+
AUTH_GOOGLE_ID=<Murun Prod (=구 Murun Staging) client id>
95+
AUTH_GOOGLE_SECRET=<동일 secret>
96+
AUTH_GOOGLE_HD=snu.ac.kr
97+
```
98+
99+
AUTH_SECRET 생성:
100+
101+
```bash
102+
openssl rand -base64 32
103+
```
104+
105+
권한 확인 (다른 유저 못 읽게):
106+
107+
```bash
108+
chmod 600 .env.prod
109+
```
110+
111+
---
112+
113+
## 4. 첫 prod 기동 (수동 한 번)
114+
115+
GHCR 에서 `prod` 이미지를 받아 띄운다. main push 가 이미 한 번 이미지를 올려둔 상태 (v0.2.0 = `prod-579064b`).
116+
117+
```bash
118+
cd ~/murun-peterabcd/deploy
119+
120+
export MURUN_ENV_FILE=.env.prod
121+
122+
# (선택) ghcr.io pull 인증. repo 가 public 이면 skip 가능.
123+
echo "<PAT_with_read:packages>" | docker login ghcr.io -u peterabcd --password-stdin
124+
125+
docker compose -p murun-prod --env-file .env.prod pull
126+
docker compose -p murun-prod --env-file .env.prod up -d
127+
docker compose -p murun-prod --env-file .env.prod ps
128+
```
129+
130+
기대 상태:
131+
132+
```
133+
NAME IMAGE STATUS
134+
murun-prod-app-1 ghcr.io/.../murun-peterabcd:prod-... healthy
135+
murun-prod-caddy-1 caddy:2-alpine running
136+
```
137+
138+
로그:
139+
140+
```bash
141+
docker compose -p murun-prod --env-file .env.prod logs -f app
142+
docker compose -p murun-prod --env-file .env.prod logs -f caddy # cert 발급 확인
143+
```
144+
145+
Caddy 로그에서 "certificate obtained" / "served HTTPS for murun.duckdns.org" 확인.
146+
147+
---
148+
149+
## 5. 첫 로그인 + ADMIN 승인
150+
151+
`https://murun.duckdns.org` 에 접속 → Google 로그인 (SNU 계정) → `/pending` 으로 이동.
152+
153+
같은 N100 에서 본인 계정을 직접 admin 으로 승격:
154+
155+
```bash
156+
cd ~/murun-peterabcd/deploy
157+
docker compose -p murun-prod --env-file .env.prod exec app node
158+
```
159+
160+
Node REPL:
161+
162+
```js
163+
const { PrismaClient } = require("@prisma/client");
164+
const db = new PrismaClient();
165+
166+
await db.user.updateMany({
167+
where: { email: "본인@snu.ac.kr" },
168+
data: {
169+
approved: true,
170+
role: "ADMIN",
171+
approvedAt: new Date(),
172+
},
173+
});
174+
175+
await db.$disconnect();
176+
.exit
177+
```
178+
179+
브라우저에서 새로고침 → `/admin/members` 접근 가능 여부 확인.
180+
181+
---
182+
183+
## 6. 자동 배포 확인
184+
185+
다음 dev → main 머지 시 자동 진행:
186+
187+
```
188+
GitHub Actions
189+
├─ Build & push image → ghcr.io ... :prod-<sha>
190+
├─ Check deploy secrets → pass
191+
└─ Deploy to prod
192+
├─ N100 SSH
193+
├─ git checkout -B main FETCH_HEAD
194+
├─ docker compose pull
195+
└─ docker compose up -d --remove-orphans
196+
```
197+
198+
수동 트리거가 필요할 때:
199+
200+
```bash
201+
gh workflow run deploy.yml --ref main -f target=prod
202+
```
203+
204+
---
205+
206+
## 7. 트러블슈팅 컷
207+
208+
| 증상 | 원인/확인 | 조치 |
209+
|------|----------|------|
210+
| Caddy `tls obtain failed` | 80/443 외부 접근 불가 | 공유기 포트포워딩 / 방화벽 (`ufw status`) 재확인 |
211+
| `couldn't find env file: ./.env.prod` | `.env.prod` 누락 | 위 §3 다시 |
212+
| Google `redirect_uri_mismatch` | OAuth client 에 prod URI 미등록 | Authorized redirect URI 에 추가 |
213+
| `Invalid host header` | `AUTH_URL` mismatch | `AUTH_URL=https://murun.duckdns.org` 확인 |
214+
| 로그인 후 `/pending` 영원 | DB user `approved=false` | 위 §5 admin 승인 |
215+
| `prisma migrate deploy` 실패 | volume 권한 / 잘못된 DATABASE_URL | `docker volume inspect murun-prod_data``/app/data` 마운트 경로 확인 |
216+
217+
---
218+
219+
## 8. 백업 (간단판)
220+
221+
prod 데이터 = SQLite 파일 + uploads 디렉터리. 둘 다 host volume.
222+
223+
```bash
224+
sudo tar czf ~/murun-backup-$(date +%Y%m%d).tar.gz \
225+
/var/lib/docker/volumes/murun-prod_data/_data \
226+
/var/lib/docker/volumes/murun-prod_uploads/_data
227+
```
228+
229+
cron 등록은 추후 (`scripts/backup.sh`).
230+
231+
---
232+
233+
## 9. 롤백
234+
235+
```bash
236+
cd ~/murun-peterabcd/deploy
237+
238+
# 직전 태그 확인
239+
docker images ghcr.io/boostcampwm-snu-2026-1/murun-peterabcd --format '{{.Tag}}\t{{.CreatedAt}}'
240+
241+
# 이전 prod-<sha> 로 되돌리기
242+
IMAGE_TAG=prod-<previous-sha> MURUN_ENV_FILE=.env.prod \
243+
docker compose -p murun-prod --env-file .env.prod up -d
244+
```
245+
246+
Schema 변경이 있던 release 면 migration rollback 까지 별도 작업 필요. v0.2.0 까지는 추가 migration 없음.

0 commit comments

Comments
 (0)