기본 콘텐츠로 건너뛰기

Kubernetes 노드 리소스 폭주 시 OOM/Kill 원인 분석 및 대응 가이드

Kubernetes 노드 리소스 폭주 시 OOM/Kill 원인 분석 및 대응 가이드

증상 정의 — 노드 리소스 폭주와 OOM/Kill이란 무엇인가

노드 리소스 폭주는 주로 메모리나 디스크(특히 /var/lib/kubelet) 부족 때문에 커널 또는 kubelet이 프로세스를 강제 종료하는 상황을 뜻합니다. 커널의 OOM(Out‑Of‑Memory)과 Kubernetes 차원의 Eviction/Pod Kill은 밀접하게 연결됩니다. 다음은 흔히 관찰되는 주요 증상입니다. 실무 체크리스트(예): ① 메모리·디스크 사용량 확인 ② kubelet의 eviction threshold 및 이벤트 점검 ③ dmesg/커널 로그 확인. Kubernetes 노드 리소스 폭주 시 OOM/Kill 원인 분석에서는 우선 이 항목들을 점검하세요.

  • Pod 상태: kubectl get pod의 containerStatuses에 "OOMKilled"로 표시되거나, Pod 이벤트에 "Evicted" 또는 "OOMKilled"가 기록됨
  • 커널 로그: dmesg나 /var/log/kern.log에 "Out of memory" 메시지, "Killed process <pid> (<name>)" 또는 oom_score 관련 정보가 남음
  • kubelet 이벤트/로그: eviction manager에서 "eviction signal" 또는 "threshold met" 경고가 발생하고, 이벤트에 MemoryPressure나 NodeHasDiskPressure가 표시됨
  • 노드 상태 변화: kubectl describe node에서 MemoryPressure나 DiskPressure 조건이 활성화되고, 컨테이너가 반복적으로 재시작하거나 OOM 재시작 루프가 발생함

데이터 수집과 가설 수립 — 어느 항목을, 어떤 시간 범위로 확인할까

문제가 발생한 시점 전후의 명확한 타임라인을 만들고, 모든 데이터 소스를 동일한 타임스탬프 기준으로 교차검증하세요. 권장 수집 항목과 범위는 다음과 같습니다:

  • 노드 메트릭(메모리/CPU/스왑): 최소 24시간 보관. 사고 전후 최소 ±1시간을 확보하고, 15초~1분 해상도로 스파이크를 확인한다.
  • kubelet/events: 노드 상태, eviction, kubelet OOM 관련 메시지를 포함한 이벤트 타임라인을 수집한다. 사고 전후 ±30~60분을 권장한다.
  • pod/container 로그: 해당 파드의 stdout/stderr와 애플리케이션 로그를 확보한다. 사고 전후 약 ±30분을 수집하고, 타임스탬프를 정렬해 분석한다.
  • dmesg/journal: 커널 OOM, oom_kill_proc, cgroup 관련 메시지를 확인한다. 특히 사고 직전 수분과 사고 직후의 기록을 우선 확보한다.

가설 작성법: 타임라인에서 메모리·CPU 급증 시점과 OOM 이벤트의 상대적 순서를 명확히 적는다(예: "메모리 스파이크 → cgroup 한계 도달 → kubelet eviction" 또는 "커널 OOM 선행"). 각 가설마다 재현 가능성, 증거(메트릭 지표·이벤트 로그·dmesg 라인)와 추가 수집 항목을 적어 우선순위로 검증한다. 실무 체크리스트 예: 문제 발생 시점의 컨테이너 식별 → 해당 시각 노드 메트릭 및 dmesg 캡처 → kubelet 이벤트와 eviction 기록 확인. Kubernetes 노드 리소스 폭주 시 OOM/Kill 원인 분석을 진행할 때 이 절차가 도움이 된다.

커널·cgroup 관점에서 원인 분석 — OOM killer와 cgroup 한계 이해

프로세스 단위의 oom_score/oom_adj(또는 oom_score_adj)는 커널이 시스템 전역 OOM 상황에서 어떤 PID를 우선적으로 선택할지 가중치를 설정한다. oom_score_adj의 범위는 -1000에서 +1000이며 기존 oom_adj 값과 호환된다. 다만 이 값은 주로 시스템 전체 OOM 결정에 영향을 주며, cgroup 내부 한계로 인한 동작과는 별개다.

  • cgroup v1: memory.limit_in_bytes(하드 한계)를 초과하면 해당 cgroup 내에서 할당이 실패한다. 정책에 따라 프로세스를 종료할 수 있다. swap 포함 제어도 가능하다(memory.memsw.limit).
  • cgroup v2: memory.max(하드), memory.high(소프트·스로틀), memory.swap.max 등으로 보다 세분화되어 있다. memory.high는 reclaim을 유도해 스로틀링을 발생시키고, memory.max를 초과해 reclaim이 실패하면 그 cgroup 단위로 OOM 처리를 한다.
  • 차이 요점: 커널 OOM은 시스템 전체 메모리 고갈 시 oom_score를 기준으로 프로세스를 선택해 종료한다. 반면 cgroup 한계는 먼저 그 그룹 내부에서 할당 실패와 reclaim을 시도하고, 필요하면 그 cgroup 내 프로세스를 대상으로 죽인다. 따라서 노드 리소스 폭주 원인을 분석할 때는 전역 정책과 cgroup별 메모리 정책, 그리고 관련 프로세스의 oom_score를 함께 살펴야 한다. 체크리스트: 전역 메모리 사용량 확인, 해당 cgroup의 limits 및 memory.high 설정 검토, 영향을 받은 프로세스의 oom_score 확인. 이 접근법은 Kubernetes 노드 리소스 폭주 시 OOM/Kill 원인 분석에 유용하다.

Kubelet·Eviction·QoS의 상호작용 — 쿠버네티스에서 리소스 부족을 다루는 방법

kubelet은 노드의 메모리, CPU, ephemeral-storage, inode 등 상태를 지속해서 관찰합니다. 사전 정의된 eviction thresholds(예: memory.available, nodefs.available, imagefs.available)를 기준으로 soft 또는 hard eviction을 트리거합니다. hard eviction은 즉시 실행되고, soft eviction은 설정된 grace period가 지나면 적용됩니다. 이 과정은 Kubernetes 노드 리소스 폭주 시 OOM/Kill 원인 분석에 중요한 단서를 제공합니다.

  • QoS 우선순위: BestEffort → Burstable → Guaranteed. requests와 limits를 모두 지정하지 않은 BestEffort 파드는 가장 먼저 eviction 대상이 되며, requests와 limits가 동일한 Guaranteed 파드는 가장 강하게 보호됩니다.
  • requests/limits 영향: requests는 스케줄링과 QoS 분류 기준으로 사용됩니다. limits(특히 메모리)는 cgroup을 통해 강제되므로, 컨테이너가 메모리 limit을 초과하면 커널 OOM으로 즉시 종료될 수 있습니다. 실무 체크리스트: requests는 평균 사용량을 반영하고, limits는 피크를 고려해 여유를 두고 설정하세요.
  • PodPriority: 동일한 QoS 등급에서는 priority가 높은 파드가 우선 보호됩니다. 따라서 중요한 시스템 파드에는 적절히 높은 priority나 critical 값을 지정해 두세요.

실전 진단 도구와 쿼리·체크리스트 — 신속한 원인 파악 방법

빠르게 원인을 추적하기 위한 플레이북 형태의 체크리스트입니다. 각 단계에서 실행할 명령과 쿼리, 기대 결과(다음 조치)를 명확히 제시합니다. 특히 Kubernetes 노드 리소스 폭주 시 OOM/Kill 원인 분석에 유용합니다.

  1. 노드 커널 로그 확인: dmesg | grep -i oom — OOM killer 로그가 있으면 메모리 초과로 판단합니다. 타임스탬프와 PID, 실행 명령을 확인해 해당 프로세스가 어떤 컨테이너와 연관되는지 조사하세요.
  2. 쿠버네티스 이벤트/상태: kubectl describe/events, kubectl describe node POD_NODE, kubectl get pod -o wide — Evicted, CrashLoop, OOMKilled 표기를 확인해 영향 범위를 노드 단위인지 파드 단위인지 구분합니다.
  3. 컨테이너 런타임 검사: crictl ps -a, crictl logs POD, ctr containers list — 비정상 종료한 컨테이너의 로그와 리소스 사용 정보를 확보해 원인 단서를 찾습니다.
  4. 메트릭(프롬etheus) 조회: 예) 노드 메모리 사용량: sum(container_memory_usage_bytes) by (node); OOMKilled 빈도: increase(kube_pod_container_status_restarts_total[1h]); Evictions: count(kube_pod_status_phase{phase="Failed",reason="Evicted"}) — 지표의 트렌드를 보고 반복적인 급증인지 단발성 스파이크인지 구분해 메모리 누수인지 일시적 과부하인지 판단합니다.
  5. 즉각 대응 체크리스트: 리소스 리미트와 리퀘스트를 검증하고 QoS 클래스를 확인합니다. 문제가 집중된 노드는 임시로 cordon/drain한 뒤 로그와 메트릭을 바탕으로 루트케이스(메모리 누수, 배치 잡 등)를 조사해 조치하세요. 실무 체크리스트(예): 노드 cordon → 중요 파드 로그·메트릭 스냅샷 확보 → 원인 판별 후 drain 또는 스케일 조정.

근본 원인 해결과 예방책 — 설정·아키텍처·모니터링 관점

실무에서는 요청·제한과 클러스터 정책으로 먼저 예방하고, 오토스케일링과 모니터링으로 발생 시 신속히 대응하는 것이 핵심입니다: Pod에는 적절한 requestslimits를 설정하고, 네임스페이스별로 LimitRangeResourceQuota를 적용하세요. VPA는 메모리·CPU 권장값을 자동화하고, HPA는 트래픽 기반의 수평 확장에 활용합니다.

  • 노드: kube-reserved, system-reserved 설정과 eviction-thresholds로 allocatable 여유를 확보하고, taint·label을 이용해 워크로드를 분리합니다.
  • 오토스케일링: 노드풀별 용량 계획과 Cluster Autoscaler의 스케일 업·다운 임계값을 조정해 급격한 수요 증가에 대비합니다.
  • 모니터링/알림: 노드 메모리 사용률, Pod OOMKilled, swap 및 eviction 이벤트를 경보로 설정하고 우선순위 기반의 런북을 준비합니다. 예: 확인 → 로그·프로파일링 → 코드 수정 또는 임시 리소스 제한 적용 → 노드 cordon → drain → 스케일. 간단 체크리스트: (1) 최근 배포 유무 확인 (2) 리소스 요청/제한 재검토 (3) 긴급 스케일 정책 실행. 실무 사례로는 Kubernetes 노드 리소스 폭주 시 OOM/Kill 원인 분석을 위한 런북을 미리 준비해 두는 것이 큰 도움이 됩니다.

경험에서 배운 점

노드의 메모리·CPU 폭주로 인한 OOM(Kill)은 애플리케이션의 메모리 누수나 일시적인 배치성 버스트뿐 아니라, 잘못된 리소스 설정이나 노드 측 예약(reservation)과 eviction 정책 부재에서 비롯되는 경우가 많습니다. 조사할 때는 먼저 kubelet 이벤트(kubectl describe node), kubelet 로그, 커널 로그(dmesg/syslog)에서 OOM killer 관련 메시지를 확인하세요. Prometheus나 node-exporter 같은 메트릭으로 메모리 사용 추세(주기적 상승인지 급격한 스파이크인지)를 파악하면 원인 규명이 훨씬 빨라집니다. 컨테이너가 OOMKilled 되었을 때는 해당 컨테이너 로그만 보는 것으로는 부족하므로, 같은 시점의 노드 전체 메모리 상태와 cgroup 사용량을 함께 확인해야 합니다. (참고: Kubernetes 노드 리소스 폭주 시 OOM/Kill 원인 분석을 진행할 때 이 흐름을 권장합니다.)

실무에서 흔히 보는 실수는 요청(requests) 없이 제한(limits)만 설정하거나 그 반대로 설정하는 것, kube-reserved/system-reserved/eviction 값을 설정하지 않아 시스템 프로세스가 쫓겨나는 것, 그리고 알람이나 지표가 OOM 발생 전후를 포착하지 못해 사후 대응에 그치는 경우입니다. 재발 방지를 위해서는 LimitRange/ResourceQuota로 requests·limits의 기본 정책을 강제하고, 노드 예약과 엄격한 eviction 정책으로 시스템 안정성을 확보해야 합니다. 더불어 OOMKill과 메모리 압력에 대한 알림을 설정하고 dmesg·kubelet 로그·스냅샷을 자동으로 수집하는 루틴을 마련해 원인 분석 시간을 단축하세요.

  • 즉시 점검 항목: kubectl describe node, kubectl get events, dmesg/syslog, kubelet 로그, 해당 파드의 컨테이너 로그 및 재시작 횟수 확인. 체크리스트 예: 메모리 사용량 추세, cgroup RSS/limit, 재시작 카운트, OOM killer 로그 유무.
  • 정책 적용: 모든 네임스페이스에 LimitRange로 기본 requests/limits를 강제하고, 네임스페이스별 ResourceQuota로 과도한 자원 사용을 제한합니다.
  • 노드 설정: kubelet의 --kube-reserved/--system-reserved 값을 명시하고 evictionHard(및 필요 시 evictionSoft) 정책을 설정해 시스템 프로세스 보호와 안정성을 확보하세요.
  • 관측성: 노드·컨테이너 메모리 시계열(예: Prometheus)과 OOMKill 이벤트를 연계한 알람을 구성합니다. 예: OOMKilled 카운트, 노드 사용 가능한 메모리 임계치 알림.
  • 테스트 및 검증: 스테이징에서 메모리 압박 시나리오(부하 테스트, 메모리 누수 시뮬레이션)를 통해 정책과 알람이 의도한 대로 동작하는지 검증합니다.
  • 자동화 대응: 재발 시 자동으로 로그·스냅샷을 수집하고, 노드 격리·교체 절차를 자동화하며 Runbook을 문서화해 대응 시간을 단축합니다.
  • 아키텍처적 완화: 긴 작업은 배치 큐로 분리하고 상태 저장 워크로드는 자원 보장(Guaranteed QoS)을 적용해 안정성을 높이세요.

댓글

이 블로그의 인기 게시물

Java Servlet Request Parameter 완전 정복 — GET/POST 모든 파라미터 확인 & 디버깅 예제 (Request Parameter 전체보기)

Java Servlet Request Parameter 완전 정복 — GET/POST 모든 파라미터 확인 & 디버깅 예제 Java Servlet Request Parameter 완전 정복 웹 애플리케이션에서 클라이언트로부터 전달되는 Request Parameter 를 확인하는 것은 필수입니다. 이 글에서는 Java Servlet 과 JSP 에서 GET/POST 요청 파라미터를 전체 출력하고 디버깅하는 방법을 다양한 예제와 함께 소개합니다. 1. 기본 예제: getParameterNames() 사용 Enumeration<String> params = request.getParameterNames(); System.out.println("----------------------------"); while (params.hasMoreElements()){ String name = params.nextElement(); System.out.println(name + " : " + request.getParameter(name)); } System.out.println("----------------------------"); 위 코드는 요청에 포함된 모든 파라미터 이름과 값을 출력하는 기본 방법입니다. 2. HTML Form과 연동 예제 <form action="CheckParamsServlet" method="post"> 이름: <input type="text" name="username"><br> 이메일: <input type="email" name="email"><b...

PostgreSQL 달력(일별,월별)

SQL 팁: GENERATE_SERIES로 일별, 월별 날짜 목록 만들기 SQL 팁: GENERATE_SERIES 로 일별, 월별 날짜 목록 만들기 데이터베이스에서 통계 리포트를 작성하거나 비어있는 날짜 데이터를 채워야 할 때, 특정 기간의 날짜 목록이 필요할 수 있습니다. PostgreSQL과 같은 데이터베이스에서는 GENERATE_SERIES 함수를 사용하여 이 작업을 매우 간단하게 처리할 수 있습니다. 1. 🗓️ 일별 날짜 목록 생성하기 2020년 1월 1일부터 12월 31일까지의 모든 날짜를 '1 day' 간격으로 생성하는 쿼리입니다. WITH date_series AS ( SELECT DATE(GENERATE_SERIES( TO_DATE('2020-01-01', 'YYYY-MM-DD'), TO_DATE('2020-12-31', 'YYYY-MM-DD'), '1 day' )) AS DATE ) SELECT DATE FROM date_series 이 쿼리는 WITH 절(CTE)을 사용하여 date_series 라는 임시 테이블을 만들고, GENERATE_SERIES 함수로 날짜를 채웁니다. 결과 (일별 출력) 2. 📅 월별 날짜 목록 생성하기 동일한 원리로, 간격을 '1 MONTH' 로 변경하면 월별 목록을 생성할 수 있습니다. TO...

CSS로 레이어 팝업 화면 가운데 정렬하는 방법 (top·left·transform 완전 정리)

레이어 팝업 센터 정렬, 이 코드만 알면 끝 (CSS 예제 포함) 이벤트 배너나 공지사항을 띄울 때 레이어 팝업(center 정렬) 을 깔끔하게 잡는 게 생각보다 어렵습니다. 화면 크기가 변해도 가운데에 고정되고, 모바일에서도 자연스럽게 보이게 하려면 position , top , left , transform 을 정확하게 이해해야 합니다. 이 글에서는 아래 내용을 예제로 정리합니다. 레이어 팝업(center 정렬)의 기본 개념 자주 사용하는 position: absolute / fixed 정렬 방식 질문에서 주신 스타일 top: 3.25%; left: 50%; transform: translateX(-50%) 의 의미 실무에서 바로 쓰는 반응형 레이어 팝업 HTML/CSS 예제 1. 레이어 팝업(center 정렬)이란? 레이어 팝업(레이어 팝업창) 은 새 창을 띄우는 것이 아니라, 현재 페이지 위에 div 레이어를 띄워서 공지사항, 광고, 이벤트 등을 보여주는 방식을 말합니다. 검색엔진(SEO) 입장에서도 같은 페이지 안에 HTML이 존재 하기 때문에 팝업 안의 텍스트도 정상적으로 인덱싱될 수 있습니다. 즉, “레이어 팝업 센터 정렬”, “레이어 팝업 만드는 방법”과 같이 관련 키워드를 적절히 넣어주면 검색 노출에 도움이 됩니다. 2. 질문에서 주신 레이어 팝업 스타일 분석 질문에서 주신 스타일은 다음과 같습니다. <div class="layer-popup" style="width:1210px; z-index:9001; position:absolute; top:3.25%; left:50%; transform:translateX(-50%);"> 레이어 팝업 내용 <...