조금 평범한 개발 이야기

k8s, docker 에서 jvm 설정 최적화 하기 본문

개발/docker & k8s

k8s, docker 에서 jvm 설정 최적화 하기

jogeum 2021. 7. 24. 02:25

개요

k8s 는 워커의 자원 (cpu, memory) 을 공유하기 때문에 리소스 제한을 통해 파드가 사용할 자원을 미리 정의 합니다.

이는 워커에 있는 파드들이 요구하는 자원이 워커의 자원을 넘어서는 오버커밋 상태가 되었을때 특정 파드가 종료되거나 이상 동작되는 것을 막기 위함 입니다.

resources:
  requests:
    cpu: 1
    memory: 1Gi
  limits:
    cpu: 1
    memory: 1Gi

SPRING BOOT 으로 구성되었고  xms, xmx 메모리 설정이 아래와 같이 run.sh 에 1g 로 설정된 이미지가 있다고 가정해 보겠습니다.

# Dockerfile
ENTRYPOINT ["./run.sh"]
#!/usr/bin/env bash
exec java \
     -jar /usr/local/service/app.jar
     -Xms1g \
     -Xmx1g \

해당 이미지를 올린 파드는 보기에는 아무런 문제가 없어 보이지만 파드 리소스 제한과 jvm 최대 메모리 설정을 동일하게 1g 로 맞추면 oom killed 처리가 되면서 파드가 계속 재시작되는 현상이 발생 됩니다.

이것은 k8s 컨테이너가 실행될때 단지 spring boot jar 만 실행되는 것이 아니라 컨테이너가 실행되기 위한 최소 메모리가 필요하기 때문 입니다.

  • requests.memory ≠ Xms
  • limits.memory ≠ Xmx

JDK 8,9

jdk 8u131 과 jdk 9 에서는 이러한 문제를 해결하기 위해 UnlockExperimentalVMOptions, UseCGroupMemoryLimitForHeap 설정을 제공하는데 실행되는 컨테이너의 메모리 크기를 인식해 컨테이너 메모리의 25% 비율로 힙 메모리를 할당해 줍니다.

$ docker run -m 1GB openjdk:8u191-alpine java \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseCGroupMemoryLimitForHeap \
  -XshowSettings:vm \
  -version

VM settings:
    Max. Heap Size (Estimated): 247.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

하지만 컨테이너에 할당된 메모리를 25% 만 쓰는건 비효율적이기 때문에 해당 비율을 조금 높이고 싶다면 MaxRAMFraction 설정을 통해 크기를 늘릴 수 있습니다. 

MaxRAMFraction 값이 1이면 메모리 전체(100%)를 사용하겠다는 의미이고 2이면 (50%), 3이면 (33%)의 메모리를 할당 합니다.

$ docker run -m 1GB openjdk:8u191-alpine java \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseCGroupMemoryLimitForHeap \
  -XX:MaxRAMFraction=2 \
  -XshowSettings:vm \
  -version

VM settings:
    Max. Heap Size (Estimated): 494.94M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

JDK 10+

jdk 10 이후 부터는 컨테이너에 대한 지원이 강화되어 컨테이너에서 설정한 메모리를 자동으로 인식하고 적용됩니다. 즉 아무런 설정을 하지 않는다고 해도 자동으로 25% 비율로 메모리가 설정이 됩니다.

$ docker run -m 1GB openjdk:10 java \
  -XshowSettings:vm \
  -version

VM settings:
    Max. Heap Size (Estimated): 247.50M
    Using VM: OpenJDK 64-Bit Server VM

그리고 동일하게 MaxRAMFraction 값으로 해당 메모리 비율을 조정 할 수 있습니다.

$ docker run -m 1GB openjdk:10 java \
  -XX:MaxRAMFraction=2 \
  -XshowSettings:vm \
  -version

VM settings:
    Max. Heap Size (Estimated): 494.94M
    Using VM: OpenJDK 64-Bit Server VM

비율 최적화

앞서 컨테이너 힙 메모리를 비율을 통해 설정할 수 있다고 이야기 드렸지만 100%, 50%, 33% 비율로 메모리를 설정 하기에는 아직도 낭비되는 메모리가 많다고 느껴집니다.

이를 위해 MinRAMPercentage, MaxRAMPercentage 설정을 제공하는데 각각 힙 메모리의 최소, 최대 메모리를 설정 할 수 있습니다.

$ docker run -m 1GB openjdk:11 java \
  -XX:MinRAMPercentage=50.0 \
  -XX:MaxRAMPercentage=80.0 \
  -XshowSettings:vm \
  -version

VM settings:
    Max. Heap Size (Estimated): 792.69M
    Using VM: OpenJDK 64-Bit Server VM

XX:MaxRAMPercentage 설정값은 컨테이너에 할당된 메모리 크기에 따라 다르겠지만 75-80 정도가 컨테이너가 동작되는 메모리를 고려한 적정 수준으로 보여집니다.


결론

k8s 컨테이너에 JVM 을 올리려면 Xms, Xmx 설정 말고 MinRAMPercentage, MaxRAMPercentage 설정을 쓰자.


참고

'개발 > docker & k8s' 카테고리의 다른 글

k8s 컨테이너가 종료될때 원인 파악하기  (0) 2021.08.06
k8s 리소스 제한 하기  (0) 2021.07.24
KUBERNETES DASHBOARD 사용하기  (3) 2019.10.15
Comments