Next.js 13 Elastic Beanstalk 자동 배포

Jiwoo JEONG·2023년 6월 10일
3
post-thumbnail

들어가며

  • Next.js을 Elastic Beanstalk Node.js 환경에 Github Action을 통해 자동 배포해요!
  • yarn berry의 PnP zero-install을 적용한 Next.js 어플리케이션을 배포해요!

왜 자동배포를 하였는가?

  • 사내 QA를 위해 stage 서버에서 어플리케이션을 배포하는 상황에서 매 번 수정 ➡️ 배포 하는 과정을 자동화하여 생산성을 향상 시키고 싶었어요.
    ➡️ release/* branch에 commit이 push되면 stage 서버 자동 배포
  • production 배포 시에도 QA가 완료된 버전을 github branch merge를 통해 자동 배포하여 생산성을 향상 시키고 싶었어요.
    ➡️ main branch에 commit이 push되면 production 서버 자동 배포

❗️아래 글은 stage 서버 자동 배포에 관한 github action입니다.

구현

branch 배포 전략

  • release/* 브랜치에 commit이 push되면 해당 action을 실행!
    on:
      push:
        branches:
          - 'release/**'

배포 실행

jobs/set-eb

  • Elastic Beastalk를 사용하기 위해 aws-cligithub action Node 환경에 세팅

  • Node 환경을 18.x로 세팅

         - name: Set up Node ${{ matrix.node-version }}
            uses: actions/setup-node@v3
            with:
              node-version: ${{ matrix.node-version }}
              registry-url: https://registry.npmjs.org/
  • aws-zip file을 캐싱
    ➡️ static한 파일이기 때문에 캐싱처리를 하면 action 속도를 개선할 수 있을 것 같았어요!

     	   - name: Cache AWS CLI
             uses: actions/cache@v3
             with:
               path: /usr/local/aws-cli
               key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/awscliv2.zip') }}
     
           - name: Install AWS CLI 2
             run: |
               curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
               unzip awscliv2.zip
               which aws
               sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update
     
     
  • AWS configuration을 github action secret을 통해 진행
    ➡️ 가장 중요한 건 보안!!

          - name: Configure AWS credentials
             uses: aws-actions/configure-aws-credentials@v1
             with:
               aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
               aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
               aws-region: ${{ secrets.AWS_REGION }}
     

jobs/build

  • 실제 Next.js App 빌드 및 EB deploy
  • Node 환경을 18.x로 세팅.
    ➡️ 16.14 이후 버전부터 yarn berry를 지원하기 때문에 넉넉하게 세팅했어요!
    ➡️ yarn을 통해 node package managing
    ➡️ cache: ‘yarn’ 를 통해 매 번 npm install -g yarn을 하지 않도록 캐싱하여 action 속도를 개선해요!
     	   - name: Set up Node ${{ matrix.node-version }}
             uses: actions/setup-node@v3
             with:
               node-version: ${{ matrix.node-version }}
               registry-url: https://registry.npmjs.org/
     					cache: 'yarn'
     
  • yarn berry 세팅
    ➡️ corepack을 통해 yarn을 사용할 수 있도록해요!
    ➡️ corepacknode v16.9.0, v14.19.0부터 기본 포함된 실험적 기능으로 yarn, pnpm 같은 package manager를 프로젝트별로 지정하여 사용할 수 있게 해요.
    ➡️ yarn set version berry 를 통해 yarn berry를 세팅해요.
    	  - name: Set yarn berry
            run: |
              corepack enable
              yarn set version berry
              yarn install
  • .envgithub action secret을 통해 생성.
    • .env 파일에 추가되는 환경 변수는 github action secretgithub action yml 파일에도 추가 해야해요

      	  - name: Generate .env
              run: |
                echo "NEXT_PUBLIC_ENV=$NEXT_PUBLIC_ENV" >> .env
                echo "NEXT_PUBLIC_INQUIRY_SLACK_HOOK_URL=$NEXT_PUBLIC_INQUIRY_SLACK_HOOK_URL" >> .env
                echo "NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=$NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN" >> .env
              env:
                NEXT_PUBLIC_ENV: ${{ secrets.NEXT_PUBLIC_ENV_STAGE }}
                NEXT_PUBLIC_INQUIRY_SLACK_HOOK_URL: ${{ secrets.NEXT_PUBLIC_INQUIRY_SLACK_HOOK_URL }}
                NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: ${{ secrets.NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN_STAGE }}
  • build한 어플리케이션을 Elastic Beanstalk 배포를 위해 zip 압축.
    	  - name: Build STAGE
            run: |
              yarn build:stage // package.json script에 존재하는 stage build command
    
          - name: Generate deployment package
            run: zip ./deploy.zip -r * .[^.]*
  • Elastic Beanstalk에 배포
    • version_label이 unique해야해서 timestamp로 우선 지정했어요!

      	  - name: Current timestamp
              id: timestamp
              run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H-%M-%S-%3NZ')"
      
            - name: Deploy to EB
              uses: einaregilsson/beanstalk-deploy@v18
              with:
                aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
                aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
                application_name: ${{ secrets.APPLICATION_NAME }}
                environment_name: ${{ secrets.STAGE_ENVIRONMENT_NAME }}
                region: ${{ secrets.AWS_REGION }}
                version_label: ${{ steps.timestamp.outputs.date }}
                deployment_package: deploy.zip
  • 결과를 Slack webhook으로 전송
    	  - name: Send result to slack
            uses: 8398a7/action-slack@v3
            with:
              status: ${{ job.status }}
              author_name: IMCEO - STAGE
              fields: repo,commit,message,author 
              mention: here
              if_mention: failure,cancelled
            env:
              SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
            if: always()

전체 코드

name: Auto Deploy - STAGE

on:
  push:
    branches:
      - 'release/**'

jobs:
  set-eb:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [ 18.x ]
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up Node ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          registry-url: https://registry.npmjs.org/

      - name: Cache AWS CLI
        uses: actions/cache@v3
        with:
          path: /usr/local/aws-cli
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/awscliv2.zip') }}

      - name: Install AWS CLI 2
        run: |
          curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
          unzip awscliv2.zip
          which aws
          sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [ 18.x ]
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up Node ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          registry-url: https://registry.npmjs.org/
          cache: 'yarn'

      - name: Set yarn berry
        run: |
          corepack enable
          yarn set version berry
          yarn install

      - name: Remove .next
        run: |
          rm -rf .next

      - name: Generate .env
        run: |
          echo "NEXT_PUBLIC_ENV=$NEXT_PUBLIC_ENV" >> .env
          echo "NEXT_PUBLIC_INQUIRY_SLACK_HOOK_URL=$NEXT_PUBLIC_INQUIRY_SLACK_HOOK_URL" >> .env
          echo "NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=$NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN" >> .env
        env:
          NEXT_PUBLIC_ENV: ${{ secrets.NEXT_PUBLIC_ENV_STAGE }}
          NEXT_PUBLIC_INQUIRY_SLACK_HOOK_URL: ${{ secrets.NEXT_PUBLIC_INQUIRY_SLACK_HOOK_URL }}
          NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: ${{ secrets.NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN_STAGE }}

      - name: Build STAGE
        run: |
          yarn build:stage

      - name: Generate deployment package
        run: zip ./deploy.zip -r * .[^.]*

      - name: Current timestamp
        id: timestamp
        run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H-%M-%S-%3NZ')"

      - name: Deploy to EB
        uses: einaregilsson/beanstalk-deploy@v18
        with:
          aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          application_name: ${{ secrets.APPLICATION_NAME }}
          environment_name: ${{ secrets.STAGE_ENVIRONMENT_NAME }}
          region: ${{ secrets.AWS_REGION }}
          version_label: ${{ steps.timestamp.outputs.date }}
          deployment_package: deploy.zip

      - name: Send result to slack
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          author_name: IMCEO - STAGE
          fields: repo,commit,message,author # action,eventName,ref,workflow,job,took 추가할 수 있음
          mention: here
          if_mention: failure,cancelled
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        if: always()

속도 개선

  • 사실 처음부터 yarnaws-cli를 캐싱하지는 않았어요!
  • 그래서 아래의 3가지를 통해 action 속도를 개선했어요!
  1. cache yarn
    • uses: actions/setup-node@v3 에서 cache:'yarn' 을 통해서 yarn 설치 시간 줄임
  2. yarn berry zero install
    • pnp zero install을 통해 github에서 매 번 모듈을 설치하는 과정 개선
  3. set-ebbuild job 분리를 통한 불필요한 동기처리 개선

개선 전 (평균 4m 20s)

개선 후(평균 2m 30s)

➡️ 평균 1m 50s 속도 개선

후기

  • 실제 사내에서 main, release/* branch에 따라 production, stage 자동 배포를 사용하고 있어요 !
  • stage에 매번 배포하는 과정을 자동화하여 생산성이 향상 되어서 QA 배포 과정이 단순화 되었어요!
  • production에 배포할 때, github 모바일 앱을 사용해도 되어서 어디서나 해당 버전을 production 출시할 수 있어서 좋았어요!
profile
FE Developer as Efficiency Maker

1개의 댓글

comment-user-thumbnail
2023년 7월 7일

안녕하세요 너무 좋은글 감사합니다 덕분에 참고하여 빠른 작업 가능했습니다~

여기서 한가지 궁금한 점은 env.cache-name
cache-name을 감추는 이유는 뭔가요?

답글 달기