본문 바로가기

programming

[1/3] Next.js + AWS ECS 컨테이너 배포하기 : CodePipeline 활용한 CI/CD 자동화와 Docker 빌드 최적화 사례 분석

⚠️ 주의: 이 포스팅을 포함한 총 3편의 Next.js 배포 최적화 시리즈는 제가 실제 환경에서 시행착오를 겪으며 얻은 경험과 지식을 정리한 글입니다.

이 글들은 완벽한 정답이 아니라, 다양한 시도와 시행착오를 통한 저의 삽질기이자 성장 과정이며, 동시에 나와 비슷한 고민을 하는 사람들과 지식을 나누고자 하는 목적으로 작성되었습니다.

따라서, 가능하면 3편의 글을 모두 읽고, 환경과 상황에 맞춰 신중히 테스트한 후 적용하는 것을 추천합니다.

마지막까지 읽으시면 무엇이 문제였는지, 그리고 어떻게 해결했는지 확인할 수 있습니다! 🚨🚧✨

 

📋 총 3편의 글 리스트 

https://tacit.tistory.com/267

 

Next.js + AWS ECS 컨테이너 배포하기 : CodePipeline 활용한 CI/CD 자동화와 Docker 빌드 최적화 사례 분석 [1

⚠️ 주의: 이 포스팅을 포함한 총 3편의 Next.js 배포 최적화 시리즈는 제가 실제 환경에서 시행착오를 겪으며 얻은 경험과 지식을 정리한 글입니다.이 글들은 완벽한 정답이 아니라, 다양한 시도

tacit.tistory.com

https://tacit.tistory.com/269

 

Next.js + AWS ECS 컨테이너 배포하기 : CodePipeline 활용한 CI/CD 자동화와 Docker 빌드 최적화 사례 분석 [2

⚠️ 주의: 이 포스팅을 포함한 총 3편의 Next.js 배포 최적화 시리즈는 제가 실제 환경에서 시행착오를 겪으며 얻은 경험과 지식을 정리한 글입니다.이 글들은 완벽한 정답이 아니라, 다양한 시도

tacit.tistory.com

https://tacit.tistory.com/271

 

Next.js + AWS ECS 컨테이너 배포하기 : CodePipeline 활용한 CI/CD 자동화와 Docker 빌드 최적화 사례 분석 [3

1. 원인 분석캐시 이미지 생성·업로드·다운로드 과정에서 소요되는 시간 때문에 Docker 레이어 캐싱 전략이 오히려 비효율적이었습니다.npm ci 명령어의 캐싱 불확실성으로 인해 Docker 레이어 캐싱

tacit.tistory.com

 


🚀 Next.js AWS ECS 배포 프로세스 개선하기

1. 현재 빌드 및 배포 프로세스 개요

Next.js 애플리케이션을 AWS ECS에 컨테이너로 배포.

CI/CD 파이프라인은 AWS CodeBuild를 활용하여 아래와 같은 작업을 수행.

  • Docker 이미지 빌드
  • Amazon ECR 업로드
  • ECS에서 컨테이너 실행

📁 빌드 관련 파일 및 폴더 구조

빌드 및 배포를 위한 스크립트와 설정 파일은 buildspec 디렉토리에서 관리.

개선 작업시 아래 파일들에서 변경이 발생.

buildspec/
├── buildspec-build.yml   # AWS CodeBuild 빌드 단계 정의
├── Dockerfile            # Docker 이미지 빌드를 위한 정의
├── pre_build.sh          # 빌드 전 환경 설정 스크립트
└── post_build.sh         # ECR 푸시 스크립트

.dockerignore
next.config.js

 

2. 📌 빌드 및 배포 프로세스 단계

전체 과정은 buildspec-build.yml 파일에서 명시된다.

version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 20
    run-as: root
    commands:
      - update-ca-trust
      - node -v

  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
      - IMAGE_TAG="v-$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | head -c 8)"
      - curl -L https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 -o /usr/local/bin/jq
      - chmod a+x /usr/local/bin/jq
      - chmod +x locale.sh buildspec/pre_build.sh
      - DOCKERFILE_PATH=buildspec/Dockerfile

  build:
    commands:
      - sh locale.sh
      - sh buildspec/pre_build.sh
      - docker build -f $DOCKERFILE_PATH -t $IMAGE_REPO_NAME:$IMAGE_TAG .

  post_build:
    commands:
      - chmod +x buildspec/post_build.sh
      - sh buildspec/post_build.sh

cache:
  paths:
    - "node_modules/**/*"

artifacts:
  files:
    - imagedefinitions.json

 

1) install 단계

  • Node.js 런타임(Node v20) 및 시스템 인증서 업데이트

2) pre_build 단계

  • Amazon ECR 로그인
  • Git 해시를 기반으로 Docker 이미지 태그 생성
  • jq 및 스크립트 권한 설정

3) build 단계

  • 환경 설정 스크립트 실행
  • Docker 이미지 빌드

4) post_build 단계

  • Docker 이미지를 ECR로 푸시
  • ECS 배포용 imagedefinitions.json 생성

 

3. 개선 내용

1) pre_build.sh 파일의 변경점 및 개선 효과

역할

  • 환경 변수 설정(dev, prod 구분)

Before/After

Before

npm install
npm run build

 

After

# npm install 및 npm run build 제거

📌 무엇이 달라졌는가?

작업 Before (pre_build.sh) After (Dockerfile)
npm install ✅ 포함됨 ✅  Dockerfile에서 처리
npm run build ✅ 포함됨 ✅   Dockerfile에서 처리

📌 변경 이유

  • 📍 책임 분리 명확화
    • 환경 설정(pre_build.sh), 이미지 빌드(Dockerfile)
  • 📍 Docker 캐시 활용으로 빌드 속도 향상

2) Dockerfile의 변경점 및 개선 효과

역할

  • 소스 코드를 컨테이너 이미지로 빌드

Before/After 비교

Before

FROM node:20.5.1-alpine3.18
WORKDIR application
COPY . ./
EXPOSE 3000
CMD ["node", "run_server.js"]

 

After (멀티스테이지 빌드)

FROM node:20.5.1-alpine3.18 AS builder
WORKDIR /application
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20.5.1-alpine3.18
WORKDIR /application
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /application/.next/standalone ./
COPY --from=builder /application/.next/static ./.next/static
COPY --from=builder /application/next.config.js ./
COPY --from=builder /application/run_server.js ./
ENV NEXT_TELEMETRY_DISABLED=1
EXPOSE 3000
CMD ["node", "run_server.js"]

📌 주요 변경사항

  • 멀티스테이지 빌드 적용
  • 개발 및 운영 의존성 분리 설치

📌 변경 이유 및 장점

  • 🎯 이미지 크기 감소로 배포 효율성 향상
  • 🎯 Docker 캐시 최적화로 빌드 속도 단축

3) next.config.js 에 "standalone"

  • Next.js의 standalone 설정 활용
  • 서비스에 필요한 파일만 결과물에 포함
output : standalone

4) .dockerignore 추가 효과

  • 불필요한 파일 제외 → 빌드 속도 향상
node_modules
.next
.git
.gitignore
Dockerfile
docker-compose.yml
README.md

 

4. 📈 CodePipeline 빌드 로그로 개선 효과 확인

  • 빌드 시간: 기존 7~8분 -> 4~5분으로 단축**
  • npm 패키지 변경 시에는 캐시가 무효화(no-cache)되어 다시 7~8분 소요


🚨 하지만, 모든 게 해결된 줄 알았는데...?

드디어 빌드 시간 7분대에서 4분대로 단축하며 🎉 완벽히 성공했다고 자축하던 순간,
예상치 못한 충격적인 문제를 발견했다! ⚠️

  • "캐싱이 성공했는데, 왜 다시 느려진 거지...?"
  • 동일한 빌드 인스턴스 내에서만 적용되는 제한적 캐싱이라는 함정 🪤
  • 성공했던 빌드가 다른 환경에서는 다시 원점(7분대)으로 돌아가는 현상 발생 😱

 

 

과연, 무엇이 잘못된 걸까? 그리고 어떻게 해결할 수 있을까? 🤔

다음 포스팅에서 CI/CD 환경의 캐싱의 숨겨진 진실을 파헤치고,
지속가능한 캐시 최적화 전략을 완성합니다. 🛠️🔥

 

이어서...

https://tacit.tistory.com/269

 

Next.js + AWS ECS 컨테이너 배포하기 : CodePipeline 활용한 CI/CD 자동화와 Docker 빌드 최적화 사례 분석 [2

⚠️ 주의: 이 포스팅을 포함한 총 3편의 Next.js 배포 최적화 시리즈는 제가 실제 환경에서 시행착오를 겪으며 얻은 경험과 지식을 정리한 글입니다.이 글들은 완벽한 정답이 아니라, 다양한 시도

tacit.tistory.com