Programming-[Backend]/Java

[TIL] Docker image JVM Heap 크기 및 옵션 설정, buildpack-gradle bootBuildImage, Packeto buildpack

컴퓨터 탐험가 찰리 2024. 7. 26. 16:23
728x90
반응형

 

1. buildpack 개념과 bootBuildImage

 

원래 애플리케이션을 이미지로 만들어내기 위해서 Dockerfile을 작성하였다. 이 과정은 보통 애플리케이션의 파일들을 COPY 명령어(레이어)로 도커 환경 내로 모두 복사하고, ENV, CMD, ENTRYPOINT와 같은 명령어들을 모두 작성하는 방식으로 진행한다.

 

buildpack은 Dockerfile을 직접 작성하는 과정을 한 번 더 캡슐화하여 이미지를 편리하게 만들 수 있게 만든 라이브러리다. 유명한 Cloud Native Computing Foundation(CNCF)의 프로젝트의 일환이며, 2018년 Heroku에서 Cloud Native Buildpacks 프로젝트를 시작하였다. 이후 open-source 형태로 Packeto buildpack이 가장 유명해졌고 자바, 파이썬 등 대부분의 언어를 이미지화할 수 있는 툴을 제공한다.

ref.) https://bell-sw.com/blog/how-to-use-buildpacks-to-build-java-containers/

 

자바와 관련해서라면, 여러 회사들에서 제공하는 JVM을 선택하고 그 옵션을 줄 수 있는 환경변수들이 따로 있다. JVM은 유명한 Amazon Corretto, Oracle JDK, GraalVM, BellSoft Lieberica(default) 등이 있다. 위 참조 링크에서 볼 수 있듯이 Packeto buildpack 프로그램을 직접 설치하여 빌드할 수도 있다. 다만 여기서는 gradle의 bootBuildImage를 다룰려고 한다.

 

gradle의 bootBuildImage는 gradle의 task 중 하나이다.(gradle 관련 정리했던 내용: https://whitepro.tistory.com/957) bootBuildImage를 통해 굳이 Dockerfile 명령어를 직접 사용할 필요도 없고, 이미지 최적화를 위한 multi-stage build를 신경쓸 필요도 없다. 예를 들어 JDK는 애플리케이션을 실행하는데 필요한 JRE에 대비하여 용량이 거의 2배나 되는데, 기존에는 multi-stage build를 하지 않으면 그냥 JDK를 모두 포함하여 이미지를 빌드하게 되어 실행에는 불필요한 파일들을 포함하게 된다. bootBuildImage가 이런 최적화 등을 대신해준다.

 

아래 Pseudo code 내용처럼 그냥 코딩하듯이 docker image build에 필요한 과정들을 수행하는 코드들을 작성할 수 있다.

//Pseudo code일뿐 작동하는 코드는 아님

// 일반 코딩을 하듯이 docker image에 필요한 내용들을 작성할 수 있다.
def envVal = project.hasProperty('env') ? project.property('env') : ''
if (envVal == 'prd' || envVal == 'dev') {

    // AWS ECR 로그인 등 필요한 과정들을 수행할 수 있다.
    project.exec {
        commandLine 'aws', 'ecr', 'get-login-password', '--region', 'ap-northeast-2'
        standardOutput = output
    }

    registryPassword = output.toString('UTF-8')
	
    // 이미지를 빌드한다.
    bootBuildImage {
        createdDate = getDate() // getDate()등 기본 함수도 제공한다.
        imageName = "${yourApplicationImageNameForAWSECR}"
        environment = ['BPE_JAVA_OPTS' : '-Xms512m -Xmx1024m']
        publish = true
        docker {
            publishRegistry {
                username = registryUserName
                password = registryPassword
                url = privateRegistryUrl
            }
        }
    }

} else {
    //...
}

 

 

 

2. DockerImage에서 JVM Heap 메모리 설정

 

위에서 build된 image를 AWS ECR에 push하고, k8s로 배포하면 애플리케이션이 실행되며 찍히는 log 내용들을 argoCD 등에서 확인할 수 있다.

 

[argoCD] 화면

 

 

로그 내용을 보면,

 

첫 번째 줄에서 JVM memory가 사용할 수 있는 영역이 약 11 GB로 계산되는 것을 볼 수 있다. 

 

두 번째 줄에서 위에서 다룬 packeto buildpack의 memory-calculator가 사용되는 것을 볼 수 있다.

 

세 번째 줄에 JVM Memory Configration 값을 설정하는 것을 볼 수 있는데, JVM의 환경 변수 값이나 실행을 위한 값을 설정하면 그대로 이미지에 적용되는 것을 확인할 수 있다. Packeto에서 제공하는 JVM을 위한 환경 변수들은 대체로 'BPL_' 라는 prefix를 붙인다. 자세한 내용은 아래 Packeto buildpack github에서 확인할 수 있다.

https://github.com/paketo-buildpacks/bellsoft-liberica

 

 

여기서 prefix는 'BPL_' 뿐만 아니라 'BPE_' 등 다양한 prefix들이 있다. 이것은 위에서 설명했듯이 JVM도 회사별로 여러가지 버전이 있고 우선순위가 있다.

 

JAVA_TOOL_OPTIONS는 buildpack에서 제공하는 옵션이고, 이것보다 우선순위는 대부분의 자바 JVM에서 기본적으로 제공되는 JAVA_OPTS 설정이다.

https://stackoverflow.com/questions/3933300/difference-between-java-opts-and-java-tool-options

 

JAVA_OPTS 설정은 환경 변수로 넣어줄 수 있다. JAVA_OPTS에 JVM Heap 크기 값의 옵션을 주면, 위 argoCD에서 인스턴스 실행 시 해당 옵션이 참조되어 실행되는 것을 확인할 수 있었다.

 

JAVA_OPTS : -Xms512m -Xmx1024m

 

 

참고로 datadog agent를 의존성에 추가하면, 해당 옵션들도 datadog sidecar가 참조하거나 변경할 수 있어서, 이 부분도 한 번 살펴봐야한다.

728x90
반응형