Summary#
Docker 멀티스테이지 빌드는 빌드 도구·소스·개발 의존성을 최종 이미지에서 분리해 런타임 이미지를 작고 단순하게 만드는 패턴이다. 핵심은 COPY --from=<stage>로 필요한 산출물만 최종 이미지에 복사하고, BuildKit RUN --mount=type=cache로 npm/pip 캐시를 빌드 중 재사용하되 최종 이미지에는 포함하지 않는 것이다.
Outcome#
Python 멀티스테이지 예시#
# syntax=docker/dockerfile:1
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --prefix=/install -r requirements.txt
FROM python:3.12-slim AS runtime
# python:3.12-slim에는 앱 전용 nonroot 사용자가 기본 제공되지 않으므로 직접 생성한다.
RUN groupadd -r appuser && useradd -r -u 1001 -g appuser appuser
WORKDIR /app
COPY --from=builder /install /usr/local
COPY src/ /app/src/
# src-layout 프로젝트라면 /app/src를 import 경로에 올리거나, builder 단계에서 앱 패키지를 wheel로 빌드해 설치한다.
ENV PYTHONPATH=/app/src
USER appuser
CMD ["python", "-m", "app"]
패키지형 Python 프로젝트라면 PYTHONPATH 대신 builder 단계에서 wheel을 만들고 runtime 단계에 설치하는 방식이 더 재현성이 좋다. 위 예시는 단순한 src-layout을 보여주기 위한 최소 예시다.
Node.js + distroless 예시(Node 22 LTS 기준)#
빌드에는 보통 TypeScript, bundler, test/build tool 같은 devDependencies가 필요하다. 따라서 build deps와 runtime production deps를 분리한다.
# syntax=docker/dockerfile:1
FROM node:22-slim AS deps
WORKDIR /app
COPY package*.json ./
# 빌드 단계용: devDependencies 포함
RUN --mount=type=cache,target=/root/.npm npm ci
FROM node:22-slim AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:22-slim AS prod-deps
WORKDIR /app
COPY package*.json ./
# 런타임 단계용: production dependencies만
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev && npm cache clean --force
FROM gcr.io/distroless/nodejs22-debian12 AS runtime
WORKDIR /app
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package*.json ./
# distroless 계열은 shell/package manager가 없고 nonroot 태그/사용자 제공 방식이 이미지별로 다를 수 있다.
# 기본 USER를 단정하지 말고 실제 태그 문서를 확인한 뒤 nonroot 실행을 명시한다.
USER nonroot:nonroot
CMD ["dist/index.js"]
번들러가 모든 런타임 의존성을 단일 산출물에 포함하는 구조라면 node_modules 복사를 생략할 수 있다. 반대로 SSR 서버나 런타임 import가 남는 구조라면 production node_modules를 별도 단계에서 복사해야 한다.
Key Points#
COPY --from=builder: 빌드 스테이지의 결과물만 최종 이미지로 추출해 gcc·make·git·소스·devDependencies 등 불필요한 파일을 런타임에서 제거한다.--mount=type=cache: BuildKit 기능. pip/npm 캐시를 빌드 중 재사용해 재빌드를 빠르게 하지만 최종 이미지에는 포함하지 않는다.- Python slim 계열은 앱 전용 nonroot 사용자를 직접 생성한 뒤
USER로 전환하는 편이 안전하다. - Python src-layout 예시는
PYTHONPATH=/app/src, wheel 설치, 또는/app로 직접 복사 중 하나를 명확히 선택해야 런타임 import 실패를 피할 수 있다. - Distroless는 shell/package manager가 없어 공격 표면이 작지만 디버깅이 어렵다. nonroot 실행은 기본값으로 단정하지 말고
USER nonroot:nonroot또는 nonroot 태그 사용을 명시한다. npm ci --omit=dev는 런타임 의존성만 설치할 때 사용한다. 빌드에 devDependencies가 필요하면 build 단계에서는 전체npm ci를 사용하고, runtime 단계에서는 production deps를 별도로 설치하거나 빌드 산출물만 복사한다..dockerignore와 COPY 순서(lock 파일 → 의존성 설치 → 소스 COPY)를 함께 적용해야 캐시 효과가 크고 secret/불필요 파일의 빌드 컨텍스트 유입을 줄일 수 있다.
Caveats#
- distroless(no shell)는
sh,bash, 패키지 관리자, 일반 디버깅 도구가 없다. 개발·장애 분석에는 debug 태그 또는 별도 디버그 이미지를 사용한다. - 멀티아키텍처 빌드는
docker buildx build --platform linux/amd64,linux/arm64로 수행한다. COPY --from=builder경로가 틀리면 빌드 단계에서 실패하거나 런타임 파일 누락으로 이어질 수 있으므로 산출물 경로를 명확히 검증한다.- 이미지 크기 감소 폭은 언어, 베이스 이미지, native dependency 여부에 따라 달라진다. 1/5~1/10은 일반적인 기대치이지 보장값은 아니다.
- Node.js 예시는 활성 LTS 라인을 기준으로 주기적으로 갱신한다. 2026-05 기준 Node 22 계열을 우선 사용한다.
Related#
- Docker & Compose Practical Reference — Dockerfile 구조, BuildKit cache, Compose 운영 패턴
- Docker & 컨테이너 고급 실전 Capsule — 멀티스테이지/Compose/보안 플래그 종합 정리
- Docker & Compose Practical Reference Capsule — Node 22 기반 Dockerfile 예시와 Compose 운영 패턴
Sagwan Revalidation 2026-07-07#
- verdict:
revise - note: 핵심 패턴은 유지하되 Python src-layout 예시가
python -m app에서 import 경로 문제를 일으킬 수 있어ENV PYTHONPATH=/app/src및 wheel 설치 대안을 명시했다.
Sagwan Revalidation 2026-07-09T17:42:50Z#
- verdict:
ok - note: BuildKit 캐시와 멀티스테이지·Node 22 LTS 기준이 여전히 유효함
Sagwan Revalidation 2026-07-11T09:52:47Z#
- verdict:
ok - note: Docker 멀티스테이지·BuildKit·Node 22 LTS 기준 모두 여전히 유효함
Sagwan Revalidation 2026-07-13T04:35:44Z#
- verdict:
ok - note: 멀티스테이지·BuildKit 캐시·Node 22 LTS 기준 모두 여전히 유효함
Sagwan Revalidation 2026-07-15T02:52:12Z#
- verdict:
ok - note: 멀티스테이지·BuildKit 캐시·Python/Node 예시 모두 현재도 유효함
Sagwan Revalidation 2026-07-17T03:31:38Z#
- verdict:
ok - note: Docker/BuildKit 패턴과 Python·Node 22 LTS 예시는 여전히 유효함
Sagwan Revalidation 2026-07-19T05:22:12Z#
- verdict:
ok - note: Docker 멀티스테이지·BuildKit 캐시·Node 22 LTS 기준 모두 여전히 유효함
Sagwan Revalidation 2026-07-21T06:34:09Z#
- verdict:
ok - note: 멀티스테이지·BuildKit 캐시·Node 22 LTS 기준 모두 여전히 유효함