diff --git a/new/22-2.md b/new/22-2.md new file mode 100644 index 0000000..03bdece --- /dev/null +++ b/new/22-2.md @@ -0,0 +1,144 @@ +## Standalone +> TL;DR +> +> Next.js 에서 지원하는 standalone 옵션을 이용해 Docker image 사이즈를 최소화하고 배포 시간을 줄이는 방법에 대해 알아봅니다 + +### Standalone +> standalone은 사전적으로 '독립형' 이라는 의미 + +- Next.js 에서는 애플리케이션을 실행하는데 필요한 최소한의 코드만 추출하겠다는 의미로 사용 +- [공식문서](https://nextjs.org/docs/app/api-reference/next-config-js/output#automatically-copying-traced-files)을 살펴보면, standalone 옵션을 켜면 `.next/standalone` 폴더가 생성된다. + +``` js +module.exports = { + output: 'standalone', +}; +``` +- standalone 옵션은 배포 환경에서 웹앱을 실행할 때 필요없는 코드는 빌드 결과물에서 제외한다. + +--- + +- 이 옵션을 통해 Docker image 사이즈를 줄일 수 있고, 배포 시간을 줄일 수 있다. +- 여러 방식을 통해 웹앱을 배포할 수 있지만, CI/CD 파이프라인과 Docker image를 통해 배포하는 경우 위 옵션은 효과적이다. + - 배포 방식 예시 + - Jenkins CI/CD 파이프라인을 이용해 Amazon ECS Cluster 에 Docker image 를 배포 + - CD 파이프라인에서는 생성된 Docker image 를 빌드한 뒤, + - ECR(Amazon Elastic Container Registry)에 push + - 따라서 Docker image 의 사이즈가 작아지면, ECR 에 push & pull 하는 시간이 감소되어 배포 시간이 단축 + +- Next.js 공식 문서에서 docker를 사용해서 애플리케이션을 빌드한 경우, Dockerfile의 예시 파일이 있다. + - [참고 예시 Dockerfile](https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile) + - Dockerfile을 작성하기 귀찮을 때는 아래 예시 파일을 사용해도 좋지만 이미지 크기가 커져서 수정해서 쓰는 것이 좋다. + ``` + FROM node:18-alpine AS base + + # Install dependencies only when needed + FROM base AS deps + # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. + RUN apk add --no-cache libc6-compat + WORKDIR /app + + # Install dependencies based on the preferred package manager + COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ + RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ + else echo "Lockfile not found." && exit 1; \ + fi + + + # Rebuild the source code only when needed + FROM base AS builder + WORKDIR /app + COPY --from=deps /app/node_modules ./node_modules + COPY . . + + # Next.js collects completely anonymous telemetry data about general usage. + # Learn more here: https://nextjs.org/telemetry + # Uncomment the following line in case you want to disable telemetry during the build. + # ENV NEXT_TELEMETRY_DISABLED 1 + + RUN \ + if [ -f yarn.lock ]; then yarn run build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + + # Production image, copy all the files and run next + FROM base AS runner + WORKDIR /app + + ENV NODE_ENV production + # Uncomment the following line in case you want to disable telemetry during runtime. + # ENV NEXT_TELEMETRY_DISABLED 1 + + RUN addgroup --system --gid 1001 nodejs + RUN adduser --system --uid 1001 nextjs + + COPY --from=builder /app/public ./public + + # Set the correct permission for prerender cache + RUN mkdir .next + RUN chown nextjs:nodejs .next + + # Automatically leverage output traces to reduce image size + # https://nextjs.org/docs/advanced-features/output-file-tracing + COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ + COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + + USER nextjs + + EXPOSE 3000 + + ENV PORT 3000 + + # server.js is created by next build from the standalone output + # https://nextjs.org/docs/pages/api-reference/next-config-js/output + CMD HOSTNAME="0.0.0.0" node server.js + ``` + + +#### standalone 적용 + +- 로컬에서 배포용 Docker 이미지를 실행하면 standalone 이 잘 적용되었는지 확인 가능 +- `next build`를 실행하여 빌드 결과물을 생성한 뒤 다음 명령어 실행 + +``` bash +Docker build . --target {특정 빌드 스테이지} -t {이미지_이름:태그} +Docker run -p {호스트 포트}:{컨테이너 포트} {이미지_이름:태그} +``` + +- 서버 실행을 위해서는 다음 명령어 실행 + +``` bash +node standalone/server.js +``` + +- `server.js` + - 실제 `.next`의 실행파일들을 기반으로 애플리케이션을 구동시켜주는 진입점 + + +- 빌드 전 미리 로드해야하는 라이브러리가 있다면 Dockerfile을 다음을 추가 + - 현재 빌드 컨텍스트의 파일을 Docker 이미지 내의 디렉토리로 복사하고, 그 파일의 소유자와 그룹을 node로 설정 +``` +COPY --chown=node:node {현재 Docker 빌드 컨텍스트에서 source 파일 경로} {복사될 대상의 Docker image 내부 경로} + +ENV LOG_LEVEL debug +ENV DD_LOGS_INJECTION true +... +ENV NODE_OPTIONS "-r ~/preload.js" // 해당 파일 경로로 설정 필요 +ENTRYPOINT ["node", "~/server.js"] // 해당 파일 경로로 설정 필요 +``` + +#### /static, /public 복사 +- standalone 내에는 `/static`, `/public` 이 존재하지 않는다 +- 따라서 공식문서에는 해당 폴더들을 CDN으로 관리할 것을 권장한다 +- 하지만 별도의 CDN으로 서빙하지 않는 경우에는 `/static`, `/public`을 직접 복사를 해야한다. + +``` +COPY --chown=node:node ~/.next/standalone ./ +COPY --chown=node:node ~/public ./~/public +COPY --chown=node:node ~/.next/static ./~/.next/static +```