스프링부트 앱을 쿠버네티스에 올렸는데 트래픽이 좀 몰리면 파드가 OOMKilled(137)로 죽습니다. limits.memory는 1Gi로 줬고, 앱에 -Xmx512m 줘서 힙은 절반밖에 안 쓰게 했는데도요. kubectl top으로 보면 죽기 직전에 1Gi를 꽉 채우더라고요.
describe 하면 이렇게 나옵니다.
State: Running
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Limits:
memory: 1Gi힙을 512로 묶었는데 왜 컨테이너 전체 메모리가 1Gi를 넘기는 건지 이해가 안 됩니다. 힙 말고 어디서 새는 걸까요?
댓글 4
권장하는 방식은 -Xmx를 고정하지 말고 -XX:MaxRAMPercentage로 컨테이너 메모리 대비 비율로 잡는 거예요. 자바 11 이상은 cgroup limit을 인식해서 1Gi를 컨테이너 메모리로 봅니다.
-XX:InitialRAMPercentage=50 -XX:MaxRAMPercentage=70
이렇게 하면 힙을 700MB쯤 쓰고 나머지를 비힙 여유로 남겨요. 지금처럼 512 고정에 limit 1Gi면 비힙이 500MB나 있어서 오히려 힙을 덜 쓰고 있는 상태일 수도 있고요. 그리고 kubectl top은 RSS 기준이라 힙 사용량이랑 다르니 참고만 하세요.
네티 다이렉트 메모리는 -Xmx랑 별개로 -XX:MaxDirectMemorySize나 io.netty.maxDirectMemory 시스템 프로퍼티로 상한을 걸 수 있어요. 안 걸면 기본이 -Xmx랑 같은 값이라 최악의 경우 힙만큼 오프힙을 또 쓸 수 있습니다. 그래서 limit만 2Gi로 올리면 당장 안 죽더라도 버퍼가 계속 늘면 또 터져요. NMT로 범인부터 특정하고 상한을 거는 순서를 추천합니다. 트래픽 대비 진짜 부족한 거면 그때 limit도 같이 올리고요.