JiwonKKang/[Docker] Dockerfile - SpringBoot

Created Fri, 18 Aug 2023 20:53:42 +0900 Modified Fri, 18 Aug 2023 21:53:38 +0900

A Better Dockerfile

Spring Boot fat JAR은 JAR 자체가 패키징되는 방식 때문에 자연스럽게 “layers"를 갖습니다.
먼저 압축을 풀면 이미 외부 종속성과 내부 종속성이 구분되어 있습니다.
Docker 빌드의 한 단계에서 이 작업을 수행하려면 먼저 JAR의 압축을 풀어야 합니다.
다음 명령은 Spring Boot fat JAR의 압축을 풉니다.

압축해제

mkdir build/dependency 
cd build/dependency 
jar -xf ../libs/sbb-{버전넘버}.jar

Dockerfile

FROM eclipse-temurin:17-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=build/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.mygroup.sbb.SbbApplication"]

도커 이미지 빌드

docker build -t sbb .

도커 컨테이너 실행

docker run -p 8080:8080  sbb

**이제 세 개의 계층이 있으며 모든 애플리케이션 리소스는 이후 두 계층에 있습니다. 응용 프로그램 종속성이 변경되지 않으면 첫 번째 계층(BOOT-INF/lib에서)을 변경할 필요가 없으므로 기본 계층이 이미 캐시되어 있는 한 빌드가 더 빠르고 런타임 시 컨테이너 시작도 더 빠릅니다. **

Spring Boot Layer Index

Spring Boot 2.3.0부터 Spring Boot Maven 또는 Gradle 플러그인으로 빌드된 JAR 파일에는 JAR 파일에 레이어 정보가 포함됩니다. 이 계층 정보는 응용 프로그램 빌드 간에 변경될 가능성에 따라 응용 프로그램의 일부를 구분합니다. 이를 사용하여 Docker 이미지 레이어를 더욱 효율적으로 만들 수 있습니다.

**레이어 정보는 JAR 콘텐츠를 각 레이어의 디렉터리로 추출하는 데 사용할 수 있습니다. **

mkdir build/extracted
java -Djarmode=layertools -jar build/libs/sbb-{버전넘버}.jar extract --destination build/extracted

Dockerfile

FROM eclipse-temurin:17-jdk-alpine
VOLUME /tmp
ARG EXTRACTED=build/extracted
COPY ${EXTRACTED}/dependencies/ ./
COPY ${EXTRACTED}/spring-boot-loader/ ./
COPY ${EXTRACTED}/snapshot-dependencies/ ./
COPY ${EXTRACTED}/application/ ./
ENTRYPOINT ["java","org.springframework.boot.loader.JarLauncher"]

도커 이미지 빌드

docker build -t sbb .

도커 컨테이너 실행

docker run -p 8080:8080  sbb
 💡 Spring Boot Fat `JarLauncher`는 JAR에서 이미지로 추출되므로 기본 애플리케이션 클래스를 하드 코딩하지 않고 애플리케이션을 시작하는 데 사용할 수 있습니다.

Multi-Stage Build - DEPENDECY

위에서 만든 Dockerfile은 fat JAR이 이미 command line에서 빌드되었다고 가정했습니다. multi-stage 빌드를 사용하고 한 이미지에서 다른 이미지로 결과를 복사하여 docker에서 해당 단계를 수행할 수도 있습니다.

다음 예제에서는 Gradle을 사용하여 이를 수행합니다.

FROM eclipse-temurin:17-jdk-alpine as build
WORKDIR /workspace/app

COPY gradlew .
COPY .gradle .gradle
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
COPY src src

RUN ./gradlew build -x test
RUN mkdir build/dependency && (cd build/dependency; jar -xf ../libs/sbb-{버전정보}.jar)

FROM eclipse-temurin:17-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/build/dependency
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/*","com.mygroup.sbb.SbbApplication"]

도커 이미지 빌드

docker build -t sbb .

도커 컨테이너 실행

docker run -p 8080:8080  sbb

Multi-Stage Build - EXTRACT

이번엔 jar파일의 extract를 사용하여 레이어를 추출하고 그 레이어들을 이용해서 Docker image를 Multi-Stage build로 만들어보겠습니다.

FROM eclipse-temurin:17-jdk-alpine as build  
WORKDIR /workspace/app  
  
COPY gradlew .               
COPY .gradle .gradle  
COPY gradle gradle  
COPY build.gradle .  
COPY settings.gradle .docker  
COPY src src  
  
RUN ./gradlew build -x test  
RUN mkdir build/extracted && (java -Djarmode=layertools -jar build/libs/sbb-0.0.8.jar extract --destination build/extracted)  
  
FROM eclipse-temurin:17-jdk-alpine  
VOLUME /tmp  
ARG EXTRACTED=/workspace/app/build/extracted  
COPY --from=build ${EXTRACTED}/dependencies/ ./  
COPY --from=build ${EXTRACTED}/spring-boot-loader/ ./  
COPY --from=build ${EXTRACTED}/snapshot-dependencies/ ./  
COPY --from=build ${EXTRACTED}/application/ ./  
ENTRYPOINT ["java","org.springframework.boot.loader.JarLauncher"]

위 코드를 간략히 설명하자면

스테이지 1

  1. 베이스이미지 eclipse-temurin:17-jdk-alpine로 설정
  2. 디렉토리를 /workspace/app 이동(cd와는 레이어가 생기지않는다는 차이점이있습니다)
  3. 빌드에 필요한 파일들 모두 컨테이너에 COPY
  4. 테스트는 제외하고 빌드
  5. build/extracted 디렉토리를 만들고 그곳에 레이어 추출

스테이지 2

  1. 베이스이미지 eclipse-temurin:17-jdk-alpine로 설정
  2. 컨테이너의 /tmp를 볼륨으로 설정
  3. 매개변수 경로 설정 EXTRACTED=/workspace/app/build/extracted
  4. 스테이지1에서 만들어진 이미지에서 레이어들 COPY
  5. 엔트리포인트 실행

이렇게하면 빌드에만 필요한 파일(스테이지1)들을 도커이미지(스테이지2)에 넣지않기때문에 도커 이미지를 경량화 시킬수있고 도커이미지가 경량화되면 빌드 및 배포가 빨라질 수 있습니다.