# Docker Multi-Stage Build로 컨테이너 이미지 크기 줄이기

> Clean Markdown view of GeekNews topic #17794. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=17794](https://news.hada.io/topic?id=17794)
- GeekNews Markdown: [https://news.hada.io/topic/17794.md](https://news.hada.io/topic/17794.md)
- Type: news
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2024-11-17T09:32:02+09:00
- Updated: 2024-11-17T09:32:02+09:00
- Original source: [labs.iximiuz.com](https://labs.iximiuz.com/tutorials/docker-multi-stage-builds)
- Points: 18
- Comments: 6

## Summary

Docker 컨테이너 이미지의 크기 증가와 보안 취약성을 줄이기 위해 Multi-Stage Build를 활용하여 빌드와 런타임 환경을 분리하는 방법을 설명합니다. Multi-Stage Build를 사용하면 불필요한 개발 의존성을 제거하여 이미지 크기를 줄이고 보안을 강화할 수 있으며, 빌드 프로세스를 간소화할 수 있습니다. 이를 통해 효율적인 컨테이너 이미지를 만들 수 있으며, 고급 기능도 지원합니다.

## Topic Body

- Docker 컨테이너 이미지를 빌드할 때, Dockerfile이 Multi-Stage 구조가 아니라면 불필요한 파일이 포함될 가능성이 큼  
- 이는 이미지 크기 증가 및 보안 취약성 증가로 이어짐  
- 컨테이너 이미지에서 발생할 수 있는 “불필요한 파일”의 주요 원인을 분석하고, Multi-Stage Build를 통해 이를 해결하는 방법을 설명  
  
### 이미지 크기가 커지는 원인  
-	애플리케이션은 빌드 타임과 런타임의 의존성을 가짐.  
-	빌드 타임 의존성은 런타임보다 많고 보안 취약점(CVEs)이 더 많음.  
-	같은 이미지를 빌드와 실행에 사용하면, 불필요한 빌드 타임 의존성(컴파일러, 린터 등)이 포함됨.  
-	빌드와 런타임 이미지는 분리되어야 하지만, 이를 간과하는 경우가 많음.  
  
### 잘못된 Dockerfile 구조 예시  
  
#### Go 애플리케이션용의 잘못된 예  
```  
FROM golang:1.23  
WORKDIR /app  
COPY . .  
RUN go build -o binary  
CMD ["/app/binary"]  
```  
- golang:1.23 이미지는 컴파일용이지만, 이를 그대로 프로덕션 환경에 사용하면 전체 Go 컴파일러와 의존성까지 포함됨.  
- 이미지 크기: 800MB 이상, 800개 이상의 보안 취약점 존재.  
  
#### Node.js 애플리케이션의 잘못된 예  
```  
FROM node:lts-slim  
WORKDIR /app  
COPY . .  
RUN npm ci  
RUN npm run build  
ENV NODE_ENV=production  
EXPOSE 3000  
CMD ["node", "/app/.output/index.mjs"]  
```  
- node_modules 폴더가 런타임에 필요 없는 개발 의존성까지 포함하게 됨.  
- npm ci --omit=dev로 수정할 수 없으며, 빌드 과정에서 필요한 개발 의존성이 제거될 수 있음.  
  
### Multi-Stage Build 이전의 Lean 이미지 제작 방법  
  
### Builder 패턴  
  
1.	Dockerfile.build에서 애플리케이션을 빌드함:  
```  
FROM node:lts-slim  
WORKDIR /app  
COPY . .  
RUN npm ci  
RUN npm run build  
```  
  
2.	빌드된 아티팩트를 호스트로 복사:  
```  
docker cp $(docker create build:v1):/app/.output .  
```  
  
3.	Dockerfile.run에서 런타임 이미지를 생성:  
```  
FROM node:lts-slim  
WORKDIR /app  
COPY .output .  
CMD ["node", "/app/.output/index.mjs"]  
```  
	•	문제점: 여러 Dockerfile 작성, 빌드 순서 관리 필요, 추가 스크립트 요구.  
  
### Multi-Stage Build의 이해  
  
- Multi-Stage Build는 Docker 내부에 Builder 패턴을 구현한 기능임.  
	- 여러 FROM 명령을 사용하여 하나의 Dockerfile에서 빌드와 런타임 스테이지를 정의 가능.  
	- COPY --from=&lt;stage&gt; 명령을 사용해 이전 스테이지에서 빌드한 파일을 가져옴.  
  
#### Multi-Stage Dockerfile 예시 (Node.js)  
  
```  
# Build stage  
FROM node:lts-slim AS build  
WORKDIR /app  
COPY . .  
RUN npm ci  
RUN npm run build  
  
# Runtime stage  
FROM node:lts-slim AS runtime  
WORKDIR /app  
COPY --from=build /app/.output .  
ENV NODE_ENV=production  
CMD ["node", "/app/.output/index.mjs"]  
```  
  
-	COPY --from=build로 빌드된 아티팩트를 직접 복사함으로써, 호스트를 거치지 않고 파일을 이동 가능.  
  
### Multi-Stage Build 실전 예시  
  
React 애플리케이션  
```  
# Build stage  
FROM node:lts-slim AS build  
WORKDIR /app  
COPY . .  
RUN npm ci  
RUN npm run build  
  
# Runtime stage  
FROM nginx:alpine  
COPY --from=build /app/build /usr/share/nginx/html  
ENTRYPOINT ["nginx", "-g", "daemon off;"]  
```  
- React 애플리케이션은 빌드 후 정적 파일이 되며, Nginx로 서빙 가능.  
  
Go 애플리케이션  
  
```  
# Build stage  
FROM golang:1.23 AS build  
WORKDIR /app  
COPY . .  
RUN go build -o binary  
  
# Runtime stage  
FROM gcr.io/distroless/static-debian12:nonroot  
COPY --from=build /app/binary /app/binary  
ENTRYPOINT ["/app/binary"]  
```  
  
- distroless 이미지를 사용해 최소화된 런타임 환경 제공.  
  
Java 애플리케이션  
  
```  
# Build stage  
FROM eclipse-temurin:21-jdk-jammy AS build  
WORKDIR /build  
COPY . .  
RUN ./mvnw package -DskipTests  
  
# Runtime stage  
FROM eclipse-temurin:21-jre-jammy  
COPY --from=build /build/target/app.jar /app.jar  
CMD ["java", "-jar", "/app.jar"]  
```  
- 빌드에는 JDK를 사용하고, 런타임에는 더 가벼운 JRE 사용.  
  
### 결론  
  
- Multi-Stage Build는 빌드와 런타임 환경을 분리하여 불필요한 개발 의존성으로 인한 이미지 크기 증가를 방지함  
- 이를 통해 이미지 크기를 줄이고, 보안을 강화하며, 빌드 프로세스를 간소화할 수 있음  
- Multi-Stage Build는 효율적인 컨테이너 이미지를 만들기 위한 표준적인 방법이며, 고급 기능(예: 분기 조건, 빌드 중 유닛 테스트)도 지원함

## Comments



### Comment 31431

- Author: savvykang
- Created: 2024-11-18T12:14:15+09:00
- Points: 1

자바의 경우 jlink가 9버전부터 도입됐지만 의존 모듈을 jdeps 로 찾아서 명시해줘야 하는 등 사용성이 안좋습니다. 사람들이 저런 방법을 모르거나 JRE를 찾는 걸 보면 자바 도구의 홍보가 부족한 것 같고, 명령어 하나로 JRE가 나오게 개선이 필요해 보입니다

### Comment 31383

- Author: brainer
- Created: 2024-11-17T18:30:11+09:00
- Points: 1

저렇게 쓰고 있긴한데, 빌드 시간이 오래 걸리는건 단점 같아요

### Comment 31428

- Author: kandk
- Created: 2024-11-18T12:09:15+09:00
- Points: 1
- Parent comment: 31383
- Depth: 1

빌드시간은 차이가 없어야할거에요. 차이가 있다면 잘못 설정한것!

### Comment 31429

- Author: brainer
- Created: 2024-11-18T12:10:54+09:00
- Points: 1
- Parent comment: 31428
- Depth: 2

아, 그렇군요!

### Comment 31425

- Author: qurare
- Created: 2024-11-18T11:49:26+09:00
- Points: 1
- Parent comment: 31383
- Depth: 1

전략에 따라서 한 스테이지를 통으로 캐시할 수도 있어서 전 오히려 빌드 시간이 단축되더라고요!

### Comment 31430

- Author: brainer
- Created: 2024-11-18T12:11:09+09:00
- Points: 1
- Parent comment: 31425
- Depth: 2

Docker에 대해 좀 더 알아봐야하겠네요!
