본문 바로가기
Q&A2026년 4월 17일2분 읽기

Kubernetes Pod가 OOMKilled로 죽는데 kubectl top은 memory usage가 절반밖에 안 나옵니다

서
서버지기영호
조회 418댓글 3

프로덕션 워크로드 중 하나가 간헐적으로 OOMKilled됩니다. 근데 kubectl top pod로 보면 limit 대비 40~50%밖에 안 쓰는 걸로 나와서 원인 파악이 안 돼요.

$ kubectl describe pod my-app-xxxxx
...
Last State:     Terminated
  Reason:       OOMKilled
  Exit Code:    137
Containers:
  app:
    Limits:   memory: 1Gi
    Requests: memory: 512Mi

$ kubectl top pod my-app-xxxxx
NAME            CPU(cores)   MEMORY(bytes)
my-app-xxxxx    120m         480Mi

처음엔 limit을 잘못 계산한 줄 알았는데, 30분 추적해도 kubectl top 기준으론 한 번도 1Gi 근처 안 갑니다. 근데 왜 OOMKilled가 나나요? 클러스터는 cgroup v2 사용 중이고 K8s 1.30입니다.

댓글 3

김김영삼6개월 전

정확히 같은 증상으로 한참 삽질하던 분들이 많은 케이스입니다. kubectl top은 진실이 아닙니다. 이유가 몇 가지 있습니다.


1) kubectl top은 샘플링 기반 (평균값)

metrics-server가 기본 15초 간격으로 cAdvisor에서 평균을 가져옵니다. 순간 스파이크는 거의 확실히 놓칩니다. 반면 cgroup OOM killer는 1ms 단위로 반응합니다.


2) 진짜로 봐야 하는 건 memory.peak

cgroup v2에서 해당 Pod의 실제 최대 메모리 사용량을 보려면 컨테이너 안에서 이 파일을 읽으세요.


kubectl exec -it my-app-xxxxx -- cat /sys/fs/cgroup/memory.peak
# 예: 1073741824 ← 정확히 1Gi, OOM 직전이었던 흔적

이 값이 limit에 근접하면 "샘플링이 스파이크를 놓쳤다"가 확정입니다.


3) Working Set vs RSS vs Cache

OOM killer가 참고하는 값은 working_set = anon + unevictable입니다. kubectl top이 보여주는 것도 working_set이지만 샘플링 때문에 차이가 남. page cache(=file cache)는 OOM 회계에서 제외되지만 튜닝상 영향 줍니다.


4) 모니터링 권장

  • Prometheus에 container_memory_working_set_bytes를 scrape 간격 15초 이하로 (1초 권장)

  • Grafana에 max_over_time(... [1m])로 스파이크 시각화

  • VPA(Vertical Pod Autoscaler)를 recommendation 모드로 먼저 띄워 "실제 p99 메모리"를 관찰


5) 언어별 함정

  • JVM: heap만 신경 쓰고 metaspace/direct buffer/thread stack 누락 → -XX:MaxRAMPercentage=75.0 권장

  • Node.js: --max-old-space-size를 limit의 75% 수준으로 (기본 1.5G가 limit보다 크면 바로 OOM)

  • Python: multiprocessing fork한 워커들의 RSS가 별도 누적 (COW이지만 write가 많으면 실질 증가)

개개발자준호6개월 전

JVM 워크로드면 90% 확률로 heap 외 영역 누적이에요. -Xmx만 보고 limit 맞추면 metaspace + DirectByteBuffer + 스레드 스택(1MB × 스레드 수)에서 터집니다. 저는 jcmd <pid> VM.native_memory summary로 breakdown 보고 튜닝하는 편입니다.

서서버지기영호6개월 전

memory.peak 체크해보니 정말 limit의 97%까지 순간 스파이크가 있었네요. Prometheus scrape 주기도 기본 60초라 못 잡은 거였고요. requests를 768Mi로 올리고 VPA recommendation 모드로 2주 관찰 들어갑니다. JVM 네이티브 메모리 breakdown은 처음 듣는 커맨드라 실무에 바로 써먹겠습니다. 감사합니다!

Ctrl+Enter로 등록