Pod 가 OOMKilled 라면 Kubernetes 는 컨테이너가 memory limit 에 걸려 커널에 의해 종료됐다고 알려 주는 것입니다. 여기서 중요한 질문은 단순히 “limit 을 올릴까?” 가 아닙니다. 실제로 leak 또는 retention 이 있는지, bursty memory behavior 인지, 아니면 request 와 limit sizing 이 현실 운영에 맞지 않는지 구분해야 합니다.
짧게 말하면 핵심은 이것입니다. usage pattern 과 limit 을 같이 봐야 합니다. 실제로 운영 중이던 Java 서비스에서 OOMKilled가 주기적으로 발생했는데, JVM heap은 limit의 70%로 잡혀 있었습니다. 문제는 off-heap 메모리(Netty direct buffer)가 나머지 30%를 초과한 것이었습니다. limit만 올렸으면 같은 문제가 더 큰 숫자에서 또 터졌을 겁니다. limit 하나를 올린다고 leak 이 해결되지는 않고, 너무 낮은 limit 은 멀쩡한 workload 를 비정상처럼 보이게 만들 수 있습니다.
usage pattern 과 limit 을 같이 본다
메모리 incident 는 단순화하기 쉽습니다.
실제로는 아래를 같이 봐야 합니다.
- 실제 memory usage
- steady growth 인지 short spike 인지
- requests 와 limits
- restart timing
이걸 같이 보지 않으면 undersizing 인지, retention 인지, burst pressure 인지 구분하기 어렵습니다.
OOMKilled 는 보통 무엇을 뜻하나
실전에선 보통 아래 중 하나입니다.
- memory limit 이 너무 낮다
- 애플리케이션이 메모리를 너무 오래 붙잡는다
- classic leak 이 아니어도 짧은 spike 가 limit 을 넘는다
- request / limit sizing 이 동작을 헷갈리게 만든다
restart reason 은 명확해도, fix path 는 usage shape 에 따라 달라집니다.
흔한 원인
1. memory limit 이 너무 낮다
workload 자체는 정상인데 configured limit 이 현실적인 peak usage 보다 낮을 수 있습니다.
특히 이런 뒤 자주 보입니다.
- traffic 증가
- payload 증가
- 동시 작업 증가
2. 애플리케이션이 메모리를 누수하거나 오래 붙잡는다
memory 가 꾸준히 올라가다가 결국 limit 을 치고 죽을 수 있습니다.
이게 항상 고전적인 forever leak 은 아닙니다. unbounded cache, queue, 큰 retained graph 도 운영상으로는 같은 결과를 만들 수 있습니다.
3. bursty workload 가 짧은 spike 를 만든다
leak 이 아니어도 cache, request, parsing, batch work 가 짧은 spike 로 limit 을 넘길 수 있습니다.
평균 usage 만 보면 이런 incident 는 랜덤해 보이기 쉽습니다.
4. requests 와 limits 가 어긋나 있다
request sizing 이 나쁘면 scheduling 과 runtime behavior 가 함께 해석하기 어려워집니다.
배치될 때의 가정과, 실제 runtime limit 이 너무 다르게 느껴질 수 있습니다.
5. 진짜 문제는 다른 데서 시작됐다
queue backlog, retry, downstream slowdown 이 pod 내부 retention 을 간접적으로 키울 수도 있습니다.
그래서 OOM incident 는 memory setting 만의 문제가 아니라 workload behavior 와도 자주 연결됩니다.
운영에서 겪은 케이스 중 하나는, 새벽에 Kafka consumer가 밀린 메시지를 한꺼번에 처리하면서 pod 3개가 동시에 OOMKilled된 경우였습니다. consumer의 max.poll.records를 줄이고, prefetch 설정을 조정해서 메모리 spike를 분산시킨 뒤에 안정됐습니다.
실전 점검 순서
1. pod 가 정말 memory pressure 로 죽었는지 확인한다
pod 가 불안정하다고 해서 모든 restart 가 OOM 인 것은 아닙니다.
restart reason 이 실제로 memory kill 인지 먼저 확인해야 합니다.
2. memory usage trend 와 configured limit 을 비교한다
알고 싶은 것은 이렇습니다.
- memory 가 꾸준히 오르는가
- 급격히 spike 하는가
- restart 직전 limit 에 얼마나 가까워지는가
3. steady growth 와 short spike 를 구분한다
이 구분이 매우 중요합니다.
steady growth 는 retention 또는 leak 성향을 시사합니다.
short spike 는 burst workload 나 너무 빡빡한 limit 을 시사합니다.
4. workload 또는 dependency 변화가 메모리 동작을 바꿨는지 본다
최근 아래 변화가 설명이 되는 경우가 많습니다.
- request size
- traffic level
- concurrency
- response aggregation
- retry
5. usage pattern 이 분명해진 뒤에만 requests / limits 를 바꾼다
메모리를 더 주면 시간을 벌 수는 있지만, retention 이 진짜 문제라면 진단을 대신해선 안 됩니다.
빠른 명령
kubectl describe pod <pod> -n <ns>
kubectl top pod <pod> -n <ns>
kubectl get pod <pod> -n <ns> -o yaml
restart reason, 실제 memory usage, requests / limits 를 한 번에 비교하기 좋은 기본 조합입니다.
memory spike 가 limit 근처에서 반복되는지, OOM kill event 가 이어지는지, requests / limits 가 workload 에 비해 어색하지 않은지 보세요.
메모리 패턴을 찾은 뒤 무엇을 바꾸면 좋나
limit 이 그냥 너무 낮다면
관측된 peak 에 맞춰 의도적으로 올려야 합니다.
retention 이 문제라면
더 큰 limit 에 기대기보다 cache, queue, object lifetime 동작을 먼저 고쳐야 합니다.
spike 가 문제라면
workload 를 더 부드럽게 만들거나 burst 에 맞는 limit 을 잡아야 합니다.
requests 가 misleading 하다면
scheduling 과 runtime behavior 가 같이 말이 되도록 workload 에 맞게 조정해야 합니다.
backlog 로 시작한 incident 라면
pod 메모리 ceiling 뿐 아니라 backlog 원인도 함께 고쳐야 합니다.
장애 중에 던져볼 질문
이 질문이 꽤 유용합니다.
memory 가 계속 자라다가 죽은 건가, 아니면 정상 운영에서도 나올 수 있는 burst 가 잠깐 limit 을 넘긴 건가?
이 구분이 fix 방향을 거의 결정해 줍니다.
FAQ
Q. 그냥 limit 을 먼저 올리면 되나
undersizing, burst usage, retention 중 무엇인지 보기 전에는 권하지 않습니다.
Q. 가장 빠른 첫 단계는 무엇인가
OOM kill 여부를 확인하고, 실제 memory usage 와 current limit 을 비교하는 것입니다.
Q. 모든 OOMKilled 가 memory leak 인가
아닙니다. spike, backlog, limit undersizing 도 매우 흔합니다.
Q. requests 도 중요한가, limits 만 중요하지 않나
requests 는 placement 와 cluster behavior 에 영향을 주고, limits 는 kill threshold 를 만든다는 점에서 둘 다 중요합니다.
Read Next
- restart 증상이 memory pressure 보다 더 일반적이라면 Kubernetes CrashLoopBackOff 와 비교해 보세요.
Related Posts
Sources:
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis, RabbitMQ, Kafka 중 어디부터 볼까 Redis, RabbitMQ, Kafka가 함께 있는 시스템에서 지금 보이는 장애가 어느 계층에 더 가까운지, 첫 10분 안에 무엇을 확인하고 어떤 글로 들어가야 하는지 정리한 실전 허브 가이드입니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Astro 기술 블로그 SEO 체크리스트: 트래픽 기다리기 전에 먼저 고칠 것 Astro 기술 블로그를 위한 실전 SEO 체크리스트입니다. 배포 호스트 확인, robots.txt, sitemap, canonical, hreflang, 구조화 데이터, 페이지별 메타데이터, noindex 판단, 검증 명령까지 우선순위대로 정리합니다.
- 다국어 블로그 canonical과 hreflang 설정 가이드: 무엇을 확인하고 어디서 깨질까 다국어 블로그에서 canonical과 hreflang을 어떻게 설정해야 하는지 실전 기준으로 정리합니다. self-canonical, 상호 연결되는 hreflang 묶음, x-default, 카테고리 페이지, 최종 렌더 HTML 점검, 한 언어 버전이 다른 언어 버전을 눌러버리는 실수까지 다룹니다.
- OpenAI Codex CLI 설치 가이드: 설치, 인증, 첫 작업까지 OpenAI Codex CLI를 실전 기준으로 설치하는 방법을 정리했다. 설치, 로그인, 첫 실행, Windows 주의점, 첫 작업을 어떻게 시작하면 좋은지까지 다룬다.