4 minute read

Google Jib 를 활용하여, 쉽고 빠르게 java image 를 만들어봅시다.


Container

" Write Once, run anywhere. "

컨테이너는 어느 환경에서든 일관된 동작을 보장하며,

애플리케이션의 빠른 배포와 확장을 가능하게 하므로

개발 생산성을 크게 높일 수 있습니다.

이를 위해서는 Dockerfile 을 작성하고,

지속적으로 관리해야하는 어려움이 있죠.

또한 서비스가 커짐에 따라,

이미지를 빌드하는 데에 걸리는 소요 시간은 점점 늘어납니다.

이로부터 CI/CD 시간이 길어지므로,

개발 생산성은 점차 떨어지게 되겠죠.

이를 해결하려면,

DockerFile 스크립트를 조정하여

이미지를 빌드하는 과정을 최적화해야 합니다.

아래와 같이 잘 정리된 아티클을 참고할 수 있습니다.

image

하지만 Devops 가 아닌 애플리케이션 개발자에게는,

DockerFile 에 대한 러닝 커브가 존재합니다.

이러한 문제점을 Google Jib 를 통해서 효과적으로 해결할 수 있습니다.

Jib 를 사용하면 DockerFile 을 작성하지 않고도,

빠르고 효율적으로 이미지를 빌드할 수 있습니다.

image

Jib 에 대해서 더 알아보기 전에,

전통적인 Docker Build 방식부터 먼저 살펴봅시다.

Docker Build

image

일반적으로는 Docker Build 방식을 사용하여 컨테이너화를 수행합니다.

dockerFile 을 통해 image 를 만들고 Dokcer registry 에 만들어진 image 를 push 한 뒤,

배포 서버에서 run 하면 컨테이너가 생성됩니다.

조금 더 디테일하게 들어가볼까요?

image

자바 애플리케이션을 빌드하여 JAR 파일을 생성하고,

DockerFile 에서 base image, layer, command 등을 설정한 뒤에,

DockerFile 과 JAR 파일을 Docker Daemon 에 전달하여, image 를 build 합니다.

그리고 AWS ECR, Docker Hub 와 같은 Docker registry 에 image 를 push 합니다.

배포 서버에서 Docker registry 에 push 된 image 를 pull 하여 애플리케이션을 실행합니다.

Optimize Docker image

위에서 언급한 바와 같이,

프로젝트가 점점 커지면 image build 시간도 같이 길어집니다.

이는 개발 생산성을 크게 떨어뜨리는 요인이 되죠.

해결하기 위해서는,

DockerFile 을 최적화해야합니다.

FROM openjdk:8
COPY target/springdemo-*.jar /app.jar
ENTRYPOINT java -jar /app.jar

가장 먼저 시도할 수 있는 것은,

경량화 된 base image 를 사용하는 것 입니다.

openjdk:8 image 는 284 MB 이므로,

alpine 버전의 base image 로 대체하면 200 MB 를 절약할 수 있습니다.

FROM openjdk:8-jre-alpine
COPY target/springdemo-*.jar /app.jar
ENTRYPOINT java -jar /app.jar

하지만 이미 프로젝트가 커져있다면,

base image 를 아무리 경량화 된 버전을 사용한다고 해도

image build time 은 여전히 느리죠.

.dockerignore 에서 target/springdemo-*.jar 를 ignore 하도록 설정하면 해결할 수 있을까요?

image build time 은 꽤 빨라지겠지만,

소스 코드의 변경이 발생하면,

어차피 target/springdemo-*.jar 를 포함하여 image 를 build 해야 하므로

적절한 해결책은 아닌 것 같습니다.

다시 dockerfile 파일을 봅시다.

FROM openjdk:8-jre-alpine
COPY target/springdemo-*.jar /app.jar
ENTRYPOINT java -jar /app.jar

아래와 같이 layer 가 구성될 것입니다.


+---------+---------+
| layer2: | app.jar |
| layer1: |openjdk:8|
+---------+---------+

docker build 를 최초 수행하면, 해당 layer 가 생성되고 캐싱됩니다.

한번 더 docker build 를 수행하면

변경점이 없는 layer 는, 캐싱된 layer 를 사용하여 image 를 build 합니다.

JAR 파일은 프로젝트가 커짐에 따라,

size 가 매우 늘어나게 될 텐데요.

JAR 파일 전체가 하나의 layer 를 이루게 된다면 비효율적이지 않을까요?

문제 상황을 다시 정리해봅시다.

  • 소스 코드나 dependency 를 일부만 수정해도 JAR Layer 를 새로 생성하기 때문에 image build time 이 매우 느려짐에 따라 개발 생산성 저하가 우려된다.

Spring boot 2.3 version 부터,

효과적으로 layering 할 수 있는 기능을 제공하고 있습니다.

image

이를 통해, JAR 파일을 네 개의 레이어로 분리할 수 있습니다.

  • dependecies
  • spring-boot-loader
  • snapshot-dependencies
  • application

아무래도 변경점이 가장 많이 발생하는 layer 는 application 이겠죠.

java -Djarmode=layertools -jar springdemo.jar extract

--
- layer1. application
- layer2. dependencies
- layer3. snapshot-dependencies
- layer4. spring-boot-loader

소스 코드를 변경하여도,

application layer 만 재생성하고

다른 layer 는 캐싱된 layer 를 사용하게 될 것이므로

docker image build time 이 효과적으로 개선됩니다.

  • 최초 build time 은 아무래도 거의 동일하겠죠?

이 방법 외에도 추가적으로 알려진 dockerFile best practice 를 적용하여,

조금 더 최적화 할 수도 있겠죠.

위 과정을 java 애플리케이션 개발자가 수행하게 된다면,

dockerFile 과 컨테이너에 대한 러닝 커브가 발생할 것이고

비지니스 로직에 집중할 리소스가 줄어들게 될 것입니다.

dockerFile 을 최적화하려는 과정에서 실수가 발생할 여지도 굉장히 많죠.

이 문제들은 Google Jib 를 사용하여 효과적으로 해결할 수 있습니다.

Google Jib

image

DockerFile 을 작성하지 않고, maven 이나 gradle 과 같은 build system 에서

java application image 를 효과적으로 build 할 수 있습니다.

일반적인 Docker build 파이프라인을 다시 한번 살펴본 뒤에,

image

Jib 를 활용한 파이프라인과 비교해봅시다.

image

굉장히 간단하죠.

아래와 같이 gradle 설정을 해준 뒤,

plugins {
  ...
  ip 'com.google.cloud.tools.jib' version '3.1.4'
  ...
}

...
jib {
    from {
      image = 'your-base-image'
    }
    to {
      image = 'remote-docker-registry/your-application-image'
      auth {
          username = 
          password = 
      }
    }
    // you can add additional options like jvm flags, program arguments, etc...
}
./gradlew jibDockerBuild

위 명령어 만으로 docker image build 를 효과적으로 수행할 수 있습니다.

image

DockerFile 을 알지 못하더라도,

Jib 를 사용하면

최적화 된 dockerFile 을 사용하여 image build 를 하는 것과 같은 효과를 누릴 수 있습니다.

image

image

프로젝트가 커질수록, Jib 의 성능은 더욱 명확하게 체감할 수 있습니다.

사내 서비스의 평균 배포 시간이 기존 15분 이상에서,

Jib 도입 후 1~3분으로 대폭 줄었습니다.


컨테이너 기반의 자바 프로젝트를 구성할 때,

DockerFile 을 작성하지 않고도 효과적으로 컨테이너화가 가능한

Jib 의 도입을 고려하는 것도 좋을 것 같습니다.


References

  1. GoogleContainerTools Official git repository
  2. Build containers faster with Jib, a Google image build tool for Java applications - Google Cloud Tech (VelocityConf)
  3. No more Dockerfile with Jib + Spring Boot plugin - Tom Gregory Tech

Categories:

Updated:

Leave a comment