////

Docker 멀티스테이지 빌드 — 이미지 크기 최소화 패턴

Docker 멀티스테이지 빌드는 빌드 도구·소스·개발 의존성을 최종 이미지에서 분리해 런타임 이미지를 작고 단순하게 만드는 패턴이다. 핵심은 COPY --from=<stage 로 필요한 산출물만 최종 이미지에 복사하고, BuildKit RUN --mount=type=cache로 npm/pip 캐시를 빌드 중 재사용하되 최종 이미지에는 포함하지 않는 것이다.

////

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/
USER appuser
CMD ["python", "-m", "app"]

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로 전환하는 편이 안전하다.
  • 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 계열을 우선 사용한다.

Sagwan Revalidation 2026-05-15#

  • verdict: revise
  • note: 이전 Node 예시는 npm ci --omit=dev 결과를 builder가 그대로 사용해 devDependencies가 필요한 npm run build에서 실패할 수 있었다. build deps와 production deps를 분리하는 예시로 보정했다.

Sagwan Revalidation 2026-05-16T11:30:17Z#

  • verdict: ok
  • note: Node 22 LTS·BuildKit·멀티스테이지 권장 패턴 모두 여전히 유효함

Sagwan Revalidation 2026-05-17T11:55:37Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit 캐시·distroless 권장안이 현재도 유효함

Sagwan Revalidation 2026-05-18T12:19:52Z#

  • verdict: ok
  • note: Docker/BuildKit 멀티스테이지 권장 패턴과 버전 전제가 여전히 유효함

Sagwan Revalidation 2026-05-19T12:48:21Z#

  • verdict: ok
  • note: Node 22·Python 3.12·BuildKit 멀티스테이지 권장안이 여전히 유효함

Sagwan Revalidation 2026-05-20T13:12:25Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit 캐시·Node 22 LTS 기준 모두 여전히 유효함

Sagwan Revalidation 2026-05-21T13:48:04Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit 캐시·distroless 권장안이 여전히 유효하다.

Sagwan Revalidation 2026-05-22T14:20:15Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit cache·distroless 패턴과 버전 전제가 여전히 유효함

Sagwan Revalidation 2026-05-23T14:58:26Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit 캐시·distroless 예시 모두 현재도 유효함

Sagwan Revalidation 2026-05-24T15:11:28Z#

  • verdict: ok
  • note: Node 22 LTS·BuildKit cache·distroless 패턴 모두 현재도 유효함

Sagwan Revalidation 2026-05-25T15:28:44Z#

  • verdict: ok
  • note: 패턴·버전·BuildKit 캐시 권장안 모두 현재도 유효하다.

Sagwan Revalidation 2026-05-26T15:38:22Z#

  • verdict: ok
  • note: [chatgpt 오류] The read operation timed out

Sagwan Revalidation 2026-05-27T16:25:43Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit 캐시·Node 22/distroless 예시는 여전히 유효함

Sagwan Revalidation 2026-05-28T17:25:41Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit 캐시·Node 22/distroless 권장안이 여전히 유효함

Sagwan Revalidation 2026-05-29T17:59:20Z#

  • verdict: ok
  • note: Docker/BuildKit, Python 3.12, Node 22 distroless 관행 모두 현재 유효함

Sagwan Revalidation 2026-05-30T18:36:16Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit 캐시·Node 22/distroless 권장안이 여전히 유효함

Sagwan Revalidation 2026-05-31T18:40:07Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit 캐시·Node 22 distroless 패턴 모두 여전히 유효함

Sagwan Revalidation 2026-06-01T18:40:37Z#

  • verdict: ok
  • note: [chatgpt HTTP 401] {

Sagwan Revalidation 2026-06-02T22:06:15Z#

  • verdict: ok
  • note: 예시와 권장안 모두 현재 Docker/BuildKit 관행에 부합함

Sagwan Revalidation 2026-06-03T22:56:26Z#

  • verdict: ok
  • note: 멀티스테이지·BuildKit 캐시·distroless 권장안은 현재도 유효함

Sagwan Revalidation 2026-06-04T23:28:24Z#

  • verdict: ok
  • note: Python 3.12·Node 22·BuildKit·distroless 권장 패턴이 여전히 유효함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
0