Docker Multi-stage builds(멀티 스테이지 빌드)

HumbleMincho·2025년 2월 23일

멀티 스테이지 빌드란?

Multi-stage build 방식은 Docker 17.05 버전에 도입된 기능으로, Dokcerfile 내에서 여러 개의 빌드 단계를 정의하여 이미지를 생성하는 방법.
애플리케이션 실행에 필요한 최소한의 구성만 포함하여 이미지 크기를 최소화할 수 있다.


왜 사용할까?

  1. 위의 설명처럼 최소한의 구성만 포함하여 이미지 크기를 최소화할 수 있기 떄문에 Docker 이미지 크기 최소화의 장점이 있다.
  2. 캐싱 기능을 지원하므로 소스 코드나 종속성이 변경되지 않은 경우 이를 재사용할 수 있다. -> 빌드 속도가 빨라지고 개발 주기가 단축될 수 있다.

Mono Repo환경일때의 CI/CD파이프라인 및 배포 시나리오

  1. 특정 브랜치에 Git Push -> 변경 감지
  2. CI/CD 스크립트로 어떤 app이 변경됐는지 감지하는 로직 정의
  3. apps/~ 의 각 어플리케이션의 변경이 있다면 해당 어플리케이션만 개별적으로 배포 가능
  4. 공유 패키지 변경 시 영향받는 모든 앱 재배포.

스테이지(단계)별 Dockerfile 예시

FROM node:20.11.0-alpine3.19 AS base

FROM base AS builder
RUN apk add --no-cache libc6-compat
RUN apk update

WORKDIR /app
RUN npm install -g turbo
COPY . .
RUN turbo prune --scope=web --docker

FROM base AS installer
RUN apk add --no-cache libc6-compat
RUN apk update
WORKDIR /app

COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN corepack enable
RUN pnpm install

COPY --from=builder /app/out/full/ .
RUN pnpm dlx turbo run build --filter=web

FROM base AS runner
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs

COPY --from=installer /app/apps/web/next.config.mjs .
COPY --from=installer /app/apps/web/package.json .

COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public

CMD node apps/web/server.js

base stage

FROM node:20.11.0-alpine3.19 AS base
  • 사용할 node버전 명시 및 alpine(최소 단위의 Linux 이미지) 정의

builder stage

FROM base AS builder
RUN apk add --no-cache libc6-compat
RUN apk update

WORKDIR /app
RUN npm install -g turbo
COPY . .
RUN turbo prune --scope=web --docker
  1. Run apk add --no-cache libc6-compat 를 수행하여 Node.js process.dlopen 사용에 필요한 공유 라이브러리가 alpine 버전에 없는 문제를 해결함.
  2. turbo를 글로벌로 설치
  3. turbo prune으로 빌드할 애플리케이션과 그 종속성만 포함하도록 가지치기하여 빌드 최적화

installer stage

FROM base AS installer
RUN apk add --no-cache libc6-compat
RUN apk update
WORKDIR /app

COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN corepack enable
RUN pnpm install

COPY --from=builder /app/out/full/ .
RUN pnpm dlx turbo run build --filter=web
  1. builder 스테이지에서 turbo prune으로 생성된 out 폴더에 있는 파일들을 복사
  2. corepack을 활성화 하여 package.json에 명시된 패키지 매니저 사용하도록 적용
  3. pnpm install로 종속성 설치
  4. builder 스테이지에 web 애플리케이션의 전체 소스 복사
  5. turbo로 web 애플리케이션 빌드.
  • *중요 이때, next.config에 output: "standalone" 옵션을 사용하여 프로덕션 배포에만 필요한 파일만 생성하도록 함.
  • 참고 링크 → next-config-output

runner stage

FROM base AS runner
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs

COPY --from=installer /app/apps/web/next.config.mjs .
COPY --from=installer /app/apps/web/package.json .

COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public

CMD node apps/web/server.js
  1. 시스템 group, user를 생성하고 nextjs 사용자로 전환하여 실행.
  • 왜 group, user를 생성하고 nextjs 사용자로 전환해서 실행하는가? -> 보안강화. 컨테이너 내부에서 애플리케이션이 루트 권한으로 실행되면 애플리케이션의 취약점을 악용하여 시스템 전체에 악영향이 끼치는 것을 방지하기 위함
  1. installer 스테이지에서 next 설정 파일과 package.json을 현재 스테이지로 복사.
  2. installer 스테이지에서 빌드 결과물, 정적 파일, public 폴더를 복사한다. 이때 소유자를 nextjs:nodejs로 설정.
  3. output: standalone 옵션을 사용하여 빌드했기 때문에 이때 복사되는 파일들은 프로덕션에만 필요한 파일들임.
  4. 애플리케이션 실행
profile
최고의 효율을 창출하기 위해 겸손히 노력합니다.

0개의 댓글