기본 콘텐츠로 건너뛰기

Kubernetes 노드 OOMKill 연쇄 장애 원인 분석

Kubernetes 노드 OOMKill 연쇄 장애 원인 분석

AI 생성 이미지: Kubernetes 노드 OOMKill 연쇄 장애 원인 분석
AI 생성 이미지: Kubernetes 노드 OOMKill 연쇄 장애 원인 분석

문제 정의 — 노드 OOMKill 연쇄 장애란 무엇인가

노드 OOMKill 연쇄 장애는 커널의 OOM(Out-Of-Memory) 킬러가 한 노드에서 여러 프로세스(컨테이너)를 연달아 종료하면서 확산되는 장애를 말한다. 초기 징후로는 컨테이너 상태가 OOMKilled로 표시되고, kubelet의 메모리 압박(eviction) 로그, 노드 상태 NotReady, 스케줄러 재스케줄링 증가 등이 관찰된다. 원인 분석 시에는 컨테이너와 호스트 수준의 메모리 지표를 함께 살펴야 한다. 이 글은 Kubernetes 노드 OOMKill 연쇄 장애 원인 분석에 실무적으로 접근하는 방법을 제공한다.

  • 영향: 서비스 불능, 상태 저장 워크로드의 데이터 손실, 반복적인 리더 선출, SLO 위반과 갑작스러운 용량 감소.
  • 발생 빈도: 자주 발생하지는 않지만 메모리 과대예약, 메모리 누수, 배치 작업의 동시 실행, 노드 업그레이드 등으로 증폭되어 엔터프라이즈 환경에서는 비교적 자주 재현될 수 있다.
  • 엔터프라이즈 위험성: 멀티테넌시 환경에서 블라스트 반경이 커지고 가시성 확보와 원인 규명이 어려워 복구에 시간이 걸리며 비즈니스 영향이 커진다.
  • 실무 체크리스트: 우선 노드 전체 메모리 사용량과 kubelet eviction 로그를 확인하고, 각 컨테이너의 requests/limits 설정과 최근 배포·업그레이드 내역을 점검하라.

Linux OOM Killer의 동작 원리와 메모리 관리 기본

커널은 페이지 회수와 스왑으로 메모리 요청을 처리합니다. 이들 방법으로도 회수가 실패하거나 메모리 할당이 거부되면 시스템은 OOM(Out Of Memory) 상황으로 판단하고 OOM Killer를 실행합니다. 각 프로세스는 /proc//oom_score로 표현되는 기본 'badness' 점수를 가지며, 관리자가 조정하는 oom_score_adj(과 과거의 oom_adj)는 이 점수를 증감시켜 대상 선정에 영향을 줍니다. 레거시 값인 oom_adj는 내부적으로 oom_score_adj로 매핑됩니다. 특히 Kubernetes 노드 OOMKill 연쇄 장애 원인 분석을 할 때는 cgroup과 컨테이너의 메모리 설정을 우선 점검해야 합니다.

  • 점수 산출: 프로세스의 RSS·스왑·총 메모리 소비를 바탕으로 oom_score가 계산되고, 여기서 oom_score_adj가 더해져 최종 우선순위가 정해집니다.
  • 권한·특수 프로세스: 루트나 시스템 프로세스는 일정 수준의 보호를 받지만, 절대적으로 면책되는 것은 아닙니다.
  • cgroup 상호작용: cgroup v1의 메모리 서브시스템이나 cgroup v2의 memory.max·memory.high 제약이 적용되면 해당 cgroup 내부에서 우선적으로 OOM 후보를 찾거나, cgroup 단위로 OOM이 발생해 연쇄 종료가 일어날 수 있습니다.
  • 실행 흐름: 메모리 할당 실패 → 페이지 회수·스왑 재시도 → oom_kill 판단 → 각 태스크의 badness 계산 → 최고 점수 프로세스에 SIGKILL 송출 → oom_reaper가 메모리를 신속히 회수하고 필요 시 연속 종료가 발생합니다. 실무 체크리스트(빠른 점검): 1) oom_score 상위 프로세스 확인, 2) 해당 cgroup의 memory.max·memory.high 설정 확인, 3) 스왑 사용량 및 페이지 회수 실패 로그 확인.

Kubernetes 레이어에서의 메모리 관리와 Eviction 메커니즘

Kubelet은 노드의 메모리 압력을 제어하기 위해 soft·hard 두 종류의 eviction 임계값을 사용한다. memory.available 같은 지표에 대해 soft threshold는 grace period를 두어 일정 시간 유지되지 않으면 파드를 축출하고, hard threshold는 조건 충족 시 즉시 축출을 일으킨다. 축출 우선순위는 QoS 클래스(BestEffort → Burstable → Guaranteed)로 결정되며, 파드의 requests와 limits 설정이 스케줄링과 런타임 동작을 좌우한다. 이 구조는 Kubernetes 노드 OOMKill 연쇄 장애 원인 분석에도 도움이 된다.

  • requests는 스케줄링 시 리소스 예약을 보장한다. limits는 컨테이너의 cgroup 제한으로, limit을 초과하면 커널 OOM의 대상이 될 수 있다.
  • cgroup v2는 memory.high(soft)와 memory.max(hard)를 통해 통합 제어를 제공한다. cgroup 단위의 OOM 처리 덕분에 파드 수준에서 더 예측 가능한 동작을 기대할 수 있다.
  • cgroup v1은 계층과 정책이 분리되어 전역 OOMKill 연쇄를 유발하기 쉽다. 운영 시에는 eviction 설정, requests/limits의 정합성, 사용할 cgroup 버전을 함께 검토해야 하며, 예: 노드 메모리 예약 확인 · 주요 파드의 limits 점검 · cgroup 버전 전환 검토 같은 기본 체크리스트를 따르는 것이 바람직하다.

연쇄 장애를 일으키는 전형적 시나리오와 근본 원인

일반적으로 여러 요소가 연쇄적으로 결합하면서 노드 OOMKill로 이어진다. 버스트성 작업이나 짧은 배치가 순간적으로 메모리 수요를 끌어올리고, 동시에 메모리 누수가 있는 애플리케이션이 장시간 동작하면 사용 가능한 메모리는 급속히 줄어든다. 여기에 로그 수집·모니터링 에이전트 같은 데몬셋이 각 노드에서 지속적으로 메모리를 소비하면 여유 여백은 더욱 작아진다. 그 결과 커널의 OOM이나 kubelet의 강제 축출이 발생해 우선순위가 낮은 파드부터 죽고, 경우에 따라 시스템 데몬까지 영향을 받아 노드 전체 장애로 확산된다. Kubernetes 노드 OOMKill 연쇄 장애 원인 분석을 실무에 적용하려면 시스템 지표와 파드별 리소스 사용을 함께 검사하는 것이 필수다.

  • 전형적 단계: 버스트 트래픽 → 메모리 누적(누수 포함) → 데몬셋·에이전트의 지속적 소비 → 총 메모리 초과 → OOMKill/노드 축출 → 다른 파드 재스케줄 실패
  • 근본 원인: 리소스 요청·한계 미설정 또는 과도한 오버커밋, QoS·PriorityClass의 오용, 데몬셋의 과다한 리소스 사용, 메모리 누수 탐지·모니터링의 미비, cgroup/커널 OOM 동작에 대한 오해. 실무 체크리스트: 요청·한계 설정, 데몬셋에 리소스 제한 적용, 메모리 누수 모니터링, QoS·PriorityClass 점검, cgroup 동작 확인.

진단 방법, 필수 수집 데이터 및 실전 명령어

OOMKill 연쇄 장애는 커널 로그와 kubelet 이벤트, 컨테이너·노드 메트릭을 시간 순으로 대조해야 원인을 정확히 밝힐 수 있다. 아래에 핵심 수집 항목과 바로 사용할 수 있는 명령어를 정리했다. Kubernetes 노드 OOMKill 연쇄 장애 원인 분석에 특히 유용하다.

  • 커널/부팅 로그: dmesg·journalctl
    dmesg -T | tail -n 100, journalctl -k -b --no-pager
  • kubelet·kube-apiserver 이벤트: 노드/파드 관련 이벤트 추출
    journalctl -u kubelet -n 200, kubectl get events --all-namespaces --sort-by='.lastTimestamp'
  • kubernetes 상태·상세: 노드/파드 설명과 로그
    kubectl describe node <node>, kubectl describe pod <pod> -n <ns>, kubectl logs/exec
  • 메트릭·성능: Prometheus/cAdvisor에서 시계열 수집
    주요 지표: node_memory_MemAvailable_bytes, container_memory_usage_bytes, container_memory_rss, kube_pod_container_status_terminated_reason
  • 상관관계 방법: OOM 메시지 타임스탬프 ↔ kubelet 이벤트 ↔ Prometheus 메모리 급증을 일별/초단위로 매칭하여 원인(노드 메모리 부족 vs 컨테이너 메모리 한계)을 구분
    간단 체크리스트: OOM 타임스탬프 확인 → 관련 kubelet 이벤트 확인 → Prometheus에서 메모리 그래프 비교

완화 및 재발 방지 대책 — 설정·아키텍처·운영 관점

설정 관점: 파드별 requests와 limits를 정확히 산정합니다. 핵심 서비스는 QoS 보장을 위해 requests=limits로 고정하고, 일반 워크로드는 requests는 평균 수준으로, limits는 피크에 맞춰 설정하세요. 또한 kubelet의 system-reserved와 kube-reserved를 적절히 지정해 노드의 시스템 프로세스가 메모리 부족에 빠지지 않도록 해야 합니다. (Kubernetes 노드 OOMKill 연쇄 장애 원인 분석 시 우선 점검할 항목입니다.)

  • Eviction 튜닝: evictionHard에 memory.available, nodefs.inodesFree 등을 설정하고 eviction-soft로 점진적인 축소 정책을 적용합니다. eviction-minimum-reclaim로 복구 목표를 명확히 하세요.
  • 오토스케일링: HPA와 VPA를 조합해 파드 스케일링과 리소스 요청 조정을 자동화하고, Cluster Autoscaler로 노드 풀을 자동 확장합니다. VPA 도입 시 QoS 변화의 영향을 반드시 검토하세요.
  • 노드 사이징: 메모리 헤드룸을 고려해 인스턴스 타입을 선택하고 시스템 예약을 반영한 오른쪽 사이징을 수행합니다. 메모리 중심/CPU 중심으로 멀티 노드풀을 설계하면 안정성이 높아집니다.
  • 모니터링·알림: node_exporter, kube-state-metrics, metrics-server 등으로 메모리 사용량, OOM 이벤트, eviction 로그를 수집하세요. 임계치 기반 경고를 설정하고 OOMKill 발생 빈도를 지표화해 추세를 관찰합니다.
  • 런북 적용: OOM 발생 시 즉시 cordon → drain으로 격리하고, 최다 OOM 파드·특정 이미지·메모리 누수 등 원인을 빠르게 식별합니다. 임시 스케일업이나 리소스 증설로 완화한 뒤 자원 재산정이나 코드 수정 같은 영구 패치를 적용하세요. 실무 체크리스트 예: cordon → drain → 원인 파악 → 임시 완화 → 영구 조치.

경험에서 배운 점

노드에서 연쇄적인 OOMKill이 발생할 때 현장에서 자주 보이는 원인은 운영용(시스템·데몬셋) 자원 예약 부족, 파드의 requests 미설정 또는 과도한 limits, 그리고 모니터링·알림 부재입니다. 프로덕션 환경에서 BestEffort나 requests 없는 파드를 허용하면 메모리 압박 시 우선순위가 낮은 파드들이 먼저 종료됩니다. 그런데 스케줄러가 해당 파드를 즉시 다른 노드로 재스케줄하면 메모리 부담이 전파되어 연쇄 장애로 이어지는 경우가 많습니다. 또한 kubelet의 eviction 설정을 기본값으로 둔 채 운영하거나 시스템용 여유 메모리를 고려하지 않은 컨테이너 배치는 문제를 악화시킵니다.

정리하면, Kubernetes 노드 OOMKill 연쇄 장애 원인 분석에서 도출되는 대응은 노드 단위의 명확한 자원 예약과 클러스터 수준의 거버넌스입니다. kube-reserved·system-reserved와 evictionHard(memory.available 등) 값을 분명히 정하고 충분히 테스트하세요. 모든 네임스페이스에 LimitRange와 ResourceQuota로 requests와 limits를 강제해야 합니다. 데몬셋(로그·모니터링·CNI 등)은 별도의 상한을 두고, 메모리 집약 워크로드는 전용 노드풀(taints/tolerations, nodeSelector)이나 우선순위/프리엠션으로 격리하세요. 마지막으로 OOMKill·kubelet eviction 이벤트와 관련된 로그와 메트릭(노드 가용 메모리, oom_kill 카운트 등)을 수집해 자동 알림을 구성하면 초동 대응 시간을 크게 줄일 수 있습니다.

실무 체크리스트(간단히 점검할 항목):
- kube-reserved / system-reserved 값 설정 확인(노드 운영 프로세스와 데몬 여유분 확보).
- kubelet evictionHard/Soft(memory.available 등) 구성 및 시나리오별 테스트 수행.
- 모든 네임스페이스에 LimitRange/ResourceQuota로 requests + limits 강제 적용.
- 데몬셋과 시스템 파드에 명확한 리소스 상한 설정(로그·모니터링은 과다 할당 금지).
- QoS 클래스 정책 정립: 프로덕션 핵심은 Guaranteed/적절한 requests 사용, BestEffort 최소화.
- 대규모 리스케일·재스케줄을 고려한 노드풀 분리(taints/tolerations, 노드 셀렉터)와 autoscaler에 최소 헤드룸 확보.
- PodPriority/Preemption 정책 도입으로 중요한 파드 보호.
- 정기적인 혼란 테스트(chaos test)로 OOM 상황 재현과 복구 절차 점검.
- 메트릭(노드 메모리 사용량, oom_kill count, kubelet eviction events) 알림 설정 및 로그(kernel messages, kubelet) 보존.

AI 생성 이미지: Kubernetes 노드 OOMKill 연쇄 장애 원인 분석
AI 생성 이미지: Kubernetes 노드 OOMKill 연쇄 장애 원인 분석

댓글

이 블로그의 인기 게시물

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%);"> 레이어 팝업 내용 <...