Docker MultiStage - Spring Boot

Choi Wang Gyu·2023년 9월 20일
0


도커 Multistage빌드를 통해 Spring Boot 이미지를 생성해 보며 겪은 경험을 적었습니다.

문제점

JVM 기반 언어의 단점은 실행시간이 다른 언어들보다 확연하게 느리다는 것이다
JVM 기반 동작 방식이 기본적으로 애플리케이션 시작 시점에 모든 것들을 메모리에 올린 상태에 최적화를 하고 이후 코드가 동작하는 특징 때문이다.
그래서 이러한 것을 해결하기 위해서 최대한 배포 시간을 줄여보자고 생각해보았다.
그중 하나가 도커이미지를 통해 배포하는데 이러한 배포 이동시간이라도 줄여보는 것도 극복하는 방법의 하나라고 생각했다.

MultiStage란?


Multi-stage는 여러 개의 Image Base를 사용하여 Docker이미지를 생성하는 것이다
사용하는 이유는? 도커 이미지 크기를 줄일 수 있습니다
FROM키워드로 스테이지(작업단위)를 구분합니다
중요한것은 마지막 스테이지가 최종 도커 이미지입니다.

이미지 크기 줄이면 머가좋은데?

이미지 크면 왜 안좋은가? 이미지도 용량을 차지해서 나중에 공간부족하다고 뜨기도하고, 불필요하게 결과물만 가지면되는데 패키지나 의존성도 같이 가질필요가없다

  1. 더 빠른 배포: 작은 이미지는 다운로드 및 업로드 시간이 감소하므로 배포 속도가 향상됩니다.
  2. 메모리 절약
  3. 보안: 작은 이미지는 보안 측면에서 이점을 가질 수 있습니다. 불필요한 패키지 및 의존성을 포함하지 않으면 공격 벡터가 줄어들고 이미지의 취약성이 감소합니다.
  4. 네트워크 대역폭 절약: 작은 이미지는 네트워크 대역폭을 덜 사용하므로 원격 서버 간 이미지 전송에 있어 이점을 제공합니다. 특히 클라우드 환경에서 중요합니다.
    그래서 이미지 만들 때 멀티스테이지 사용하는것을 추천한다

언제 사용하는게 적합?

크기 줄일려는것은 모두의 워너비인데 이 크기 줄이는게 누구에게 적합하냐면은
빌드를 하는 프로그램에게 적합하다
stage1에서는 빌드에 필요한 도구 설치 + 빌드 수행
stage2에서는 빌드 수행 결과물을 복사하여 결과 실행 (이미지로 만들기)
stage는 병렬실행도 가능하다 따라서 빌드 속도 향상 병렬실행하려면 주의사항은 관계가없어야한다

Spring Boot프로젝트 멀티스테이지 이용하기

결과부터 보면은 첫번째가 멀티스테이지 안사용한거고 2번째가 사용한것이다. 확실히 줄어들었다

그러면 이제부터 만드는 방법을 알아보겠다

멀티스테이지 안한경우
FROM gradle:7.4-jdk17-alpine as builder #as 표현 필수

WORKDIR /app
COPY ./ ./ 
RUN gradle clean build --no-daemon

가장 문제가 생겨서 삽집을 했던 부분은 바로 베이스 이미지였다.
FROM openjdk:17을 베이스 이미지로 하면 gradle이 없는거 같았다 그래서 여러개를 바꾸면서 찾다가 발견하게 gradle:7.4-jdk17-alpine 이미지다

yaml해석

일단 yaml파일을 보면서 이야기하면은

  • FROM gradle:7.4-jdk17-alpine as builder 에서 as builder는 별칭을 주는것이다 멀티스테이지시에 별칭은 필수다

  • WORKDIR /app 은 작업 디렉토리를 정하는데 도커이미지가 만들어지는 과정이 도커가 임시 가상컨테이너안에 들어가서 만드는데 이때의 가상컨테이너 내부의 작업 디렉토리를 정하는것이다. 내 컴퓨터(로컬)환경이 아니다

  • COPY ./ ./ 현재 작업 디렉토리(./)의 모든 파일과 폴더를Docker 이미지 내부의 ./ 디렉토리로 복사하는것이다. 2번째 인자가 도커 디렉토리를 의미한다

  • RUN gradle clean build --no-daemon 빌드한다

멀티스테이지 적용

FROM gradle:7.4-jdk17-alpine as builder

WORKDIR /app
COPY ./ ./
RUN gradle clean build --no-daemon
# 빌더 이미지에서 애플리케이션 빌드
#COPY ./build/libs/*.jar /app.jar

# APP
FROM openjdk:17.0-slim
WORKDIR /app # 
# 빌더 이미지에서 jar 파일만 복사
COPY --from=builder /app/build/libs/test17-0.0.1-SNAPSHOT.jar .

EXPOSE 8080
# root 대신 nobody 권한으로 실행
USER nobody
ENTRYPOINT ["java", "-jar", "test17-0.0.1-SNAPSHOT.jar"]

헷갈렸던 부분

첫번째 스테이지에서 했던 작업경로는 그대로이고 두번째 스테이지가 시작되면 작업디렉토리는 루트부터다 그래서 두번째 또한 작업 디렉토리를 해줘야한다 두번째 스테이지는 다른 컨테이너이다
COPY --from=builder /app/build/libs/test17-0.0.1-SNAPSHOT.jar . 여기서 builder컨테이너에 들어가 builder컨테이너 내부에있는
/app/build/libs/test17-0.0.1-SNAPSHOT.jar 파일을 두번째 컨테이너 .경로로 가져오는 것이다.

결과물

https://github.com/cwangg897/multistage-test

도커 컴포즈를 통해 이미지 실행

docker-componse파일
version: "3"

services:
  spring-app:
      build: .
      ports:
        - 8080:8080


도커허브에 이미지 올리기

중요한게 도커이미지 이름이랑 레포지토리 이름이랑 동일해야함
업로드중..

참고한 곳

profile
도파민 채우기

0개의 댓글