ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 도커에서 자바를 실행할 때의 문제
    책/스프링으로 하는 마이크로서비스 구축 2023. 12. 20. 17:17

     

    예전 자바 버전에서는 도커 컨테이너에 할당된 리소스 제약을 무시하고 도커 호스트의 전체 자원을 할당하는 이슈가 있었습니다.

    도커와 로컬의 비교, 그리고 자바 버전별로 리소스 사용을 비교해보겠습니다.

     

     


     

     

    1. 도커 없이 자바 커맨드 실행

    현재 로컬 컴퓨터에서 사용가능한 리소스들을 측정해보겠습니다.

    컴퓨터의 하드웨어를 살펴보면 8core를 사용할 수 있고, 메모리는 16GB 입니다.

     

    로컬 컴퓨터의 사양

     

    1) 코어

    먼저 사용가능한 코어 숫자를 확인해 봅니다.

    $ echo 'Runtime.getRuntime().availableProcessors()' | jshell -q

     

     

    Hardware Overview에서 보여주는 core 숫자와 일치하는 것을 확인할 수 있습니다.

     

     

    2) 최대 힙 사이즈

    이번에는 최대 힙 사이즈를 확인해보겠습니다.

    $ java -XX:+PrintFlagsFinal -version | grep MaxHeapSize

     

     

    별도로 최대 힙 크기를 지정하지 않으면 JVM은 사용가능한 메모리의 1/4을 최대 힙 크기로 지정합니다.

    로컬 컴퓨터의 최대 메모리는 16GB이니까 4GB(4294967296byte)를 사용합니다.

     

     

     

    2. adoptopenjdk/openjdk12 도커 컨테이너의 문제

    도커에 할당한 리소스 정보입니다.

    CPU 6core와 메모리 8GB, 최대 디스크 용량은 64GB를 할당하였습니다.

     

     

     

    1) 코어

    먼저 사용가능한 코어 숫자를 확인해 봅니다.

    $ echo 'Runtime.getRuntime().availableProcessors()' | docker run --rm -i openjdk:12.0.2 jshell -q

     

    m1 맥을 사용하고 있기 때문에 역시나 쉽게 안됩니다..

    필요한 것은 openjdk의 12 버전이니 직접 찾아서 받아봅니다.

     

    docker hub에서 openjdk 12 버전을 찾은 화면

     

    $ echo 'Runtime.getRuntime().availableProcessors()' | docker run --rm -i adoptopenjdk/openjdk12 jshell -q

     

    잘 설치 되었습니다.

    위에서 도커 엔진에 최대 cpu 6개를 할당했기 때문에 아래와 같이 6개를 반환하고 있습니다.

     

    이번에는 책에 나오는대로 --cpus를 사용해서 3개만 사용하도록 해보겠습니다.

    $ echo 'Runtime.getRuntime().availableProcessors()' | docker run --rm -i --cpus 3 adoptopenjdk/openjdk12 jshell -q

     

     

    책에서는 3개를 반환했는데... 저는 6개를 반환해줍니다.. ( 이런 이유로 따라하기 종류의 책은 선호하지 않습니다... ㅜ )

    12 이전 버전에서는 컨터네이너의 설정이 아닌 호스트 전체의 리소스를 가져다 쓴다고 되어있는데, adoptopenjdk/openjdk 12에서는 여전히 호스트의 설정을 따라가는 것 같습니다.

     

    $  echo 'Runtime.getRuntime().availableProcessors()' | docker run --rm -i --cpuset-cpus 0-2 adoptopenjdk/openjdk12 jshell -q

     

    gpt와 대화해보니 위 처럼 core의 index 범위를 설정해주면 해당 core만 사용하게 할 수도 있다고 합니다

     

     

     

    2) 메모리

    메모리는 어떤지 한 번 보겠습니다.

    $ docker run -it --rm adoptopenjdk/openjdk12 java -XX:+PrintFlagsFinal -version | grep MaxHeapSize

     

    위에서 도커 엔진에 8GB를 할당 하였고, JVM이 사용가능한 시스템 메모리의 1 / 4을 사용하기 때문에 약 2GB 가량이 할당된것을 확인할 수 있습니다.

     

     

     

    책에서는 컨테이너가 최대 사용할 수 있는 메모리를 1GB를 할당하면 JVM은 1 / 4인 256MB를 할당한다는 예제를 보여주고 있습니다.

     

    $ docker run -it --rm -m=1024M adoptopenjdk/openjdk12 java -XX:+PrintFlagsFinal -version | grep MaxHeapSize

     

    하지만 제 컴퓨터는 여전히 2GB를 사용하는 모습입니다. 호스트의 리소스를 기준으로 하고 있습니다.

     

     

     

    JVM에 직접 메모리를 할당하면 잘 적용됩니다.

    $ docker run -it --rm -m=1024M adoptopenjdk/openjdk12 java -Xmx800m -XX:+PrintFlagsFinal -version | grep MaxHeapSize

     

    위 명령어는 -m=1024M과는 별개로 JVM에 바로 800mb를 사용하겠다고 지시하는 명령어입니다.

     

     

     

    마지막으로 JVM에 데이터를 할당해보겠습니다.

    현재 도커 컨테이너에 할당되는 최대 힙 사이즈는 2GB 입니다.

    $ echo 'new byte[100_000_000]' | docker run -i --rm -m=1024M adoptopenjdk/openjdk12 jshell -q

     

    100mb의 데이터는 잘 할당 되는 것을 확인할 수 있습니다.

     

    $ echo 'new byte[3_000_000_000]' | docker run -i --rm adoptopenjdk/openjdk12 jshell -q

     

    3GB를 할당하려고 하니 에러가 발생합니다.

     

     

     

    3.  도커에서 자바 컨테이너 실행

    책에서는 12버전 부터는 된다고 적혀있으나, adoptopenjdk12에서는 문제가 발생하는 것을 확인하였습니다.

    그러면 잘 작동하는 버전을 찾아서 확인해보겠습니다. 17버전은 상당히 최신 버전이니 잘 될 것이란 희망을 가지고 도전해보겠습니다.

     

    1) 코어

    $ echo 'Runtime.getRuntime().availableProcessors()' | docker run --rm -i eclipse-temurin:17-jdk jshell -q
    $ echo 'Runtime.getRuntime().availableProcessors()' | docker run --rm -i --cpus 3 eclipse-temurin:17-jdk jshell -q

     

    첫번째 명령어를 통해서 도커에 할당된 6개의 코어를 사용하는 것을 확인할 수 있습니다.

    17버전에서는 --cpus 3개를 컨테이너에 할당하였을 때, JVM에서 3개의 코어를 인지하는 것을 확인할 수 있습니다.

     

     

    2) 메모리

    $ docker run -it --rm dclipse-temurin:17-jdk java -XX:+PrintsFlagsFinal -version | grep MaxHeapSize
    $ docker run -it --rm -m=1024M dclipse-temurin:17-jdk java -XX:+PrintsFlagsFinal -version | grep MaxHeapSize

     

    첫번째 명령어를 통해서 JVM이 도커에 할당된 16GB의 메모리 중 1/4인 4GB를 사용한다는 것을 확인할 수 있습니다.

    두번째 명령어를 통해서 컨테이너에 최대 1GB의 메모리를 할당한다면 256MB를 최대 힙사이즈로 사용한다는 것을 확인할 수 있습니다.

     

     

    이전에 확인했던 adoptopenjdk/openjdk12는 도커와 함께 사용하면 문제가 발생하는 것에 비해 temurin-17 버전에서는 잘 작동하는 것을 확인하였습니다.

     

     

     


     

     

     

     

     

    최신 버전에서는 보통 문제가 없겠지만, 자바와 도커를 같이 사용할 때 JVM이 이런 이슈를 가지고 있다는 것을 알게되었고,

    무엇보다도 JVM이 가용 메모리의 1/4 혹은 1/2을 최대 힙사이즈로 잡는다는 것을 알게 된 것이 재미있었습니다 ㅎㅎ

     

     

     

     

     

     

     


    [이미지 출처]

    http://www.acornpub.co.kr/book/microservices-spring (글 대표 이미지)

     

     

     

Designed by Tistory.