Spring Boot 2.3 버전부터 내장 애플리케이션을 Docker Image로 만드는 라이브러리가 포함되어 배포되었다. 내 기억에 초기에는 Jib를 사용했었던 것 같은데, 2.3.8 버전을 사용해보니 BuildPack을 사용하도록 변경되어있었다. BuildPack에 대한 내용은 아래 사이트를 참고하자.
Cloud Native Buildpacks
Cloud Native Buildpacks transform your application source code into images that can run on any cloud.
buildpacks.io
백문이불여일견 기존에 개발하고 있던 프로젝트(2.2.6)를 한번 2.3.8로 버전을 올리고, 내장된 BuildPack 라이브러리를 통해 Docker Image를 만들어 보겠다.
[주의사항]
Spring 2.3 버전부턴 javax 패키지 하위에 있던 validation class들이 spring-boot-starter-validation이라는 스타터 라이브러리로 분리되어 배포된다. 2.3 버전 이하에선 spring-boot-starter-web 하위에 포함되어있었기에 2.2 버전을 사용하다가 2.3 버전으로 올릴 때 javax 패키지 하위에 있는 validation class를 사용한 코드가 있는 경우 에러가 난다.
gradle wrapper를 통해 빌드를 실행하고, plain 모드로 각 task가 어떻게 진행되는지 봤다. bootBuildImage task를 실행하면, buildPack에서 애플리케이션을 도커 이미지로 만들기 위해 필요한 buildpack 도커 이미지를 먼저 pulling 한다.
그 이후, 도커 이미지를 만들면서 실행된 각종 단계들이 로그로 찍히게 된다. 참고로 buildpack은 애플리케이션이 구동되기 위해 필요한 jvm 사이즈를 알아서 계산해서 넣어준다. 이는 컨테이너로 애플리케이션을 구동할 때 메모리를 최적으로 사용하도록 하기 위함인 것 같다.
다음은 직접 Dockerfile을 통해 도커 이미지로 만드는 방법이다. 가장 먼저 build.gradle에 다음과 같이 task를 추가하자.
// separate jar into app and library layers
task moveLib {
doLast {
def unpackDir = "$buildDir/unpack"
ant.move(file: "${unpackDir}/app/BOOT-INF/lib", toFile: "${unpackDir}/lib")
}
}
task unpackJar(type: Copy) {
def unpackDir = "$buildDir/unpack"
delete unpackDir
from zipTree(jar.getArchiveFile())
into "$unpackDir/app"
finalizedBy moveLib
}
build {
finalizedBy unpackJar
}
fatJar 파일을 app과 lib디렉토리로 분리한다. 다음은 Dockerfile을 다음과 같이 작성해보자.
# Build Stage
FROM gradle:6.8.0-jdk8 AS builder
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
# Only copy dependency-related files
COPY build.gradle settings.gradle ./
# Copy Sourcefile
COPY src src
# build soruce file with dependnecies
RUN gradle clean build --console=plain -x test
# Runtime Stage
FROM openjdk:8-jre-alpine
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
ARG buildDir=$APP_HOME/build/unpack
COPY --from=builder ${buildDir}/lib BOOT-INF/lib
COPY --from=builder ${buildDir}/app .
EXPOSE 8080
CMD ["java", "org.springframework.boot.loader.JarLauncher"]
그리고 다음 명령어를 통해 Dockerfile을 빌드하고 도커 이미지를 만들어보자. -f 옵션 뒤에 붙는 부분은 내 Dockerfile이 다른 디렉터리 하위에 있기 때문이다. Dockerfile의 위치를 설정하는 옵션으로 프로젝트 루트 경로에 Dockerfile이 바로 있다면 작성 안 해줘도 된다.
docker buid -t user-test-dockerfile -f docker/Dockerfile .
도커 이미지가 생성된 사이즈를 보니, 기존에 build.gradlew에서 bootJar Task에서 layered 구문으로 fatJar를 분리하고, Dockerifle에서 build stage를 통해 빌드한 이미지 사이즈보다 크게 나온것을 확인할 수 있었다(ㅡ,ㅡ;;).
Dockerfile은 각 명령어 라인들이 도커파일 이미지의 layer가 되고 용량을 잡아먹기 때문에 최소화하는 게 좋다.
docker history 명령어를 통해 buildpack으로 생성된 이미지의 layer를 확인해보니 꽤 많이 생겨있는 것을 확인할 수 있는데, 이 점은 단점일 수도 장점일 수 도있다. 왜냐하면 도커는 기본적으로 도커 이미지를 만들 때 각 layer에 대한 캐싱 기능을 지원한다. 따라서 상세하게 나뉜 layer는 빌드 타임을 많이 줄일 수 있다는 장점이 있으면서, 도커 이미지의 크기가 커질 수도 있다는 단점이 존재한다.
또한 buildPack의 경우, 도커 이미지를 만들때 필요한 커스텀 기능들이 많이 부족하다, EXPOSE 또는 파일을 옮기기 위한 COPY의 기능, RUN 등 옵션들을 지정할 수 없는 것 같다. build image를 변경하면 해결이 되는지는 모르겠지만,, buildpack을 이용한 dockerizing은 아직 시기상조일 것 같다.
'Programming > Spring' 카테고리의 다른 글
[Spring Cloud] Spring Cloud Gateway - 다운스트림 로그 확인 (1) | 2020.11.06 |
---|---|
[Spring Boot] Filter (2) -Response Body Modify (5) | 2020.08.04 |
[Spring Boot] Filter (1) - Request Body Modify (0) | 2020.07.15 |
[SpringBoot] Dockerizing a Spring Boot Application (0) | 2019.11.19 |
[Spring Boot] Java Servlet Filter for Logging Request Parameter (2) | 2019.11.12 |