8.7 KiB
chocoadmin 배포
1. 환경 파일
Synology NAS에서 환경 파일을 생성합니다. 실제 .env.* 파일은 커밋하지 마세요.
스테이지 파일 경로:
/volume1/docker/service/jinaju/chocoadmin/.env.stage
스테이지 예시:
DATABASE_URL="mysql://USER:PASSWORD@mariadb.jisangs.com:30001/chocomae"
AUTH_SECRET="replace-with-stage-secret"
NEXTAUTH_SECRET="replace-with-stage-secret"
AUTH_URL="https://chocoadmin-stage.jisangs.com"
NEXTAUTH_URL="https://chocoadmin-stage.jisangs.com"
운영 파일 경로:
/volume1/docker/service/jinaju/chocoadmin/.env.production
운영 예시:
DATABASE_URL="mysql://CHCOCO_ADMIN_USER:PASSWORD@chocomae.jinaju.com:3306/chocomae"
AUTH_SECRET="replace-with-production-secret"
NEXTAUTH_SECRET="replace-with-production-secret"
AUTH_URL="https://chocoadmin.jinaju.com"
NEXTAUTH_URL="https://chocoadmin.jinaju.com"
AUTH_SECRET과 NEXTAUTH_SECRET은 Auth.js 호환을 위해 각 환경 내에서 동일한 강력한 랜덤 값이어야 합니다. 운영과 스테이지 사이에는 서로 다른 값을 사용하세요.
APP_ENV(production / stage)는 docker-compose*.yml의 environment 블록에서 설정합니다 — env 파일에 넣지 마세요. 이 값이 세션 쿠키 이름(chocoadmin-${APP_ENV}.session-token)을 결정하며, 브라우저를 공유하더라도 운영과 스테이지 쿠키를 분리해 줍니다.
공개 URL이 HTTPS를 사용하면 AUTH_URL과 NEXTAUTH_URL 모두 외부 HTTPS URL과 정확히 일치해야 합니다.
NAS에서 시크릿 생성:
openssl rand -base64 32
2. Synology 스테이지 배포
운영과 스테이지 모두 /volume1/docker/service/jinaju/chocoadmin의 동일한 Git 체크아웃을 공유합니다. 여기에서 pull하세요:
cd /volume1/docker/service/jinaju/chocoadmin-stage
git pull --ff-only
스테이지는 docker-compose.stage.yml, chocoadmin-stage 컨테이너, 외부 Docker 네트워크 proxy-network를 사용합니다.
네트워크 확인 또는 생성:
docker network inspect proxy-network >/dev/null 2>&1 || docker network create proxy-network
스테이지 시작 또는 업데이트:
docker compose -f docker-compose.stage.yml up -d --build
docker compose -f docker-compose.stage.yml ps
docker compose -f docker-compose.stage.yml logs -f
compose 파일에서 서비스 이름을 바꾼 경우, 오래된 컨테이너를 제거하기 위해 --remove-orphans를 한 번 추가하세요.
재배포 후 NPM의 업스트림 DNS 캐시를 비우기 위해 재로드합니다:
docker exec npm nginx -s reload
docker-compose.stage.yml은 호스트 포트 3000을 바인딩하는 대신 컨테이너 포트 3000을 proxy-network에 노출합니다 — 호스트 포트 3000이 Gitea 같은 다른 서비스에서 이미 사용 중일 수 있기 때문입니다.
Nginx Proxy Manager 설정:
- Scheme:
http - Forward Hostname / IP:
chocoadmin-stage - Forward Port:
3000 - Websockets Support: 활성화
- SSL: 활성화
- Force SSL: 활성화
스테이지 URL:
https://chocoadmin-stage.jisangs.com
AUTH_URL, NEXTAUTH_URL 또는 쿠키 관련 설정을 변경한 뒤에는 스테이지 도메인의 브라우저 쿠키를 지우거나 시크릿 창에서 테스트하세요.
3. 운영 배포
운영은 docker-compose.yml을 사용하며 기본적으로 .env.production을 읽습니다:
cd /volume1/docker/service/jinaju/chocoadmin
git pull --ff-only
docker compose up -d --build
docker compose ps
docker compose logs -f chocoadmin
재배포 후 NPM 재로드:
docker exec npm nginx -s reload
Nginx Proxy Manager 설정:
- Scheme:
http - Forward Hostname / IP:
chocoadmin - Forward Port:
3000 - Websockets Support: 활성화
- SSL: 활성화
- Force SSL: 활성화
운영 URL:
https://chocoadmin.jinaju.com
Docker 빌드는 시크릿을 커밋하지 않고도 Next.js가 컴파일될 수 있도록, 빌드 시점 환경 변수를 플레이스홀더로만 사용합니다. 런타임 값은 compose의 env_file에서 읽습니다.
4. AWS EC2 보안 그룹
MariaDB는 Synology NAS 공인 IP에서만 접근하도록 허용합니다.
- Type:
MYSQL/Aurora - Protocol:
TCP - Port:
3306 - Source:
NAS_PUBLIC_IP/32 - Description:
chocoadmin Synology NAS
3306을 0.0.0.0/0으로 열지 마세요.
5. 읽기 전용 리허설
운영 DB에 대해 승인/거절 작업을 활성화하기 전:
- 읽기 전용 권한을 가진 DB 계정을 생성하거나 사용합니다.
.env.production의DATABASE_URL을 해당 읽기 전용 계정으로 설정합니다.- 컨테이너를 시작합니다.
- 로그인, 마에스트로 목록, 연장 신청 목록, 업그레이드 신청 목록을 확인합니다.
- 읽기 화면이 정상 동작한 뒤에만 쓰기 가능한 운영 chocoadmin DB 계정으로 전환합니다.
6. 스모크 체크
배포 후 매번:
docker compose -f docker-compose.stage.yml ps # 스테이지
docker compose ps # 운영
브라우저에서 다음 경로들을 확인합니다:
/login/maestros/extension-requests/upgrade-requests
7. 트러블슈팅
로그인 후 ERR_TOO_MANY_REDIRECTS
미들웨어가 /login으로 리다이렉트하고, 로그인 페이지가 다시 /로 리다이렉트 — 무한 루프입니다. 다음 체크리스트를 순서대로 진행하세요:
AUTH_URL/NEXTAUTH_URL확인 — 스킴을 포함해 공개 HTTPS URL과 정확히 일치해야 합니다.- NPM 스킴 확인 — NPM은 컨테이너로
https가 아닌http스킴으로 포워드해야 합니다. 앱은 들어오는 요청이 아니라AUTH_URL로 HTTPS를 감지합니다. - 쿠키 이름 일관성 확인 —
auth.ts(cookies.sessionToken.name)와proxy.ts(getToken({ cookieName })) 모두 동일한chocoadmin-${APP_ENV}.session-token값을 사용해야 합니다.proxy.ts가 기본 이름을 사용하는데auth.ts는 커스텀 이름을 쓰면, 미들웨어는 세션을 절대 찾지 못합니다. __Secure-접두사 확인 — 붙이지 마세요. 미들웨어는 Node.js 런타임에서 실행되고 Nginx로부터 HTTP를 받습니다.__Secure-쿠키는 HTTP 상에서 조용히 거부됩니다.- 해당 도메인의 브라우저 쿠키를 삭제하고 시크릿 창에서 테스트합니다.
.env.*를 변경한 뒤에는 컨테이너를 재생성합니다.
운영과 스테이지 트래픽이 섞임
증상: chocoadmin.jinaju.com에 로그인했는데 스테이지 UI가 표시되거나, 요청이 두 컨테이너를 번갈아가며 도달함.
원인: Docker는 각 services: 키를 proxy-network의 DNS 별칭으로 등록합니다. 스테이지와 운영이 같은 서비스 이름을 공유하면, NPM의 업스트림이 라운드 로빈으로 두 컨테이너 모두로 매핑됩니다.
네트워크 확인:
docker network inspect proxy-network --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
기대 결과: chocoadmin과 chocoadmin-stage가 서로 다른 IP로 나타남. chocoadmin이 두 번 나타나면, 오래된 컨테이너가 그 별칭을 사용 중입니다.
수정: docker-compose.stage.yml이 services: chocoadmin-stage:(chocoadmin 아님)를 사용하는지 확인하고, 오래된 컨테이너를 정리하기 위해 --remove-orphans로 스테이지를 한 번 재배포한 뒤 NPM을 재로드합니다.
재배포 후 Failed to find Server Action
증상: 새 빌드를 배포한 뒤 버튼(예: 로그아웃)을 클릭하면 404 또는 Failed to find Server Action 에러가 발생함.
원인: 액션이 인라인 "use server" 클로저로 정의되었습니다. 클로저는 빌드마다 새로운 액션 ID를 얻습니다. 브라우저는 오래된 ID를 캐싱합니다.
수정: 강제 새로고침(Cmd+Shift+R / Ctrl+Shift+R)으로 오래된 액션 ID가 담긴 캐시 페이지를 폐기합니다. 배포마다 문제가 반복된다면, 액션을 별도의 actions.ts 파일에서 모듈 레벨 named export로 추출해야 합니다.
컨테이너가 시작하자마자 종료됨
docker compose logs chocoadmin
일반적인 원인:
- 누락되었거나 형식이 잘못된
DATABASE_URL연결 문자열. AUTH_SECRET이 설정되지 않음 — NextAuth가 시작 시점에 예외를 던집니다.- 포트가 이미 할당됨 — 다른 서비스가 호스트 포트 3000을 사용 중인지 확인하세요. 두 compose 파일 모두 포트 3000에
ports가 아닌expose를 사용하므로, compose 파일이 수정되지 않은 한 이런 문제는 발생하지 않아야 합니다.