일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 |
- useMemo
- Github Actions
- visual test
- next-auth
- Flutter
- next.js
- 이메일 인증
- kakao blind recruitment
- typescript
- suffixicon
- Vanilla JavaScript
- storybook
- Props Drilling
- React.memo
- Python
- React-hook-form
- custom hook
- 백준
- ZOD
- javascript
- interaction test
- context api
- 사탕게임
- 피보나치 함수
- locale data
- TextFormField
- useEffect
- 프로그래머스
- react
- 리팩토링
- Today
- Total
Dev Diary
Next.js 프로젝트의 Docker image 빌드시 Size 줄이기 본문
서문
K-paas 공모전에 참가하며 Docker image를 빌드하여 배포를 시도해야하는 상황이 생겼다. 지금까지 나름 다수의 프로젝트를 진행해봤지만 DevOps라 해봤자 겨우 AWS의 S3와 CloudFront를 활용해서 프론트엔드 배포를 진행하거나 Vercel를 이용하고, Github Action을 이용해 간단한 배포 자동화를 구현해본 경험이 전부였는데 갑자기 Docker와 Kubernetes를 사용해야했다. 잘되었다. 마침 새로운 공부가 하고싶었는데 이김에 Docker에 대한 공부를 해보자!
일단 도전
Docker란 것을 사용해본 경험이 전무했지만, 일단 검색을 통해 Docker Desktop 버전을 설치하고 계정도 생성하였다. 여기까지 왔다면 이미지를 빌드하는건 생각보다 간단해보였다.
먼저, Dockerfile이란 것에 대해 알아야하는데 Dockerfile이란 컨테이너 내부에 설치할 소프트웨어나 설정값, 특정 명령 등을 명시해두는 스크립트 형태의 파일이다. Dockerfile을 작성해야 Docker image를 빌드할 수 있다.
나름대로 검색을 통해 아래와 같은 Dockerfile을 작성해보았다.
FROM node:alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
그리고 터미널에서 아래 명령을 실행한다.
docker build -t beank99/bk/frontend .
- docker build: Docker 이미지를 빌드해라
- -t: 이미지명과 태그명을 붙여라
- beank99/bk/frontend: 내가 직접 지은 이미지 이름 (그냥 간단하게 frontend 라고만 해도 괜찮음 자유.)
- .: 현재 작업 디렉토리에서 Dockerfile을 찾아 이것을 바탕으로 이미지를 빌드해라
참고로 위 명령을 실행하려고 했는데 아래와 같은 오류가 계속 발생했다
마지막에 현재 작업 디렉토리를 의미하는 .
을 기입했어야하는데 안적어서 발생한 오류이다^^!
결과적으로 run 명령을 이용해 빌드한 Docker image를 실행해보았는데 잘~ 실행되었고, 별 문제도 없었다!
그래서 끝인줄 알았는데...
내 이미지는 용량이 왜 이렇게 커?
조금 뒤 친구가 빌드한 서버의 Docker image를 봤는데 용량이 480MB 였다. 근데 내가 빌드한 프론트엔드 Docker image는 용량이 무려 1.14GB였다. 거의 3배 차이가 나버리니 "아니 쟤는 서버고 나는 프론트엔드라서 그런가? 프론트는 원래 이렇게 용량이 큰건가?"하는 생각과 함께 뭔가 찜찜한 느낌이 들었다.
바로 검색을 해보니 관련 글이 많이 나왔다. Next.js를 사용한 프로젝트를 이미지로 빌드하는 경우 Next.js에서 권장하는 Docker 배포 방식이 있다고 한다. 결론적으로 아래와 같은 부분때문에 용량이 커질 수 있고, 이를 수정하여 용량을 줄일 수 있다고 한다.
- 단일 스테이지 빌드: 빌드 중 생성된 임시 파일과 중간 결과물들이 최종 이미지에 포함될 수 있다. (즉, 실제로 불필요한 것들이 포함될 수 있다.)
- npm install 명령으로 설치된 모든 node_modules가 최종 이미지에 포함될 수 있다.
- production 배포를 위한 설정인 standalone 설정을 하지 않았다.
어떻게 수정해야할까?
나는 아래와 같은 과정을 통해 Dockerfile을 수정해보기로 했다.
1. next.config.mjs에 standalone 설정을 한다.
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
};
export default nextConfig;
2.멀티 스테이지 빌드를 사용해 최종 이미지에는 빌드 단계에서 생성된 standalone 폴더와 필요한 파일만 복사해 넣을 수 있도록 Dockerfile을 수정한다.
# 빌드 단계
FROM node:20-alpine AS builder
WORKDIR /app
# 패키지 파일 복사 및 종속성 설치
COPY package.json package-lock.json ./
RUN npm install
# 소스 파일 복사 및 빌드
COPY . .
RUN npm run build
# 프로덕션 이미지
FROM node:20-alpine
WORKDIR /app
# 애플리케이션 실행에 필요한 사용자 추가
RUN addgroup -S nextjs && adduser -S nextjs -G nextjs
# 빌드 단계에서 생성된 standalone 폴더와 필요한 파일만 복사
COPY --from=builder --chown=nextjs:nextjs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nextjs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nextjs /app/public ./public
COPY --from=builder --chown=nextjs:nextjs /app/package.json ./package.json
# 사용자 권한 설정
USER nextjs
# 포트 노출
EXPOSE 3000
# 앱 실행
CMD ["node", "server.js"]
3. 불필요한 파일이 Docker 빌드 컨텍스트에 포함되지 않도록 .dockerignore 파일을 추가한다.
node_modules
build
dist
.next
Dockerfile
.dockerignore
README.md
4. docker build 명령을 통해 새로운 Dockerfile 내용으로 다시 이미지를 빌드한다.
docker build -t bk-frontend .
결과
1.14GB 에서 153.49MB로 확연하게 용량이 줄어든 것을 볼 수 있었다. 마음이 편안해졌다.