컨테이너(Kubernetes Pod) 환경에서 Java 애플리케이션을 운영할 때, JVM 메모리(Heap Size)를 효과적으로 설정하는 것은 성능 안정성과 OOMKilled 방지에 매우 중요합니다.
일반적인 -Xms, -Xmx 절대값 대신, 컨테이너 환경의 유연성을 극대화하는 백분율 기반 설정 방법을 중심으로 JVM 힙 사이즈를 최적화하는 방법을 안내합니다.
1. 왜 컨테이너에서는 -Xmx 대신 백분율을 사용해야 할까요?
전통적인 서버 환경에서는 -Xmx512m와 같이 힙의 최대 크기를 절대값으로 설정하는 것이 일반적이었습니다. 하지만 Kubernetes 환경에서는 상황이 다릅니다.
- JVM의 컨테이너 인식: 최신 JVM (JDK 8u191 이상, JDK 10 이상)은 -XX:+UseContainerSupport (JDK 10부터 기본 활성화) 옵션을 통해 호스트 메모리가 아닌 컨테이너에 설정된 메모리 제한(Limit)을 실제 사용 가능한 RAM으로 인식합니다.
- 유연성 확보: 힙 크기를 메모리 제한의 백분율로 설정하면, Pod의 메모리 Limit을 변경할 때마다 JVM 옵션을 수정할 필요 없이 JVM이 자동으로 힙 크기를 조절하여 유연하게 대응할 수 있습니다.
2. 핵심 JVM 옵션: RAM Percentage 설정
컨테이너 환경에서 힙 크기를 설정하는 가장 효과적인 방법은 다음과 같은 백분율 옵션을 사용하는 것입니다.
| 옵션 | 기능 | 권장 설정 | 참고 |
| -XX:MaxRAMPercentage | 힙의 최대 크기를 컨테이너 Limit의 백분율로 지정합니다. | 60 ~ 80 | 가장 중요합니다. 100% 미만으로 설정하여 Non-Heap 공간을 확보해야 합니다. |
| -XX:InitialRAMPercentage | 힙의 초기 크기를 컨테이너 Limit의 백분율로 지정합니다. | MaxRAMPercentage와 동일 | 힙 크기 변경(확장)으로 인한 런타임 성능 저하를 방지합니다. |
💡 최적의 튜닝 원칙: 힙 크기 고정
성능 최적화를 위해 InitialRAMPercentage와 MaxRAMPercentage를 동일한 값으로 설정하여 JVM 힙 크기를 컨테이너 메모리 제한의 일정 비율로 고정하는 것이 좋습니다.
# 예시: 컨테이너 메모리 Limit의 75%를 힙 크기로 고정
-XX:InitialRAMPercentage=75
-XX:MaxRAMPercentage=75
컨테이너 환경에서 Java 애플리케이션의 응답 시간을 예측 가능하게 유지하고, 힙 확장으로 인한 CPU 오버헤드와 일시적인 서비스 지연을 피하기 위해 -XX:InitialRAMPercentage와 -XX:MaxRAMPercentage를 동일한 값으로 설정하여 힙 메모리 크기를 시작부터 고정하는 것이 표준적인 튜닝 방법입니다.
또한, Kubernetes 환경에서는 메모리 요청(requests)과 제한(limits)이 동일할 때 Guaranteed QoS 등급을 받아 최고의 안정성을 보장합니다. JVM 힙 설정 역시 이 안정성 철학을 따라야 합니다.
💡 버전 요구사항
| JDK 버전 | -XX:MaxRAMPercentage, -XX:InitialRAMPercentage 지원 | 참고 사항 |
| JDK 8 | 8u191 이상 버전부터 지원 시작 | 8u131에서 컨테이너 지원 기능이 처음 도입되었으나, 백분율 옵션은 8u191 이후에 안정적으로 사용할 수 있게 되었습니다. |
| JDK 9 | 9 버전부터 지원 | |
| JDK 10 | 10 버전부터 지원 | UseContainerSupport 옵션이 기본값으로 활성화되었습니다. |
| JDK 11 이상 | 모든 버전에서 지원 | 최신 LTS(Long-Term Support) 버전인 JDK 11, 17, 21 등에서 가장 안정적으로 동작하며 사용이 권장됩니다. |
💡 JVM 메모리 옵션의 우선순위
JVM은 힙 메모리 크기를 결정할 때, 일반적으로 절대값 설정을 백분율 기반 설정보다 더 높은 우선순위로 간주합니다.
| 옵션 종류 | 예시 | 우선순위 |
| 절대값 기반 (Explicit Size) | -Xms512m, -Xmx2g | 가장 높음 (Final 결정값) |
| 백분율 기반 (Percentage) | -XX:InitialRAMPercentage=60, -XX:MaxRAMPercentage=80 | 낮음 (절대값이 없을 경우 적용되는 값) |
- Xmx를 설정하면 -XX:MaxRAMPercentage를 덮어씁니다.
- Xms를 설정하면 -XX:InitialRAMPercentage를 덮어씁니다.
3. 메모리 계산 및 OOMKilled 방지
JVM이 사용하는 메모리는 힙(Heap)만 있는 것이 아닙니다.
JVM이 메모리 제한을 초과하여 OOMKilled 되는 것을 막으려면, Non-Heap 영역을 고려해야 합니다.
컨테이너 메모리 총량 = 힙 메모리 + Non-Heap 메모리
| 메모리 영역 | 설명 | 튜닝 고려 사항 |
| Non-Heap 메모리 | Metaspace, 스레드 스택(Thread Stacks), 코드 캐시, 네이티브 메모리 등. | 가장 예측하기 어렵습니다. 스레드 개수가 많거나 JNI를 사용하면 커집니다. |
| 안전 마진 (Safety Buffer) | 힙 및 Non-Heap을 설정하고 남기는 여유 공간. | OOMKilled 방지를 위해 이 공간을 확보하는 것이 핵심입니다. |
🔑 안전 마진 확보의 중요성
MaxRAMPercentage를 100% 미만으로 설정하는 것이 바로 이 안전 마진을 확보하기 위함입니다.
- 권장 범위: 일반적인 Java 애플리케이션의 경우 60% ~ 80% 사이의 값을 사용하고, 나머지 20% ~ 40%를 Non-Heap 영역 및 안전 마진으로 남겨두는 것이 안전합니다.
- 과도한 설정의 위험: MaxRAMPercentage=95와 같이 너무 높게 설정하면, Non-Heap 영역이 메모리 제한을 초과하여 Kubernetes의 OOM Killer에 의해 Pod가 강제 종료될 가능성이 매우 커집니다.
4. Kubernetes Manifest 설정 예시
JVM 옵션을 완벽하게 설정했다면, Kubernetes Manifest (Deployment 또는 Pod)에서는 resources.limits.memory를 명확하게 지정해야 합니다.
# Kubernetes Pod/Deployment Manifest
spec:
containers:
- name: java-app-container
image: your-java-image:latest
resources:
limits:
# 이 값이 JVM의 MaxRAMPercentage 계산 기준이 됩니다!
memory: "4Gi"
requests:
# Limit과 동일하게 설정하는 것을 권장 (안정성 확보)
memory: "4Gi"
env:
- name: JAVA_OPTS
# 4Gi의 75%인 3Gi가 힙 메모리로 할당됩니다.
value: "-XX:InitialRAMPercentage=75 -XX:MaxRAMPercentage=75 ..."
이와 같이 설정하면, 컨테이너 메모리 Limit이 4Gi로 지정되고, JVM은 해당 4Gi 중 75%인 3Gi를 힙 메모리로 고정하여 사용하게 됩니다.
🔥 요약 및 최종 권장 사항:
- JDK 버전 확인: 최소 JDK 8u191 이상, 권장 JDK 11 이상을 사용하세요.
- 힙 고정: -XX:InitialRAMPercentage와 -XX:MaxRAMPercentage를 동일하게 설정하세요.
- 안전 마진: 이 비율을 70%~80% 사이로 설정하여 Non-Heap 영역을 위한 충분한 공간을 남겨주세요.
- K8s 설정: Pod의 memory.limits를 명확하게 지정하고, requests와 limits를 동일하게 설정하여 메모리 자원을 보장하세요.
참고
https://www.baeldung.com/java-jvm-parameters-rampercentage
'DevOps > Kubernetes' 카테고리의 다른 글
| 모놀리식(Monolithic)에서 MSA으로 전환시 고려사항 (2) | 2024.09.05 |
|---|---|
| [OCP] permission denied, mkdir in container on openshift (0) | 2023.12.20 |
| Kubernetes Pod Security Admission - Namespace Level (0) | 2023.10.27 |
| AWS EKS Node 인스턴스 타입의 최대 Pod 개수 (Too many pods 해결 방법) (0) | 2023.08.04 |
| Kubernetes Deprecated API Version Check 방법 (클러스터 버전 업그레이드 사전 작업) (1) | 2023.05.23 |