# UGREEN NAS 운영 배포 안내 ## 운영 원칙 - 기능 테스트는 개발 Mac에서 수행한다. - NAS에는 운영 컨테이너만 실행한다. - 로컬 빌드와 Docker 검증을 통과한 커밋만 NAS에 배포한다. - NAS에서 소스 코드를 직접 수정하지 않는다. - 운영 주소는 현재 `http://playuniare.sori.studio`다. - HTTPS는 인증서와 리버스 프록시 구성이 완료된 뒤 전환한다. 현재 프로젝트는 Vue 정적 프런트엔드만 포함한다. 실시간 서버가 추가되면 같은 Compose 프로젝트 안에 Node.js 서비스를 추가한다. ## 배포 구조 ```text 사용자 브라우저 ↓ http://playuniare.sori.studio ↓ NAS 리버스 프록시 ↓ 127.0.0.1:41809 ↓ Play Uniare Nginx 컨테이너 ├── / → Vue 빌드 결과 └── /card-assets/ → 기존 Uniare 카드 이미지 ``` 카드 이미지는 복사하지 않고 다음 폴더를 읽기 전용으로 연결한다. ```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 VITE_SOCKET_SERVER_URL=http://playuniare.sori.studio ``` 현재 Socket.IO 서버는 구현 전이므로 `VITE_SOCKET_SERVER_URL`은 아직 실제 기능에 사용되지 않는다. ### 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`이 `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/ua03/EX01BT_HTR-2-001.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/ua03/EX01BT_HTR-2-001.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//` 확인 - [ ] `.env`, 토큰, 비밀번호가 Git에 없는지 확인 ## 향후 실시간 서버 Socket.IO 서버가 추가되면 브라우저에 서버 포트를 직접 공개하지 않는다. ```text 브라우저 → http://playuniare.sori.studio/socket.io → Play Uniare Nginx → Docker 내부 Socket.IO 서버:31873 ``` Docker 내부에서는 다른 컨테이너와 같은 포트를 사용해도 충돌하지 않는다. Socket.IO 서비스는 `ports` 대신 내부 통신용 `expose`를 사용하는 방향을 우선한다.