TIL

Spring Boot — Container Images

실행 가능 JAR의 구조와 jarmode tools

java -Djarmode=tools -jar app.jar list-layers   # 추출 가능한 레이어 목록
java -Djarmode=tools -jar app.jar extract        # 기본 layout으로 압축 해제
java -Djarmode=tools -jar app.jar extract --layers --destination extracted  # 레이어별로 분리 추출
java -jar app/app.jar   # 추출된 디렉터리 구조에서 실행

레이어드 JAR — 변경 빈도로 레이어 분리

순서 레이어 내용 변경 빈도
1 dependencies 정식 릴리스 의존성 가장 낮음 → 최하단(캐시 재사용 극대화)
2 spring-boot-loader org/springframework/boot/loader 로더 클래스 거의 안 바뀜
3 snapshot-dependencies SNAPSHOT 의존성 릴리스보다 자주 바뀜(분리해 릴리스 레이어 보호)
4 application 앱 클래스·리소스(BOOT-INF/classes, manifest) 가장 높음 → 최상단
<!-- Maven: spring-boot-maven-plugin -->
<configuration><layers><enabled>true</enabled></layers></configuration>
// Gradle
bootJar { layered() }

Dockerfile로 빌드 — multi-stage + 레이어별 COPY

FROM bellsoft/liberica-openjre-debian:25-cds AS builder
WORKDIR /builder
COPY target/*.jar application.jar
RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted

FROM bellsoft/liberica-openjre-debian:25-cds
WORKDIR /application
# 변경 빈도 낮은 레이어부터 COPY → 의존성 레이어는 캐시 재사용, 앱 코드만 바뀌면 마지막 레이어만 무효화
COPY --from=builder /builder/extracted/dependencies/ ./
COPY --from=builder /builder/extracted/spring-boot-loader/ ./
COPY --from=builder /builder/extracted/snapshot-dependencies/ ./
COPY --from=builder /builder/extracted/application/ ./
ENTRYPOINT ["java", "-jar", "application.jar"]

Cloud Native Buildpacks — Dockerfile 없이 이미지 생성

./gradlew bootBuildImage      # Gradle
mvn spring-boot:build-image   # Maven
spring.web.resources.cache.use-last-modified=false   # 정적 리소스가 last-modified에 의존하지 않도록

언제 무엇을 쓰나

기준 Dockerfile + layered jar Cloud Native Buildpacks
Dockerfile 관리 직접 작성·유지 불필요(빌드 명령만)
베이스 이미지·JRE 제어 완전 제어(원하는 base 지정) buildpack이 자동 선택
빌드 진입점 docker build bootBuildImage / build-image
적합 세밀한 커스터마이징·기존 Docker 파이프라인 표준화·빠른 시작·Dockerfile 보일러플레이트 제거