nextjs 14 docker, ec2, gitaction, runner 배포
🏞️

nextjs 14 docker, ec2, gitaction, runner 배포

작성일
2024년 06월 20일
태그
nextjs
docker
AWS
github
카테고리
docker
Last edited time
Last updated October 20, 2024
날짜
 
 
nextjs docker이미지를 생성하기위한 dockerfile이며 nextjs 에서 예시로 올려둠. ec2와 S3버킷, 코드푸쉬를 사용한 방법은 정적인 페이지를 배포할때 유용하다. 처음에는 버킷으로 배포하려 했는데, 에로사항이 있어서 git hub runner 를 사용하기로 결정함.
git hub runner를 이용한 위의 방법이 더 직관적이고 간편하게 배포가능 (깃액션을 사용하기때문에 월 한도량 초과시 요금청구)
 
 

git hub runner 간단 사용법

 
docker file, nextjs공식 페이지에서 확인가능.
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
nextjs Dockerfile 해석
 

베이스 이미지 설정

FROM node:18-alpine AS base
  • node:18-alpine 이미지를 기반으로 새로운 이미지를 생성합니다. Alpine은 경량의 리눅스 배포판으로, 이미지 크기를 줄이는 데 유리합니다.
  • AS base: 이 이미지를 base라는 이름으로 태깅하여 이후 단계에서 참조할 수 있게 합니다.

의존성 설치 단계

FROM base AS deps
  • 이전 단계에서 생성한 base 이미지를 deps라는 이름으로 태깅하여 사용합니다.
RUN apk add --no-cache libc6-compat
  • Alpine 패키지 관리자인 apk를 사용하여 libc6-compat 라이브러리를 설치합니다. 이 라이브러리는 일부 Node.js 패키지에서 필요할 수 있습니다.
  • -no-cache 옵션은 패키지 캐시를 사용하지 않도록 하여 이미지 크기를 줄입니다.
WORKDIR /app
  • 작업 디렉토리를 /app으로 설정합니다. 이후 명령어들은 이 디렉토리 내에서 실행됩니다.

패키지 매니저에 따른 의존성 설치

COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
  • 현재 디렉토리의 package.json, yarn.lock, package-lock.json, pnpm-lock.yaml 파일을 이미지의 /app 디렉토리로 복사합니다.
  • 는 파일이 존재하면 복사하고, 존재하지 않으면 무시합니다.
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
  • RUN 명령어는 쉘 명령어를 실행합니다.
  • if [ -f yarn.lock ]; then yarn --frozen-lockfile; \\: yarn.lock 파일이 존재하면 yarn --frozen-lockfile 명령어를 실행하여 Yarn 패키지를 설치합니다.
  • elif [ -f package-lock.json ]; then npm ci; \\: package-lock.json 파일이 존재하면 npm ci 명령어를 실행하여 npm 패키지를 설치합니다.
  • elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \\: pnpm-lock.yaml 파일이 존재하면 corepack enable pnpm 명령어를 실행하여 PNPM을 활성화하고, pnpm i --frozen-lockfile 명령어를 실행하여 PNPM 패키지를 설치합니다.
  • else echo "Lockfile not found." && exit 1; \\: 위의 세 파일 중 어느 것도 존재하지 않으면 "Lockfile not found." 메시지를 출력하고, exit 1로 종료하여 빌드를 실패하게 합니다.

소스 코드 복사 및 빌드 단계

FROM base AS builder
  • base 이미지를 builder라는 이름으로 태깅하여 사용합니다.
WORKDIR /app
  • 작업 디렉토리를 /app으로 설정합니다.
COPY --from=deps /app/node_modules ./node_modules
  • deps 이미지에서 /app/node_modules 디렉토리를 현재 디렉토리(./node_modules)로 복사합니다.
COPY . .
  • 현재 디렉토리의 모든 파일을 이미지의 현재 작업 디렉토리(/app)로 복사합니다.

Next.js 텔레메트리 설정 (선택 사항)

# ENV NEXT_TELEMETRY_DISABLED 1
  • 주석 처리된 환경 변수 설정으로, Next.js 텔레메트리를 비활성화할 수 있습니다.

빌드 명령어 실행

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
  • RUN 명령어는 빌드 명령어를 실행합니다.
  • if [ -f yarn.lock ]; then yarn run build; \\: yarn.lock 파일이 존재하면 yarn run build 명령어를 실행하여 빌드를 수행합니다.
  • elif [ -f package-lock.json ]; then npm run build; \\: package-lock.json 파일이 존재하면 npm run build 명령어를 실행하여 빌드를 수행합니다.
  • elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \\: pnpm-lock.yaml 파일이 존재하면 corepack enable pnpm 명령어를 실행하여 PNPM을 활성화하고, pnpm run build 명령어를 실행하여 빌드를 수행합니다.
  • else echo "Lockfile not found." && exit 1; \\: 위의 세 파일 중 어느 것도 존재하지 않으면 "Lockfile not found." 메시지를 출력하고, exit 1로 종료하여 빌드를 실패하게 합니다.

프로덕션 이미지 설정

FROM base AS runner
  • base 이미지를 runner라는 이름으로 태깅하여 사용합니다.
WORKDIR /app
  • 작업 디렉토리를 /app으로 설정합니다.
ENV NODE_ENV production
  • 환경 변수를 NODE_ENV=production으로 설정합니다.

Next.js 텔레메트리 설정 (선택 사항)

# ENV NEXT_TELEMETRY_DISABLED 1
  • 주석 처리된 환경 변수 설정으로, Next.js 텔레메트리를 비활성화할 수 있습니다.

시스템 사용자 및 그룹 설정

RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs
  • 시스템 그룹 nodejs를 생성합니다.
  • 시스템 사용자 nextjs를 생성하고, uid를 1001로 설정합니다.

빌드된 파일 복사

COPY --from=builder /app/public ./public
  • builder 이미지에서 /app/public 디렉토리를 현재 디렉토리(./public)로 복사합니다.

prerender 캐시 권한 설정

RUN mkdir .next RUN chown nextjs:nodejs .next
  • .next 디렉토리를 생성합니다.
  • .next 디렉토리의 소유권을 nextjs 사용자와 nodejs 그룹으로 변경합니다.

빌드 산출물 복사

COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
  • builder 이미지에서 /app/.next/standalone 디렉토리를 현재 디렉토리(./)로 복사하고, 소유권을 nextjs:nodejs로 변경합니다.
  • builder 이미지에서 /app/.next/static 디렉토리를 현재 디렉토리(./.next/static)로 복사하고, 소유권을 nextjs:nodejs로 변경합니다.

사용자 설정 및 포트 노출

USER nextjs
  • 이후 명령어들은 nextjs 사용자로 실행됩니다.
EXPOSE 3000
  • 컨테이너에서 포트 3000을 외부에 노출합니다.

환경 변수 설정

ENV PORT 3000
  • 환경 변수를 PORT=3000으로 설정합니다.

애플리케이션 시작

CMD HOSTNAME="0.0.0.0" node server.js
  • HOSTNAME 환경 변수를 0.0.0.0으로 설정하고, node server.js 명령어를 실행하여 애플리케이션을 시작합니다.

요약

이 Dockerfile은 Node.js 18을 기반으로 Next.js 애플리케이션을 빌드하고, 최적화된 프로덕션 이미지를 생성하여 실행하는 과정을 정의합니다. 이를 통해 애플리케이션을 도커 컨테이너에서 실행할 수 있습니다.
 
 
위의 도커파일을 nextjs 루트에 생성.

notion image
  1. github setting에서 러너선택
 
 
notion image
  1. 상단의 new self hosted runner를 선택
 
 
notion image
리눅스를 선택한뒤 (ec2를 우분투로 생성해야함)
 
위의 명령을 ec2 쉘에 모두 입력하면
notion image
위와같이 action이 돌기 시작한다 백그라운드로 실행시켜주자.
이후
notion image
위와같이 runner가 도는것을 확인할 수 있으며
옆의 태그는 git action을 작성할때 사용된다.
 
 
또한 해당 git action은
기본 분기의 마지막 커밋” 에만 트리거됨.
 
이부분을 인지하지 못해서 프로젝트 편의성 초반에 default 분기를 dev로 설정해놨었는데 덕분에 mian에 아무리 커밋을 쌓아도 안되서
왜 트리거가 안되지하고 하루종일 삽질했습니다…
notion image
 
이후 도커허브에 업로드하는 git action을 설정해준다.
도커 파일에 적힌 내용에 따라 이미지를 빌드하고 도커허브에 업로드해준다.
name: Deploy on: push: branches: - main jobs: build: runs-on: ubuntu-latest steps: - name: Checkout source code uses: actions/checkout@v3 - name: Log in to DockerHub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME_FRONT }} password: ${{ secrets.DOCKERHUB_PASSWORD_FRONT }} - name: Build the Docker image run: docker build -t ajvls98/nextjs-app . - name: Tag the Docker image run: docker tag ajvls98/nextjs-app:latest ajvls98/nextjs-app:latest - name: Push to DockerHub run: docker push ajvls98/nextjs-app:latest
 
 
위에서 도커허브에 업로드된 최신 이미지를 ec2에서 pull 받고 run 하는 action
name: CD on: workflow_run: workflows: ["Deploy"] types: - completed jobs: build: runs-on: self-hosted steps: - name: Pull Docker image run: sudo docker pull ajvls98/nextjs-app:latest - name: Stop and Remove Old Docker container run: | sudo docker stop nextjs-app || true sudo docker rm nextjs-app || true - name: Run Docker container run: sudo docker run -d -p 3000:3000 --name nextjs-app ajvls98/nextjs-app:latest
 
깃허브에 코드푸쉬 → 도커허브에 업로드 → Deploy(action이름) 가 complet되면 깃허브 runner 실행 → ac2에 해당 깃액션 명령어 원격으로 실행
 
 
백엔드로 나와있지만 동일한 아키텍쳐.
notion image
 
 
 
이렇게 간단한것을 s3와 버킷 코드푸쉬를 통해 배포한다고 고생고생을 했었다 ㅠㅠ..