392 lines
9.0 KiB
Markdown
392 lines
9.0 KiB
Markdown
# UGREEN NAS 운영 배포 안내
|
|
|
|
## 운영 원칙
|
|
|
|
- 기능 테스트는 개발 Mac에서 수행한다.
|
|
- NAS에는 운영 컨테이너만 실행한다.
|
|
- 로컬 빌드와 Docker 검증을 통과한 커밋만 NAS에 배포한다.
|
|
- NAS에서 소스 코드를 직접 수정하지 않는다.
|
|
- 운영 주소는 현재 `http://playuniare.sori.studio`다.
|
|
- HTTPS는 인증서와 리버스 프록시 구성이 완료된 뒤 전환한다.
|
|
|
|
현재 프로젝트는 Vue 정적 프런트엔드와 Node.js Socket.IO 서버를 같은
|
|
Compose 프로젝트로 배포한다.
|
|
|
|
## 배포 구조
|
|
|
|
```text
|
|
사용자 브라우저
|
|
↓
|
|
http://playuniare.sori.studio
|
|
↓
|
|
NAS 리버스 프록시
|
|
↓
|
|
127.0.0.1:41809
|
|
↓
|
|
Play Uniare Nginx 컨테이너
|
|
├── / → Vue 빌드 결과
|
|
├── /card-assets/ → 기존 Uniare 카드 이미지
|
|
└── /socket.io/ → Docker 내부 playuniare-server:31873
|
|
```
|
|
|
|
`31873`은 Docker 내부 통신에만 사용하며 NAS 호스트에는 공개하지 않는다.
|
|
외부에서는 웹 주소와 같은 `http://playuniare.sori.studio/socket.io` 경로로
|
|
접속한다.
|
|
|
|
카드 이미지는 복사하지 않고 다음 폴더를 읽기 전용으로 연결한다.
|
|
|
|
```text
|
|
/volume1/docker/projects/apps/uniare/config/www/images
|
|
```
|
|
|
|
## 로컬 배포 전 검증
|
|
|
|
개발 Mac의 프로젝트 폴더에서 실행한다.
|
|
|
|
```bash
|
|
npm install
|
|
npm run build
|
|
docker compose build
|
|
```
|
|
|
|
필요하면 로컬 Docker 컨테이너도 실행한다.
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
브라우저에서 다음 주소를 확인한다.
|
|
|
|
```text
|
|
http://localhost:41809
|
|
```
|
|
|
|
로컬 Mac에는 NAS 카드 폴더가 없으므로 `/card-assets` 확인은 NAS 배포 후
|
|
수행한다.
|
|
|
|
검증이 끝나면 로컬 컨테이너를 종료한다.
|
|
|
|
```bash
|
|
docker compose down
|
|
```
|
|
|
|
## NAS 최초 설치
|
|
|
|
### 1. SSH로 NAS 접속
|
|
|
|
개발 Mac 터미널에서 실행한다.
|
|
|
|
```bash
|
|
ssh NAS사용자명@192.168.50.146
|
|
```
|
|
|
|
실제 SSH 사용자명은 NAS 설정에 맞게 사용한다.
|
|
|
|
### 2. 저장소 복제
|
|
|
|
NAS 터미널에서 실행한다.
|
|
|
|
```bash
|
|
cd /volume1/docker/projects/apps
|
|
git clone https://git.sori.studio/zenn/playuniare.sori.studio.git playuniare
|
|
cd playuniare
|
|
```
|
|
|
|
비공개 저장소가 인증을 요구하면 Git 서버의 접근 토큰을 사용한다. 토큰을
|
|
문서나 `.env`에 저장하지 않는다.
|
|
|
|
### 3. NAS 환경변수 파일 생성
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
기본 내용:
|
|
|
|
```text
|
|
PLAYUNIARE_PRODUCTION_HTTP_PORT=41809
|
|
VITE_CARD_ASSET_BASE_URL=/card-assets
|
|
```
|
|
|
|
운영 Socket.IO 컨테이너는 내부 포트 `31873`을 사용한다. 브라우저는 현재
|
|
도메인의 `/socket.io`를 사용하므로 별도 공개 Socket.IO 주소가 필요하지
|
|
않다.
|
|
|
|
### 4. 포트 사용 여부 확인
|
|
|
|
```bash
|
|
docker ps --format "table {{.Names}}\t{{.Ports}}"
|
|
```
|
|
|
|
`41809`가 이미 사용 중이면 `.env`의 값을 다른 포트로 바꾼다.
|
|
|
|
```text
|
|
PLAYUNIARE_PRODUCTION_HTTP_PORT=41819
|
|
```
|
|
|
|
이 경우 리버스 프록시 대상 포트도 같은 값으로 변경해야 한다.
|
|
|
|
### 5. 운영 컨테이너 실행
|
|
|
|
```bash
|
|
docker compose \
|
|
-f compose.yaml \
|
|
-f compose.nas.yaml \
|
|
up -d --build
|
|
```
|
|
|
|
상태 확인:
|
|
|
|
```bash
|
|
docker compose \
|
|
-f compose.yaml \
|
|
-f compose.nas.yaml \
|
|
ps
|
|
```
|
|
|
|
`playuniare-web`과 `playuniare-server`가 모두 `healthy` 또는 `running`이면
|
|
정상이다.
|
|
|
|
### 6. 리버스 프록시 연결
|
|
|
|
현재 연결 기준:
|
|
|
|
```text
|
|
외부 주소: http://playuniare.sori.studio
|
|
내부 대상: http://127.0.0.1:41809
|
|
```
|
|
|
|
`.env`에서 포트를 변경했다면 내부 대상 포트도 변경한다.
|
|
|
|
### 7. 배포 확인
|
|
|
|
메인 화면:
|
|
|
|
```text
|
|
http://playuniare.sori.studio
|
|
```
|
|
|
|
카드 이미지:
|
|
|
|
```text
|
|
http://playuniare.sori.studio/card-assets/ua01/EX02BT_CGH-2-007.png
|
|
```
|
|
|
|
두 주소가 모두 열리면 정적 배포와 카드 자산 연결은 정상이다. 추가로 서로
|
|
다른 브라우저 또는 시크릿 창 두 개를 열어 한쪽에서 방을 만들고 다른 쪽에서
|
|
코드로 참가해 실시간 연결을 확인한다.
|
|
|
|
## 새 버전 업데이트
|
|
|
|
### 개발 Mac
|
|
|
|
1. 기능을 구현한다.
|
|
2. 관련 문서를 수정한다.
|
|
3. `npm run build`를 실행한다.
|
|
4. 필요한 경우 로컬 Docker로 검증한다.
|
|
5. Git에 커밋하고 푸시한다.
|
|
|
|
### NAS
|
|
|
|
```bash
|
|
cd /volume1/docker/projects/apps/playuniare
|
|
git pull
|
|
|
|
docker compose \
|
|
-f compose.yaml \
|
|
-f compose.nas.yaml \
|
|
up -d --build
|
|
```
|
|
|
|
배포 후 반드시 확인한다.
|
|
|
|
```bash
|
|
docker compose \
|
|
-f compose.yaml \
|
|
-f compose.nas.yaml \
|
|
ps
|
|
|
|
docker compose \
|
|
-f compose.yaml \
|
|
-f compose.nas.yaml \
|
|
logs --tail=100
|
|
```
|
|
|
|
마지막으로 실제 도메인에서 메인 화면과 주요 기능을 확인한다.
|
|
|
|
## 기존 NAS 테스트 컨테이너 제거
|
|
|
|
이전에 `playuniare-test-web`을 실행했다면 한 번만 정리한다.
|
|
|
|
```bash
|
|
docker rm -f playuniare-test-web
|
|
```
|
|
|
|
컨테이너가 없다는 메시지가 나오면 이미 제거된 것이므로 무시해도 된다.
|
|
|
|
테스트용 Docker 네트워크가 남아 있다면 확인 후 제거한다.
|
|
|
|
```bash
|
|
docker network ls
|
|
docker network rm playuniare-test_default
|
|
```
|
|
|
|
네트워크가 사용 중이거나 존재하지 않으면 무리하게 삭제하지 않는다.
|
|
|
|
## 이전 버전으로 되돌리기
|
|
|
|
최근 커밋 확인:
|
|
|
|
```bash
|
|
git log --oneline -10
|
|
```
|
|
|
|
정상 동작했던 커밋으로 전환:
|
|
|
|
```bash
|
|
git checkout 정상커밋식별자
|
|
|
|
docker compose \
|
|
-f compose.yaml \
|
|
-f compose.nas.yaml \
|
|
up -d --build
|
|
```
|
|
|
|
복구 후 개발 Mac에서 문제를 수정해 새로운 커밋을 만든다. 다시 최신
|
|
`main`으로 돌아갈 때:
|
|
|
|
```bash
|
|
git checkout main
|
|
git pull
|
|
|
|
docker compose \
|
|
-f compose.yaml \
|
|
-f compose.nas.yaml \
|
|
up -d --build
|
|
```
|
|
|
|
## 운영 점검 명령
|
|
|
|
상태:
|
|
|
|
```bash
|
|
docker compose -f compose.yaml -f compose.nas.yaml ps
|
|
```
|
|
|
|
최근 로그:
|
|
|
|
```bash
|
|
docker compose -f compose.yaml -f compose.nas.yaml logs --tail=100
|
|
```
|
|
|
|
재시작:
|
|
|
|
```bash
|
|
docker compose -f compose.yaml -f compose.nas.yaml restart
|
|
```
|
|
|
|
중지 및 제거:
|
|
|
|
```bash
|
|
docker compose -f compose.yaml -f compose.nas.yaml down
|
|
```
|
|
|
|
## 포트 충돌
|
|
|
|
다음 오류는 NAS 호스트 포트가 다른 서비스에 사용 중이라는 뜻이다.
|
|
|
|
```text
|
|
Bind for 0.0.0.0:<포트> failed: port is already allocated
|
|
```
|
|
|
|
사용 중인 포트 확인:
|
|
|
|
```bash
|
|
docker ps --format "table {{.Names}}\t{{.Ports}}"
|
|
```
|
|
|
|
`.env`의 `PLAYUNIARE_PRODUCTION_HTTP_PORT`를 사용하지 않는 숫자로 변경하고
|
|
다시 실행한다. 컨테이너 내부의 80 포트는 다른 컨테이너와 같아도 괜찮다.
|
|
|
|
## 카드 이미지가 403인 경우
|
|
|
|
로그에 다음 내용이 있으면 NAS ACL에 의한 읽기 권한 문제다.
|
|
|
|
```text
|
|
open() "/srv/uniare-card-images/..." failed (13: Permission denied)
|
|
```
|
|
|
|
현재 이미지는 카드 파일 소유자 UID `1000`과 맞춘 `cardreader` 사용자를
|
|
사용한다. 최신 소스를 받고 반드시 이미지를 다시 빌드한다.
|
|
|
|
```bash
|
|
git pull
|
|
docker compose -f compose.yaml -f compose.nas.yaml up -d --build
|
|
```
|
|
|
|
Nginx 워커 확인:
|
|
|
|
```bash
|
|
docker exec playuniare-web ps aux
|
|
```
|
|
|
|
`nginx: worker process`가 `cardreader`로 표시되어야 한다.
|
|
|
|
카드 읽기 확인:
|
|
|
|
```bash
|
|
docker exec playuniare-web \
|
|
su -s /bin/sh cardreader -c \
|
|
'head -c 10 /srv/uniare-card-images/ua01/EX02BT_CGH-2-007.png >/dev/null && echo READ_OK'
|
|
```
|
|
|
|
## 데이터 마이그레이션
|
|
|
|
현재는 데이터베이스가 없으므로 마이그레이션할 데이터도 없다. 방 상태 저장,
|
|
사용자 계정 또는 덱 저장 데이터베이스가 추가되면
|
|
`docs/migrations.md`의 원칙을 따른다.
|
|
|
|
NAS에서 SQL이나 임시 명령을 직접 입력하는 방식은 초기에는 빠르지만, 같은
|
|
변경을 다시 재현하거나 복구하기 어렵다. 운영 데이터가 생긴 뒤에는 반드시
|
|
Git에 포함된 버전 관리 마이그레이션 코드를 실행하는 방식으로 전환한다.
|
|
|
|
## 배포 체크리스트
|
|
|
|
- [ ] 로컬 `npm run build` 성공
|
|
- [ ] 필요한 로컬 Docker 검증 성공
|
|
- [ ] 관련 문서 업데이트
|
|
- [ ] Git 커밋과 푸시 완료
|
|
- [ ] NAS `git pull` 완료
|
|
- [ ] 운영 컨테이너 재빌드
|
|
- [ ] 컨테이너 상태와 로그 확인
|
|
- [ ] `http://playuniare.sori.studio` 확인
|
|
- [ ] `/card-assets/<series>/<fileName>` 확인
|
|
- [ ] 두 브라우저의 방 생성·참가와 준비 상태 동기화 확인
|
|
- [ ] `.env`, 토큰, 비밀번호가 Git에 없는지 확인
|
|
|
|
## 실시간 서버 구조
|
|
|
|
브라우저에 Socket.IO 서버 포트를 직접 공개하지 않는다.
|
|
|
|
```text
|
|
브라우저
|
|
→ http://playuniare.sori.studio/socket.io
|
|
→ Play Uniare Nginx
|
|
→ Docker 내부 Socket.IO 서버:31873
|
|
```
|
|
|
|
Docker 내부에서는 다른 컨테이너와 같은 포트를 사용해도 충돌하지 않는다.
|
|
Socket.IO 서비스는 `ports` 대신 내부 통신용 `expose`를 사용한다.
|
|
|
|
실시간 서버 로그만 확인하려면 다음 명령을 사용한다.
|
|
|
|
```bash
|
|
docker compose \
|
|
-f compose.yaml \
|
|
-f compose.nas.yaml \
|
|
logs --tail=100 playuniare-server
|
|
```
|
|
|
|
현재 방 상태는 서버 메모리에만 저장된다. `playuniare-server`를 재시작하거나
|
|
재배포하면 진행 중인 방은 종료되므로, 이용자가 없는 시간에 업데이트한다.
|