핵심 정리
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html빌드 흐름
어떤 이미지 분리 전략을 먼저 떠올리면 되나
| 상황 | 먼저 떠올릴 선택 |
|---|---|
| 빌드 도구와 런타임 분리 | multi-stage build |
| 최종 이미지에 산출물만 포함 | COPY --from |
| 테스트 단계만 따로 실행 | --target |
| 이미지 크기를 크게 줄이고 싶다 | 경량 런타임 스테이지 |
빌드 도구가 최종 이미지에 남지 않아야 하는 이유
컴파일러, 패키지 매니저, 소스 코드는 빌드할 때만 필요하다. 이것들이 최종 이미지에 포함되면 이미지 크기가 커지고, 불필요한 바이너리가 공격 표면이 된다. 멀티 스테이지 빌드는 빌드 환경과 런타임 환경을 Dockerfile 안에서 분리한다.
# build 스테이지: Go 컴파일러 포함 (600MB+)
FROM golang:1.22 AS build
WORKDIR /app
COPY . .
RUN go build -o server .
# runtime 스테이지: 바이너리만 복사 (10MB 미만)
FROM scratch
COPY --from=build /app/server /server
ENTRYPOINT ["/server"]COPY --from으로 이전 스테이지의 아티팩트만 가져오는 방법
COPY --from=스테이지명 또는 COPY --from=숫자로 앞서 완료된 스테이지의 특정 파일이나 디렉터리만 복사한다. 스테이지 전체가 아니라 최종 결과물만 선택적으로 가져올 수 있어 최종 이미지를 최소화할 수 있다.
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install -r requirements.txt
FROM python:3.12-slim
COPY --from=builder /install /usr/local # 설치된 패키지만 복사
COPY src/ /app/src/
CMD ["python", "/app/src/main.py"]이미지 크기 감소 효과 — 빌더 이미지 vs 런타임 이미지
빌드 도구가 포함된 이미지와 산출물만 담은 이미지는 크기가 크게 차이난다. 멀티 스테이지를 적용하면 최종 이미지에는 런타임에 꼭 필요한 파일만 남는다.
# 빌드 전후 크기 비교
docker build -t my-app:single -f Dockerfile.single .
docker build -t my-app:multistage -f Dockerfile.multistage .
docker image ls | grep my-app
# my-app single 1.2GB
# my-app multistage 45MB--target으로 특정 스테이지까지만 빌드하는 패턴
--target을 사용하면 Dockerfile의 특정 스테이지까지만 빌드를 멈출 수 있다. CI에서 테스트 스테이지만 실행하거나, 빌드 결과물을 중간 단계에서 확인할 때 유용하다.
FROM node:22-alpine AS deps
COPY package*.json ./
RUN npm ci
FROM deps AS test
COPY . .
RUN npm test
FROM deps AS build
COPY . .
RUN npm run build
FROM nginx:alpine AS runtime
COPY --from=build /app/dist /usr/share/nginx/html# 테스트 스테이지까지만 실행
docker build --target test -t my-app:test .
# 최종 runtime 스테이지까지 전체 빌드
docker build --target runtime -t my-app:latest .multi-stage와 단일 스테이지는 최종 이미지에 남는 것이 다르다
단일 스테이지는 빌드 도구와 소스, 캐시까지 최종 이미지에 같이 남기기 쉽고, multi-stage는 런타임에 필요한 산출물만 남기는 데 초점이 있습니다. "결과물만 배포할 것인가, 빌드 환경도 같이 남겨도 되는가"를 기준으로 고르면 됩니다.
# 나쁜 단일 스테이지 예
FROM node:22
WORKDIR /app
COPY . .
RUN npm ci && npm run build
CMD ["npm", "start"]
# 좋은 multi-stage 예
FROM node:22 AS build
WORKDIR /app
COPY . .
RUN npm ci && npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html체크포인트
| 상황 | 적합한 선택 |
|---|---|
| 빌드 도구와 런타임 환경을 분리하고 싶을 때 | 멀티 스테이지 빌드 |
| 컴파일 결과물만 이미지에 포함할 때 | COPY --from=빌드스테이지 |
| CI에서 테스트와 빌드를 단계별로 실행 | --target 플래그 |
| 이미지 크기를 최소화하려면 | 런타임 베이스로 alpine 또는 scratch |
주의할 점
COPY --from=build 범위를 넓게 잡으면 산출물 외에 불필요한 파일까지 최종 이미지에 들어간다. "런타임에 실제로 필요한 파일만" 복사하는 원칙을 끝까지 유지해야 멀티 스테이지 빌드의 효과가 살아난다.
FROM node:22 AS build
WORKDIR /app
COPY . .
RUN npm ci && npm run build
FROM nginx:alpine
COPY --from=build /app /app빌드 산출물만 필요한데 /app 전체를 복사하면 소스, 테스트 파일, node_modules, 캐시까지 최종 이미지에 따라올 수 있습니다.
참고 링크
2 sources