DevOps/DOCKER

[Docker] MultiStage란?

IT록흐 2023. 9. 22. 18:38
반응형

 

Dockerfile

##### Build Stage #####
# 종속성 추출
# 빌드 작업을 위한 JDK 베이스이미지
FROM openjdk:11-jdk-slim as build
 
# 워킹 디렉토리 설정
WORKDIR /workspace/app
 
# 빌드에 필요한 Gradle 소스 복사
COPY gradle gradle
COPY build.gradle settings.gradle gradlew ./
COPY src src
 
# 빌드 진행
RUN ./gradlew bootJar # 빌드 진행
RUN mkdir -p build/libs/dependency && (cd build/libs/dependency; jar -xf ../*.jar) # 종속성 추출
 
##### Run Stage #####
 
# 실행 작업을 위한 JRE 베이스이미지
FROM openjdk:11-jre-slim
 
# 호스트 서버에 전달이 필요한 데이터 저장공간
VOLUME /tmp
 
# Arugument에 종속성 경로를 추가
ARG DEPENDENCY=/workspace/app/build/libs/dependency
 
# Build Stage에서 추출된 종속성 카피하기
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
 
# 실행하기
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.hellospring.HelloSpringApplication"]

 

 

Multi Stage 방식으로 구성된 Dockerfile이 있다. 

Multi Stage 방식은 이미지 빌드를 여러 Stage로 나누어 최종 이미지를 경량화 하는데 목적이 있다. 

 

1) Build Stage : JDK를 베이스이미지로 빌드하여 종속성 추출하기

2) Run Stage : 추출된 종속성을 카피하여 JRE를 베이스이미지로 한 최종 이미지파일 생성하기 

 

첫 번째 Stage는 Build Stage이다. 

Build Stage는 JDK이미지를 베이스이미지로 가져온다. 

 

- WORKDIR

 

WORKDIR은 JDK베이스이미지에 워킹 디렉토리를 생성한다. 

 

 

 

- COPY

 

빌드에 필요한 gradle, build.gradle, gradlew, src 등을 JDK 베이스이미지 워킹 디렉토리에 복사(COPY)한다. src 폴더에는 .java 파일이 존재한다. .java 파일을 빌드하여 .class파일로 만들어야 JAVA 프로그램 구동이 가능해진다. java 파일 빌드는 gradle이 담당한다.

gradle도 로컬에서 JDK 베이스이미지로 COPY했으니 빌드를 시작해보자.

 

- RUN

RUN으로 gradle를 동작시켜 .class 파일 압축파일인 jar를 생성한다. ( RUN ./gradlew bootJar ) 그리고 jar 파일을 jar -xf 명령으로 build/libs/dependency 폴더에 압축을 해제한다. 그럼 dependency 폴더에는 실행이 가능한 class파일들이 모인다. 

 

 

그럼 이제 JAVA 프로그램 구동만 남았다. 

 

 

그런데 JAVA 프로그램 구동에는 dependecy 폴더에 위치한 파일만 있으면 된다. 로컬에서 COPY한 src, gradle, build.gradle 등은 빌드가 끝나면 더이상 필요없다. 또한 JDK 베이스이미지도 단순히 JDK 실행을 목적으로 한다면 용량이 크다. 

 

여기서 MultiStage의 장점이 드러난다. 

 

FROM으로 JRE 베이스이미지를 가져온다. JRE는 JDK 안에 있는 패키지로 JAVA 프로그램 실행환경을 제공한다. 단순히 실행만 목적으로 한다면 JDK가 있을 필요가 없다. JRE만 있어도 충분히 JAVA 프로그램 구동이 가능하다.

 

 

 

 

위 그림처럼 dependency 폴더에 위치한 클래스 파일을 JRE베이스 이미지로 COPY해야 한다. 이때 --from build 옵션을 두면 Build Stage의 베이스이미지 경로에서 Run Stage의 베이스이미지 경로로 COPY가 이루어진다. JRE베이스이미지의 /app 디렉토리에는 JAVA 프로그램 구동에 필요한 클래스 파일들이 위치한다. 

 

이렇게 되면 JAVA 프로그램 구동을 위한 최종이미지가 만들어진다. 최종이미지는 JDK 이미지가 아닌 JRE 이미지를 베이스로 하여 더욱 가볍다 또한 java 소스가 들어있는 src나 빌드에 필요한 gradle 파일이 없고 오로지 프로그램 실행에 필요한 파일들만 /app 디렉토리에 존재한다. Stage를 Multi로 구성함으로써 이미지를 경량화 시킨 것이다. 

 

실제로 JDK를 베이스이미지로 하고 src와 gradle파일을 모두 가지고 있는 이미지랑 MultiStage로 구성하여 생성된 이미지랑은 용량차이가 난다. 

 

 

JDK로 이미지를 만들었을때는 457MB였는데 Multi Stage로 JRE를 베이스 이미지로 만들면 256MB로 사이즈가 뚝 떨어진다. 그럼 해당 이미지로 실제 컨테이너를 구동해보자. 

 

 

docker run으로 컨테이너를 실행하고 docker exec로 컨테이너 내부로 들어가 보았다. 컨테이너의 /app 디렉토리에 프로그램 구동에 필요한 파일들이 위치해 있다. JRE베이스이미지로 한 최종이미지가 컨테이너로 잘 생성되었음을 확인할 수 있다. 이렇듯 MultiStage 방식은 베이스이미지를 두 개 이상 가져와 작업을 분리하여 최종이미지를 경량화하는데 목적이 있다. 

 

 

 

 

 

반응형