diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a908f41 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +.git +.gitignore +.next +.pnpm-store +.turbo +coverage +docker-compose.override.yml +node_modules +playwright-report +test-results + +.DS_Store +*.log +*.tsbuildinfo + +.env +.env.local +.env.*.local diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..2398ea7 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +DATABASE_URL= +REDIS_URL= +NEXTAUTH_SECRET= +NEXTAUTH_URL= diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..0ec3ae1 --- /dev/null +++ b/.env.production.example @@ -0,0 +1,4 @@ +DATABASE_URL= +REDIS_URL= +NEXTAUTH_SECRET= +NEXTAUTH_URL=https://math-quiz.jisangs.com diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a188ccd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# syntax=docker/dockerfile:1 + +FROM node:20-alpine AS base +WORKDIR /app +ENV NEXT_TELEMETRY_DISABLED=1 +RUN corepack enable + +FROM base AS deps +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile + +FROM base AS builder +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN pnpm build + +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +ENV HOSTNAME=0.0.0.0 +ENV PORT=3000 + +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 nextjs + +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +CMD ["node", "server.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f461821 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +services: + math-quiz: + container_name: math-quiz + build: + context: . + dockerfile: Dockerfile + image: math-quiz:latest + restart: unless-stopped + env_file: + - .env.production + environment: + NODE_ENV: production + PORT: 3000 + HOSTNAME: 0.0.0.0 + networks: + - proxy-network + +networks: + proxy-network: + external: true diff --git a/docs/synology-deploy.md b/docs/synology-deploy.md new file mode 100644 index 0000000..c40f23b --- /dev/null +++ b/docs/synology-deploy.md @@ -0,0 +1,148 @@ +# Synology NAS Docker Compose 배포 + +이 문서는 현재 프로젝트를 Synology NAS에서 `math-quiz` 컨테이너로 실행하고, Nginx Proxy Manager(NPM)에서 `math-quiz.jisangs.com`으로 연결하는 절차입니다. + +## 전제 + +- NAS에 Docker 또는 Container Manager가 설치되어 있어야 합니다. +- NPM 컨테이너가 80/443 포트를 사용하고 있어야 합니다. +- `math-quiz.jisangs.com` DNS가 NAS로 연결되어 있어야 합니다. +- NPM 컨테이너와 `math-quiz` 컨테이너가 같은 Docker 네트워크에 있어야 합니다. + +## 1. NPM 공용 네트워크 준비 + +NAS SSH에서 한 번만 실행합니다. + +```bash +docker network create npm-proxy +``` + +이미 같은 이름의 네트워크가 있으면 에러가 나도 괜찮습니다. + +NPM 컨테이너가 이미 실행 중이라면 같은 네트워크에 붙입니다. + +```bash +docker network connect npm-proxy +``` + +예를 들어 NPM 컨테이너 이름이 `nginx-proxy-manager`라면: + +```bash +docker network connect npm-proxy nginx-proxy-manager +``` + +## 2. 프로젝트 파일 업로드 + +NAS에 배포 디렉터리를 만듭니다. + +```bash +mkdir -p /volume1/docker/math-quiz +``` + +로컬 작업 폴더에서 NAS로 프로젝트를 복사합니다. + +```bash +rsync -av --delete \ + --exclude .git \ + --exclude node_modules \ + --exclude .next \ + --exclude .pnpm-store \ + ./ @:/volume1/docker/math-quiz/ +``` + +Git을 NAS에서 직접 사용할 수 있다면 아래 방식도 가능합니다. + +```bash +cd /volume1/docker +git clone math-quiz +``` + +이미 clone 되어 있다면: + +```bash +cd /volume1/docker/math-quiz +git pull +``` + +## 3. 운영 환경변수 작성 + +NAS의 `/volume1/docker/math-quiz/.env.production` 파일을 만듭니다. + +```bash +cd /volume1/docker/math-quiz +cp .env.production.example .env.production +vi .env.production +``` + +현재 구현은 DB/Redis/NextAuth를 아직 사용하지 않지만, 이후 기능 추가를 위해 키는 준비해 둡니다. + +```env +DATABASE_URL= +REDIS_URL= +NEXTAUTH_SECRET= +NEXTAUTH_URL=https://math-quiz.jisangs.com +``` + +`NEXTAUTH_SECRET`을 사용할 때는 아래처럼 생성할 수 있습니다. + +```bash +openssl rand -base64 32 +``` + +## 4. Docker 이미지 빌드 및 실행 + +```bash +cd /volume1/docker/math-quiz +docker compose up -d --build +``` + +상태 확인: + +```bash +docker compose ps +docker logs -f math-quiz +``` + +정상 실행되면 컨테이너 내부에서 Next.js 서버가 `3000` 포트로 떠 있습니다. 외부 80/443은 NPM이 담당하므로 `docker-compose.yml`에는 호스트 포트 노출을 두지 않았습니다. + +## 5. Nginx Proxy Manager 설정 + +NPM 관리자 화면에서 Proxy Host를 추가합니다. + +- Domain Names: `math-quiz.jisangs.com` +- Scheme: `http` +- Forward Hostname / IP: `math-quiz` +- Forward Port: `3000` +- Websockets Support: 필요 시 활성화 +- SSL: Let's Encrypt 인증서 발급 +- Force SSL: 활성화 + +NPM 컨테이너와 `math-quiz` 컨테이너가 `npm-proxy` 네트워크를 공유해야 `Forward Hostname`에 `math-quiz`를 사용할 수 있습니다. + +## 6. 업데이트 반영 + +로컬에서 변경한 파일을 다시 rsync 하거나 NAS에서 `git pull` 후 재빌드합니다. + +```bash +cd /volume1/docker/math-quiz +docker compose up -d --build +docker image prune -f +``` + +로그 확인: + +```bash +docker logs -f math-quiz +``` + +## 참고: 네트워크 이름이 다를 때 + +NPM에서 이미 쓰는 Docker 네트워크 이름이 있다면 `docker-compose.yml`의 `npm-proxy`를 해당 네트워크 이름으로 바꿉니다. + +```yaml +networks: + npm-proxy: + external: true +``` + +위 블록의 `npm-proxy` 이름과 서비스의 `networks` 값을 같은 이름으로 맞추면 됩니다. diff --git a/next.config.mjs b/next.config.mjs index 4678774..8c8bab6 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,6 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + output: "standalone", +}; export default nextConfig;