본문으로 건너뛰기
개발 머꼬
개발 노트인프라·운영
hohyeon.dev23

컨테이너가 랜덤하게 재시작됐는데 알고 보니 OOMKilled였던 경우

  • #DevOps
  • #Engineering Note
  • #Kubernetes

문제 발생

애플리케이션이 특정 패턴 없이 가끔 재시작되는 문제가 있었는데, 애플리케이션 자체 로그에는 에러나 크래시 흔적이 전혀 없었습니다.

원인 분석

애플리케이션 로그가 깨끗한데 컨테이너가 재시작된다면, 애플리케이션이 스스로 죽은 게 아니라 외부(커널/오케스트레이터)에 의해 강제 종료됐을 가능성을 의심해야 합니다. 대표적인 원인이 OOMKilled입니다 — 컨테이너가 설정된 메모리 제한(resources.limits.memory)을 초과하면, 리눅스 커널의 OOM(Out Of Memory) killer가 그 프로세스를 즉시 강제 종료합니다. 이 종료는 애플리케이션에게 정상적으로 로그를 남기거나 정리할 기회조차 주지 않습니다 — SIGKILL과 유사하게 즉시 종료되기 때문에 애플리케이션 로그가 깨끗한 것입니다.

Kubernetes 환경이라면 kubectl describe pod <pod-name>으로 확인했을 때 Last StateTerminated, Reason: OOMKilled로 표시되는 것이 명확한 증거입니다.

해결 방안

  1. 먼저 실제 메모리 사용 패턴을 확인합니다. kubectl top pod나 모니터링 도구(Prometheus + Grafana 등)로 컨테이너가 실제로 얼마나 메모리를 쓰는지, 특정 시점에 급증하는지 확인합니다.
  2. 일시적인 스파이크라면 limits.memory를 실제 필요량에 맞게 늘립니다 — 너무 빠듯하게 잡은 제한이 정상적인 부하에서도 OOMKilled를 유발할 수 있습니다.
resources:
  requests:
    memory: "256Mi"
  limits:
    memory: "512Mi"   # 실제 사용 패턴에 맞춰 조정
  1. 메모리 사용량이 시간이 지날수록 계속 늘어나는 패턴이라면 메모리 누수를 의심해야 합니다 — 이 경우 제한을 늘리는 것은 문제를 늦추는 것일 뿐 근본 해결이 아닙니다. 힙 덤프나 프로파일러로 실제 누수 지점을 찾아야 합니다.
  2. requestslimits의 차이도 이해해야 합니다 — requests는 스케줄링 시 "이 정도는 보장해달라"는 값이고, limits는 "이 이상은 절대 못 쓴다"는 강제 상한입니다. 둘을 너무 다르게 잡으면(예: request는 작게, limit은 크게) 노드에 예상보다 많은 파드가 몰려 스케줄링될 수 있고, 실제 부하가 몰리는 시점에 여러 파드가 동시에 메모리 경합을 벌이는 상황이 생길 수 있습니다.
  3. JVM처럼 자체적으로 힙 크기를 관리하는 런타임이라면, 컨테이너 메모리 제한과 JVM 힙 설정(-Xmx 등)을 서로 일치시켜야 합니다 — JVM이 컨테이너 제한을 인지하지 못하고 그보다 큰 힙을 쓰려고 하면 오히려 더 빨리 OOMKilled됩니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.