Spring Boot 과 Docker (with jib)

Junghoon Song
8 min readSep 16, 2019

--

이 글은 Spring Boot 기반의 Application 을 Docker 이미지로 배포하는 과정을 jib 를 사용하여 간소화하는 방법에 대해서 다루고 있습니다.

Spring Boot 에서의 배포

Spring Boot 로 오면서 Application 의 배포는 이전보다 더 쉬워졌습니다.

일반적으로 Spring Boot 에서의 배포는 Maven 이나 Gradle 등을 사용하여 빌드한 application.jar 를 Java Runtime 환경이 구성된 서버에 복사하여 실행하기만 하면 됩니다.

최근에는 Docker 등을 사용하여 컨테이너 이미지로 배포하고 실행하는 방법도 많이 사용하고 있는데, 이와 같은 기술을 사용하면 서버환경등에 구애 받지 않고 실행이 가능하다는 장점이 있습니다.

Dockerizing Spring Boot Application

Spring Boot Application 을 실행하기 위한 도커 이미지 생성은 Spring 공식 가이드문서 https://spring.io/guides/gs/spring-boot-docker 에서 확인할 수 있습니다.

문서내용대로 JRE 가 설치된 Base Image 에 빌드된 application.jar 를 복사하는 Dockerfile 을 생성하도록 합니다. (단 여기서는 좀 더 작은 용량의 alpine linux 를 베이스로 한 jre 이미지를 사용하였습니다.)

Gradle 에 Docker Image 를 생성하기 위한 Plugin 을 적용하도록 합시다.

여러가지 Plugin 이 있지만 여기서는 가이드에 나와있는 Docker Gradle Plugin 을 사용하도록 하겠습니다.

해당 플러그인에서 제공하는 Task 중 docker 는 이미지를 생성해주며, dockerPush 는 이미지를 생성하고 저장소에 push 까지 합니다.

바로 저장소에 push 까지 해보도록 하겠습니다.

빌드가 완료되면 아래와 같이 gaemi/jib-example-case1 이미지가 생성된 걸 확인할 수 있습니다.

저는 편의상 2개의 Tag 를 추가로 지정하여 사용하고 있는데, 이 부분은 적당히 고민해서 추가해주시면 되겠습니다.

history 를 살펴보면 jar 파일을 복사하는 COPY layer가 17mb 정도의 용량을 차지하는 걸 볼 수 있습니다. 빌드된 jar 파일과 거의 동일한 용량입니다.

이 Image 로 Container 를 실행하면 정상적으로 Spring Boot Application 이 구동되는걸 확인할 수 있습니다.

의존성을 분리하기

Docker 를 공부하셨던 분이라면 위와 같이 하나의 jar 파일을 복사하여 Image를 생성하는 방식은 Docker 의 Layer Caching 방식의 잇점을 잘 살리지 못하고 있다고 생각하실 수 있습니다.

우선 코드상의 약간의 변경사항을 추가하여 0.0.2-SNAPSHOT 버전을 빌드하여 history 를 비교해보도록 합시다.

보시면 31f9502c38a5 layer 까지 0.0.1-SNAPSHOT 버전과 동일하고 이후에 새로운 layer 가 추가된걸 확인할 수 있습니다.

우려하였던대로 제일 큰 작업인 jar 파일을 복사하는 부분은 Layer Caching 에서 제외되었으며 빌드시간도 이전과 큰 차이가 없습니다. (제 PC 에서는 각각 34초, 30초가 걸렸습니다.)

여기서 한가지 문제점을 더 확인할 수 있는데요. 새로운 버전을 빌드할때마다 만들어지는 layer 의 크기가 jar 파일과 동일하게 공간을 점유하기 때문에 낭비가 심하게 된다는 점 입니다.

jar 파일을 사용하면 Layer Caching 을 사용하기 어렵다

앞에 언급하였던 가이드에서는 이런 문제를 해결하기 위하여 의존성을 분리하여 Dockerizing 하는 방법도 소개하고 있습니다.

아래와 같이 Dockerfile 과 Gradle 스크립트를 작성하도록 합니다.

배포단위는 jar 파일이 아닌 Docker Image 이기 때문에 jar 파일을 사용할 필요가 없으니, 실제 빌드했던 class 와 library 를 분리하여 COPY 할 수 있습니다.

여기서 중요한것은 항상 library 를 먼저 COPY 하도록 해야 Layer Caching 의 잇점을 살릴 수 있다는 점 입니다.

잘 변경되지 않는 library 와 meta-inf 를 별도 layer 로 분리하여 Layer Caching 을 사용하고 있다

이런 방법으로0.0.1-SNAPSHOT0.0.2-SNAPSHOT 2 개의 Image 를 생성하여 비교해 봅시다.

먼저번과 가장 큰 차이점은 library 와 meta-inf 를 COPY 하는 layer 가 기존에 cache 된 layer 를 재사용하였으며, 따라서 0.0.2-SNAPHOST Image 를 생성하면서 추가로 점유한 용량은 2kb 정도에 불가하다는 점 입니다. (물론 빌드시간도 약간 단축되었습니다.)

이 방식을 사용하게 되면 기존보다 좀 더 효율적으로 Docker Image 를 생성하고 관리할 수 있게 됩니다.

Jib 를 사용하자

조금 더 나아지긴 하였지만 위와 같은 방법은 여전히 불편한 부분이 몇가지가 있습니다.

첫번째로는 불필요하게 jar 를 생성하고 압축을 해제하고 의존성을 분리하는 작업을 포함하고 있다는 점이며, 두번째로는 Docker Image 를 생성하기 위하여 빌드를 수행하는 서버에 Docker Daemon 이 실행되어 있어야 한다는 점 입니다.

Google Container Tools 중 하나인 Jib 는 Gradle 이나 Maven 의 Plugin 으로 동작하며, 쉽게 Java Application 을 Docker Image 로 생성해주는 도구입니다.

Java Application 에 최적화된 방법으로 Dockerizing 해주므로 직접 의존성을 분리하거나 하는 작업을 할 필요가 없습니다.

이 방법은 Dockerfile 도 필요없고 Docker Daemon 도 설치할 필요가 없습니다.

아래와 같이 Gradle 스크립트를 작성만 하면 됩니다.

jib Gradle Task 를 실행하면 Image 를 생성하여 저장소에 push 하여 줍니다.

Docker Hub 에 Image가 정상적으로 push 된 것을 확인할 수 있지만, 로컬환경에서는 빌드된 Image 를 확인할 수 없습니다.

이것은 Jib 가 기본적으로 Docker Daemon 을 사용하지 않기 때문입니다.

Docker Daemon 상에 Image 가 생성되지 않으므로 빌드서버에서 공간낭비를 줄일 수 있다는 장점이 있습니다. (캐시디렉토리를 삭제해야겠지만요.)

만약 Docker Daemon 위에 Image 를 빌드하고 싶다면 jibDockerBuild Task 를 사용하도록 합니다.

여기서는 원격저장소에 2개 버전의 Image 를 push 한 상태에서, pull 하여 비교하여 보도록 하였습니다.

Jib 로 빌드된 Image 는 history 상에 Image 의 hash 값을 확인하기가 어려워 inspect 로 layer 를 살펴봐야 합니다.

하지만 위와 같이 docker system df 명령어를 사용하였을 때 Image 전체 사이즈가 빌드한 Image 사이즈와 거의 유사한 것을 보아, 대부분의 layer 가 재사용되었다는걸 알 수 있습니다.

또한 Jib 를 사용할 때 두번째 빌드는 5초정도 소요되었으며, 기존에 30초 정도 걸릴때보다 훨씬 빠르게 수행되는 걸 확인할 수 있습니다.

Jib 를 사용하면 Spring Boot Application 에 최적화된 Docker Image 를 쉽게 생성할 수 있었습니다.

코드는 https://github.com/gaemi/jib-example 에서 확인하실 수 있습니다.

Spring Boot Application 에 대한 환경변수

실제 운영환경에서 Spring Boot Application 을 운영하려면 VM 옵션이나 Spring Profile 등을 변경해야 할 필요성이 있습니다.

Jib FAQ 상에도 여러가지 내용들이 나와있지만 개인적으로 맘에 들었던 방법은 환경변수를 등록하고 사용하는 방법이었습니다.

아래와 같이 Gradle 스크립트를 수정하여 default 로 사용할 환경변수를 입력합니다.

이렇게 하면 컨테이너를 실행할 때 -e 옵션을 사용하여 환경변수를 변경하여 실행할 수 있습니다.

여기까지 Jib 를 사용하여 Spring Boot Application 을 jar 가 아닌 Docker Image 로 배포하는 방법을 살펴보았습니다.

이 포스팅은 아래 자료들을 참고하였습니다.

감사합니다.

--

--