[꿀팁] 도커 이미지 최적화하기

in-ch·2024년 3월 30일
1

꿀팁

목록 보기
12/14

서론


최근에 next.js로 간단한 페이지만 추가한 상태에서 도커 이미지를 빌드했는데 도커 이미지가 2.19G가 넘어가는 것을 확인했습니다. 😭
결과부터 미리 말하자면 2.19G에서 850MB로 약 60%정도 이미지 크기를 줄였으며, 빌드 시간 또한 241.7s에서 198s로 최적화되었습니다.

그럼 어떻게 최적화를 진행하였는지 기록하도록 하겠습니다.

가즈아 🔥🔥

왜 최적화해야 할까?


가장 큰 이유는 EC2의 디스크 공간을 더 적게 차지하기 때문입니다.
대략적으로 SSD Volume이 추가될 때 마다 $0.10/GB/월 정도의 비용이 발생하는데 최적화되지 않은 이미지가 계속 추가된다면 비용은 기하급수적으로 늘어날 수도 있겠죠.

또한 이미지를 최적화하면 다운로드 및 전송 시간이 줄어듭니다.
시간은 소중하죠..

최적화 과정


먼저 최적화 전의 Dockerfile을 살펴보겠습니다.

# 1. Base image
FROM node:20.9.0 AS base

# 2. Dependencies
# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
COPY .yarn ./.yarn
COPY package.json yarn.lock ./
RUN yarn set version berry

# 3. Builder
FROM deps AS builder
WORKDIR /app
ENV NEXT_TELEMETRY_DISABLED 1

COPY --from=deps /app/.yarn ./.yarn
COPY --from=deps /app/yarn.lock ./
COPY . .

ARG SERVER_ENV
RUN yarn
RUN yarn build:dev

# 4. Production image
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN cd /app && echo 'YARN VERSION IN BUILDER: ' && yarn --version
# RUN yarn rebuild && yarn build
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder --chown=nextjs:nodejs /app/next.config.js ./
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder --chown=nextjs:nodejs /app/.yarn ./.yarn
COPY --from=builder --chown=nextjs:nodejs /app/yarn.lock ./yarn.lock
COPY --from=builder --chown=nextjs:nodejs /app/.yarnrc.yml ./.yarnrc.yml
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json

RUN rm -rf /app/.yarn/unplugged && yarn rebuild
RUN chown -R nextjs:nodejs /app/.next
RUN echo "YARN VERSION IN RUNNER: " && yarn --version

USER nextjs

EXPOSE 3000

CMD ["yarn", "start"]

위의 코드는 다중 단계 빌드를 사용하여 Next.js 애플리케이션을 빌드합니다.
상세하게 나눠 설명하자면

  • FROM node:20.9.0 AS base: Node.js 20.9.0 버전을 베이스 이미지로 지정합니다.

  • .yarn 디렉터리와 package.json, yarn.lock 파일을 복사하여 이미지 내에 추가합니다.

  • yarn set version berry 명령을 사용하여 Yarn Berry를 설치합니다.

  • FROM deps AS builder: 앞서 생성한 의존성 이미지를 기반으로 새로운 단계를 생성합니다.

  • 애플리케이션의 소스 코드를 복사하고 필요한 의존성을 설치합니다.

  • yarn build:dev를 실행하여 개발용 빌드를 수행합니다.

  • 빌드된 애플리케이션 파일 및 필요한 파일을 복사합니다.

  • yarn start를 실행하여 애플리케이션을 시작합니다.

1. Base image 문제점

첫 번째는 베이스로 사용한 Node 이미지 문제입니다.
기존에는 node:20.9.0 AS base를 사용하고 있습니다.
이걸 alpine으로 변경해봅시다.

# FROM node:20.9.0 AS base
FROM node:20.9.0-alpine AS base

과연 얼마나 줄어들까?
감탄

2.19GB에서 1.26GB 크게 감소하였습니다!

alpine 태그는 Alpine Linux를 기반으로 한 경량화된 Docker 이미지를 가리킵니다. 따라서 base로 사용할 때는 alpine를 사용하는 것만으로도 이미지 크기를 크게 절약할 수 있습니다.

2. 불필요한 파일 미포함하기

이미 크게 용량을 줄였지만 아직 더 줄일 요지는 있습니다.
도커 이미지 실행에 필요하지 않은 파일을 이미지에서 제거한다면 용량이 더 줄어들지 않을까요?
.dockerignore를 활용하면 도커 이미지에 불필요한 파일들을 포함시키지 않을 수 있습니다.

  • .dockerignore 파일
node_modules
npm-debug.log
*.log
*.swp
.DS_Store
.next
.vscode
README.md

3. 이미지 분석 도구 dive 활용

아직 낭비되고 있는 공간이 분명 있을 겁니다.
한번 이미지 분석 도구를 통해 낭비되고 있는 공간을 확인해봅시다.

여기서는 도커 이미지 분석툴인 dive를 사용할 겁니다.

  • dive 설치법
brew install dive
  • dive 사용법
dive 이미지_이름:태그

명령어를 치면 Fetching image...라는 문구가 뜨면서 분석 결과를 확인해 볼 수 있습니다.

분석 결과를 보니 500 MB를 아낄 수 있다고 나오네요.
아래에 낭비되고 있는 불필요한 파일들이 나옵니다.

아.. 불필요하게 cache 파일들이 추가되고 있었네요.

빌드를 다 한 후에는 cache 파일을 더이상 필요가 없습니다.

Runner 스탭에서 앱을 실행하기 전에 불필요한 cache 파일을 지우도록 수정합시다.

RUN rm -rf /app/.yarn/unplugged && yarn rebuild &&  rm -rf /app/.yarn/cache
RUN chown -R nextjs:nodejs /app/.next && rm -rf /app/.next/cache 

최종적으로 500mb를 더 절약하며 Image efficiency score를 99%로 달성할 수 있었습니다.
1%는 도저히 발견할 수가..

4. 기타

만약 CI 단계에서 이미 린트 체크를 하고 있다면 프로덕션 빌드 단계에서 린트 체크를 스킵할 수가 있습니다. 그러면 불필요한 빌드 시간을 단축시킬 수 있으며 이미지 크기도 추가적으로 더 아낄 수 있습니다.

.next.config.js를 수정합니다.

eslint: {
	ignoreDuringBuilds: true
}

결론


도커 이미지를 최적화해서 불필요한 시간과 비용을 아낍시다.

끝 ..!!

profile
인치

0개의 댓글