kubectl describe pod에서 Last State: Terminated, Reason: OOMKilled, Exit Code: 137을 봤다면, 컨테이너가 메모리 한도를 넘어 커널 OOM killer에 의해 강제 종료된 것입니다. 두 가지 경우를 구분해야 합니다: 내 컨테이너가 자기 limit을 넘었나, 아니면 노드 전체가 부족했나.
requests vs limits — 먼저 이해
| 설정 | 의미 |
|---|---|
requests.memory | 스케줄링 보장치(이만큼은 확보) |
limits.memory | 상한(넘으면 OOMKilled) |
OOMKilled는 보통 컨테이너가 limits.memory를 초과했을 때 발생합니다. limit이 없으면 노드 메모리가 부족할 때 죽을 수 있습니다.
1단계 — 어느 쪽인지 확인
kubectl describe pod <pod> # Reason: OOMKilled, 컨테이너별 Last State
kubectl get events --field-selector reason=OOMKilling
컨테이너의 Last State가 OOMKilled면 그 컨테이너의 limit 문제. 노드 레벨 이벤트(The node was low on resource: memory)면 노드 압박.
2단계 — 실제 사용량 측정
추측으로 limit을 올리지 말고 실제 사용량을 봅니다.
kubectl top pod <pod> --containers # metrics-server 필요
피크 사용량을 알아야 limit을 합리적으로 잡습니다.
해결
① limit이 너무 낮음 — 실제 피크 + 여유(20~30%)로 limits.memory 상향.
② 메모리 누수 — limit을 올려도 시간이 지나면 또 죽음. 앱의 누수를 잡아야 함(힙 덤프·프로파일링). 임시로 limit 상향은 미봉책.
③ JVM 등 런타임 인식 문제 — 컨테이너 limit을 모르고 호스트 메모리 기준으로 힙을 잡는 경우. 최신 런타임은 cgroup 인식하지만, 옛 설정은 -XX:MaxRAMPercentage 등으로 명시.
④ requests만 있고 limit 없음 — 노드 압박 시 우선 종료 대상. 적절한 limit을 설정.
체크리스트
kubectl describe pod <pod> # OOMKilled가 컨테이너인지 노드인지
kubectl top pod <pod> --containers # 실제 사용량
kubectl get pod <pod> -o jsonpath='{.spec.containers[*].resources}' # 현재 requests/limits
requests/limits를 직접 설정하고 OOMKilled를 재현·해결하는 실습은 쿠버네티스 트랙에서 무료로 할 수 있습니다.