Docker 이미지 크기가 몇 GB나 됐던 이유 — 멀티스테이지 빌드
문제 발생
애플리케이션을 담은 Docker 이미지가 3GB에 육박해서, 배포할 때마다 이미지를 pull하는 데 시간이 오래 걸리고 레지스트리 저장 비용도 늘었습니다.
FROM node:24
WORKDIR /app
COPY . .
RUN npm install # devDependencies까지 전부 설치
RUN npm run build
CMD ["node", "dist/server.js"]원인 분석
위 Dockerfile은 빌드에 필요한 것과 실행에 필요한 것을 구분하지 않고 하나의 이미지에 전부 담습니다.
npm install은 기본적으로devDependencies(빌드 도구, 테스트 라이브러리, 타입 정의 등)까지 전부 설치합니다 — 실제 실행에는 필요 없는 패키지가 이미지에 그대로 남습니다.- 소스 코드 전체(
COPY . .)가 그대로 이미지에 남아 있습니다 — 빌드 산출물(dist/)만 있으면 되는데 원본 TypeScript/JSX 소스까지 함께 배포됩니다. node:24(전체 이미지, 각종 빌드 도구가 미리 설치된 버전)를 실행 단계에서도 그대로 쓰면, 실제 실행에는 필요 없는 컴파일러/개발 도구까지 이미지에 남아있게 됩니다.
해결 방안
멀티스테이지 빌드로 "빌드용 스테이지"와 "실행용 스테이지"를 분리합니다. 최종 이미지에는 실행에 진짜 필요한 파일만 복사해 넣습니다.
# ---- 빌드 스테이지: 여기서만 무거운 도구/전체 의존성을 씀
FROM node:24 AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install # devDependencies 포함 전체 설치
COPY . .
RUN npm run build # dist/ 생성
# ---- 실행 스테이지: 여기서부터가 최종 이미지
FROM node:24-alpine AS runner # 훨씬 작은 base 이미지
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install --omit=dev # 실행에 필요한 의존성만
COPY --from=builder /app/dist ./dist # 빌드 산출물만 가져옴
CMD ["node", "dist/server.js"]COPY --from=builder는 이전 스테이지의 파일시스템에서 필요한 결과물만 골라 가져오는 명령입니다 — 빌드 스테이지에 있던 소스 코드, devDependencies, 빌드 도구는 최종 이미지에 전혀 포함되지 않습니다.
- base 이미지도
alpine(경량 리눅스 배포판) 버전을 실행 스테이지에서 쓰면 이미지 크기를 추가로 크게 줄일 수 있습니다. docker image ls나docker history <image>로 각 레이어의 크기를 확인하면 어느 부분이 이미지 크기를 키우고 있는지 파악할 수 있습니다.- 이미지 크기가 줄면 pull 시간뿐 아니라 컨테이너 시작 시간, 레지스트리 저장 비용, 공격 표면(이미지 안에 불필요한 도구가 적을수록 취약점 개수도 줄어듦)까지 함께 개선됩니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.