# Kubernetes OOMKilled: 내 파드가 자꾸 죽는 진짜 이유 (Exit Code 137)
Table of Contents
“메모리를 2배로 늘렸는데 또 죽었어요”
쿠버네티스를 운영하다 보면 가장 흔하게, 그리고 가장 골치 아프게 마주치는 에러가 바로 **OOMKilled (Exit Code 137)**입니다.
개발자의 첫 반응은 보통 limits.memory를 늘리는 것입니다. 1GB에서 2GB로, 2GB에서 4GB로 말이죠.
하지만 며칠 뒤, 그 파드(Pod)는 더 비대해진 몸집으로 또다시 죽습니다.
왜 그럴까요? 단순히 메모리가 부족해서가 아니라, 메모리가 새고 있거나(Leak), 잘못된 설정 때문일 확률이 높습니다. 밑 빠진 독에 물 붓기를 멈추고, 이제 독을 수리해야 할 때입니다.
OOMKilled의 정체: 범인은 커널(Kernel)이다
파드가 OOMKilled 되었다는 건, 쿠버네티스가 임의로 죽인 것이 아닙니다.
정확히는 리눅스 커널의 OOM Killer가 “이 컨테이너가 할당된 메모리 한계(cgroup limit)를 넘었네? 시스템 전체를 살리기 위해 널 죽여야겠다.” 하고 SIGKILL (9) 시그널을 보낸 것입니다.
Exit Code 137은 128 + 9 (SIGKILL)을 의미합니다. 즉, 외부 요인에 의해 강제 종료당했다는 뜻입니다.
왜 죽었을까? 3가지 유력한 용의자
-
진짜 메모리 누수 (Memory Leak): 코드 어딘가에서 객체를 계속 생성하고 해제하지 않는 경우입니다. 전역 변수에 데이터를 계속 쌓거나, 닫히지 않은 고루틴/스레드가 누적되는 경우가 흔합니다. 이 경우 리미트를 아무리 늘려도 결국엔 죽습니다.
-
순간적인 트래픽 폭주 (Spike): 평소엔 얌전하다가, 특정 API 호출 시 대용량 데이터를 메모리에 한 번에 로딩하는 경우입니다. 예를 들어, 100만 건의 엑셀 데이터를 스트리밍하지 않고 한 번에 읽어서 처리하려고 하면 순간적으로 메모리 사용량이 치솟아 죽게 됩니다.
-
잘못된 JVM/Runtime 설정: Java 앱의 경우, 컨테이너 메모리는 2GB인데 힙(Heap) 메모리를 2GB로 설정하면 어떻게 될까요? JVM 자체 오버헤드(Metaspace, Stack, Native Memory) 때문에 전체 프로세스 사용량은 2GB를 넘게 되고, 결국 OOMKilled 당합니다. (컨테이너 메모리 > 힙 메모리 + 오버헤드여야 합니다)
디버깅 가이드: 범인을 잡자
1. kubectl describe pod 확인하기
가장 먼저 해야 할 일입니다. 파드의 상태를 확인하세요.
kubectl describe pod <pod-name>
State: Terminated, Reason: OOMKilled가 찍혀 있다면 메모리 부족이 확실합니다.
2. 모니터링 그래프 보기 (Prometheus/Grafana)
메모리 사용량 그래프의 기울기를 유심히 살펴보세요.
- 계단식으로 꾸준히 우상향: 100% 메모리 누수입니다. 배포 직후부터 서서히 차오르다가 한계선에 도달하면 죽습니다.
- 갑자기 수직 상승: 트래픽 스파이크나 대용량 데이터 처리 로직 문제입니다. 로그와 대조해서 그 시간에 호출된 API를 찾아야 합니다.
3. 언어별 프로파일링 (Profiling)
메모리 누수가 의심된다면 코드를 들여다봐야 합니다.
- Go:
pprof를 사용하여 힙 프로파일(Heap Profile)을 분석합니다. 어떤 함수가 메모리를 점유 중인지 나옵니다. - Java:
Heap Dump를 생성하여 Eclipse MAT 같은 도구로 분석합니다. - Node.js: Chrome DevTools의 Memory 탭이나
heapdump라이브러리를 사용합니다.
해결책: 독을 수리하는 법
- 메모리 누수 고치기: 프로파일링 결과로 찾은 누수 지점을 수정합니다. 불필요한 객체 참조를 끊거나, 캐시 만료(TTL) 설정을 추가합니다.
- 데이터 처리 방식 변경: 대용량 데이터는 한 번에 로딩하지 말고, **스트림(Stream)**이나 페이지네이션(Pagination) 방식으로 나누어 처리하도록 로직을 변경합니다.
- 적절한 Limit 설정: 애플리케이션의 평소 사용량(Request)과 최대 사용량(Limit)을 모니터링 데이터를 기반으로 재설정합니다. 일반적으로
limits를requests의 1.2~1.5배 정도로 설정하는 것이 안전합니다. - HPA (Horizontal Pod Autoscaler) 적용: 메모리 사용량이 70~80%에 도달하면 파드 개수를 늘려서 부하를 분산시키도록 설정합니다.
마치며
OOMKilled는 시스템이 보내는 구조신호입니다. “메모리 더 줘!”라고 떼쓰는 게 아니라, “나 지금 뭔가 잘못됐어, 빨리 고쳐줘!”라고 외치는 것입니다. 이 신호를 무시하고 리미트만 늘리는 건, 시한폭탄의 타이머를 조금 늦추는 미봉책일 뿐입니다.
오늘 밤, 여러분의 파드가 안녕한지 모니터링 대시보드를 한 번 확인해 보세요. 우상향 하는 그래프가 보인다면, 바로 지금이 디버깅을 시작할 때입니다.