Files
chocoadmin/docs/deployment.md
T

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_SECRETNEXTAUTH_SECRET은 Auth.js 호환을 위해 각 환경 내에서 동일한 강력한 랜덤 값이어야 합니다. 운영과 스테이지 사이에는 서로 다른 값을 사용하세요.

APP_ENV(production / stage)는 docker-compose*.ymlenvironment 블록에서 설정합니다 — env 파일에 넣지 마세요. 이 값이 세션 쿠키 이름(chocoadmin-${APP_ENV}.session-token)을 결정하며, 브라우저를 공유하더라도 운영과 스테이지 쿠키를 분리해 줍니다.

공개 URL이 HTTPS를 사용하면 AUTH_URLNEXTAUTH_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을 바인딩하는 대신 컨테이너 포트 3000proxy-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

33060.0.0.0/0으로 열지 마세요.

5. 읽기 전용 리허설

운영 DB에 대해 승인/거절 작업을 활성화하기 전:

  1. 읽기 전용 권한을 가진 DB 계정을 생성하거나 사용합니다.
  2. .env.productionDATABASE_URL을 해당 읽기 전용 계정으로 설정합니다.
  3. 컨테이너를 시작합니다.
  4. 로그인, 마에스트로 목록, 연장 신청 목록, 업그레이드 신청 목록을 확인합니다.
  5. 읽기 화면이 정상 동작한 뒤에만 쓰기 가능한 운영 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으로 리다이렉트하고, 로그인 페이지가 다시 /로 리다이렉트 — 무한 루프입니다. 다음 체크리스트를 순서대로 진행하세요:

  1. AUTH_URL / NEXTAUTH_URL 확인 — 스킴을 포함해 공개 HTTPS URL과 정확히 일치해야 합니다.
  2. NPM 스킴 확인 — NPM은 컨테이너로 https가 아닌 http 스킴으로 포워드해야 합니다. 앱은 들어오는 요청이 아니라 AUTH_URL로 HTTPS를 감지합니다.
  3. 쿠키 이름 일관성 확인auth.ts(cookies.sessionToken.name)와 proxy.ts(getToken({ cookieName })) 모두 동일한 chocoadmin-${APP_ENV}.session-token 값을 사용해야 합니다. proxy.ts가 기본 이름을 사용하는데 auth.ts는 커스텀 이름을 쓰면, 미들웨어는 세션을 절대 찾지 못합니다.
  4. __Secure- 접두사 확인 — 붙이지 마세요. 미들웨어는 Node.js 런타임에서 실행되고 Nginx로부터 HTTP를 받습니다. __Secure- 쿠키는 HTTP 상에서 조용히 거부됩니다.
  5. 해당 도메인의 브라우저 쿠키를 삭제하고 시크릿 창에서 테스트합니다.
  6. .env.*를 변경한 뒤에는 컨테이너를 재생성합니다.

운영과 스테이지 트래픽이 섞임

증상: chocoadmin.jinaju.com에 로그인했는데 스테이지 UI가 표시되거나, 요청이 두 컨테이너를 번갈아가며 도달함.

원인: Docker는 각 services: 키를 proxy-network의 DNS 별칭으로 등록합니다. 스테이지와 운영이 같은 서비스 이름을 공유하면, NPM의 업스트림이 라운드 로빈으로 두 컨테이너 모두로 매핑됩니다.

네트워크 확인:

docker network inspect proxy-network --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'

기대 결과: chocoadminchocoadmin-stage가 서로 다른 IP로 나타남. chocoadmin이 두 번 나타나면, 오래된 컨테이너가 그 별칭을 사용 중입니다.

수정: docker-compose.stage.ymlservices: 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 파일이 수정되지 않은 한 이런 문제는 발생하지 않아야 합니다.