실무 리더가 정리한 쿠버네티스 노드 OOM 빈발 해결: 메모리 예약으로 안정화한 운영 아키텍처와 모범사례
실무 리더 요약 정리
이 글은 실무 리더가 정리한 쿠버네티스 노드 OOM 빈발 해결: 메모리 예약으로 안정화한 운영 아키텍처와 모범사례를 둘러싼 현업 의사결정 포인트를 정리해 둔 섹션입니다.
- 이 글에서 짚고 가는 핵심 포인트
- 핵심 요약
- 배경과 증상
- 원인 분석
팀 내 위키나 아키텍처 리뷰 문서에 그대로 옮겨 적고, 우리 조직 상황에 맞게만 수정해도 큰 도움이 됩니다.
몇 년 전 우리 팀은 쿠버네티스 노드 OOM 빈발, 메모리 예약으로 해결한 사례를 제대로 설계하지 못해 장애와 불필요한 야근이 반복되었습니다. 이 글은 그런 상황을 되풀이하지 않기 위해, 리더 입장에서 어떤 구조와 운영 방식을 먼저 정리해야 하는지에 초점을 맞추고 있습니다.
이 글에서 짚고 가는 핵심 포인트
- 핵심 요약
- 배경과 증상
- 원인 분석
- 실제 사례: 노드 OOM 발생과 조치
실제 엔터프라이즈 환경에서 쿠버네티스 노드 OOM 빈발, 메모리 예약으로 해결한 사례를 적용할 때 꼭 체크해야 할 구조와 운영 포인트만 정리했습니다.
핵심 요약
쿠버네티스 운영 환경에서 노드 OOM(Out Of Memory) 빈발 문제는 리소스 예약 미흡과 시스템 프로세스(예: kubelet, container runtime, logging 등)에 대한 고려 부족에서 주로 발생합니다. 본 문서는 원인 분석, 현장에서 적용한 메모리 예약 방법(kubeReserved, systemReserved, eviction thresholds)과 실전 사례, 설정 예시, 그리고 운영상 주의사항을 정리합니다. 또한 재발 방지를 위한 모범사례와 바로 적용 가능한 체크리스트를 제시합니다.
배경과 증상
수십~수백 노드 규모 클러스터에서 특정 시점에 노드가 OOM으로 재부팅되거나 kubelet이 반복적으로 팩토리 리스타트를 하는 증상을 관찰했습니다. 주기적으로 발생하는 배치 작업 또는 대형 파드 롤아웃 시, 노드의 사용 가능한 메모리가 급격히 줄어들며 OOM-killer가 활성화되었습니다.
증상은 대체로 다음과 같습니다: 파드가 Evicted 또는 OOMKilled 상태, 노드에서 kubelet/CRI 중 하나가 메모리 부족으로 재시작, dmesg/journal에 OOM 관련 로그 다수 기록. 관찰된 패턴은 시스템 프로세스(프로세스 레벨 예약)가 제공받는 메모리보다 과다한 워크로드가 할당된 경우였습니다.
원인 분석
분석 결과 주요 원인은 다음 세 가지로 정리됩니다. 첫째, 파드의 requests/limits 설정 부재 또는 미흡으로 노드 레벨에서 과다 배치가 발생했습니다. 둘째, kubelet과 container runtime, 로그 드라이버 같은 시스템 컴포넌트에 대한 메모리 예약(kubeReserved/systemReserved)이 설정되어 있지 않아, 사용자 워크로드가 시스템 프로세스 메모리를 침범했습니다. 셋째, eviction 정책(정책 임계치)이 적절히 설정되어 있지 않아 OOM 발생 시점에서 적절한 파드 축소가 이뤄지지 않았습니다.
특히 대량 로그 발생 또는 메모리 급증을 하는 배치 작업이 동시에 실행될 때 노드 전체 메모리가 빠르게 소진되는 패턴이 관찰되었습니다. 또한, 노드 운영체제(예: 파일 시스템 캐시) 영향도 고려되지 않아 가용 메모리 예측이 부정확했습니다.
실제 사례: 노드 OOM 발생과 조치
사례 A — 프로덕션 검색 서비스 롤아웃 중 노드 OOM
증상: 검색 서비스 신규 버전 롤아웃 시 특정 노드군에서 중복된 OOM 로그와 함께 다수 파드가 Evicted 되었습니다. 노드 재부팅이 몇 차례 발생했고, 서비스 레이턴시 상승을 초래했습니다.
원인 분석 및 조치 단계:
- 로그 수집: journalctl, dmesg, kubelet 로그와 kube-apiserver 이벤트를 수집해 OOM-killer 대상 프로세스와 시점을 확인했습니다.
- 파드 분포 검토: 파드의 requests가 불명확하여 한 노드에 집중 배치된 것을 확인했습니다. Horizontal Pod Distribution과 스케줄링 규칙을 검토했습니다.
- 임시 완화: 문제 재발을 막기 위해 신규 배포를 중단하고 해당 노드에서 일부 배치를 수동으로 이주시키는 조치를 했습니다.
- 영구 개선: kubeReserved/systemReserved 설정으로 시스템 프로세스에 메모리를 예약하고 evictionThreshold를 상향 조정했으며, 파드의 requests/limits 기준을 강화했습니다.
사례 B — 밤 시간에 발생한 배치 잡 동시 실행으로 인한 OOM
증상: 야간에 대용량 데이터 처리 배치가 스케줄되면서 수십 개 파드가 동시 실행, 일부 노드에서 메모리 과다 사용으로 OOM 발생.
원인 및 해결:
- 원인: batch job이 requests를 과소 설정하고 concurrency 제어가 없어 동시 실행 시 노드 자원을 초과했습니다.
- 해결: CronJob 수준에서 동시성 제한(spec.concurrencyPolicy 및 spec.successfulJobsHistoryLimit 활용)과 파드 수준의 requests/limits 강화, 그리고 Job 분산을 위한 PodAntiAffinity 정책 적용으로 재발을 억제했습니다.
메모리 예약 적용 방법 및 설정 예시
kubelet에서 시스템 프로세스와 kubelet 자체에 메모리를 예약하면 사용자 파드가 시스템 영역을 침범하는 것을 예방할 수 있습니다. 기본적으로 두 가지 예약을 권합니다: kubeReserved(쿠버네트스 컴포넌트용)와 systemReserved(노드 OS 및 기타 데몬용). 또한 evictionHard/soft 임계값을 설정해 과도한 메모리 사용 시 파드 축소를 자동화해야 합니다.
아래는 kubelet config 예시와 권장되는 파드 요청/제한 예시를 함께 제공한 블록입니다. 실제 환경에 맞춰 값을 조정하시고, 테스트 후 점진 적용하시기 바랍니다.
# /var/lib/kubelet/config.yaml (예시)
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
kubeReserved:
cpu: "250m"
memory: "512Mi"
ephemeral-storage: "1Gi"
systemReserved:
cpu: "250m"
memory: "1Gi"
ephemeral-storage: "1Gi"
evictionHard:
memory.available: "200Mi"
nodefs.available: "10%"
evictionSoft:
memory.available: "300Mi"
evictionSoftGracePeriod:
memory.available: "1m"
# Pod spec 권장 예시 (일부)
apiVersion: v1
kind: Pod
metadata:
name: example
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
설정 적용 순서: 먼저 테스트 노드에 kubelet config를 적용하고 모니터링(메모리 사용, eviction 이벤트, kubelet 재시작 여부)을 확인합니다. 이후 롤링 방식으로 전체 노드에 적용하세요. 또한 클러스터 오토스케일러와 연동 시 메모리 예약 변경이 오토스케일링 의사결정에 영향을 주므로 정책 검토가 필요합니다.
모범사례 / 베스트 프랙티스
- 파드에 반드시 requests와 limits를 설정해 스케줄러가 안정적으로 노드 배치를 하도록 합니다. 특히 메모리는 요청(requests)에 기반해 스케줄링됩니다.
- kubelet의 kubeReserved 및 systemReserved를 환경에 맞게 설정해 시스템 컴포넌트가 안정적으로 동작하도록 보장합니다.
- evictionHard/evictionSoft 임계값을 설정하고, 메트릭 기반 알람(예: memory.available 경고)을 구성합니다.
- 대규모 배치 작업은 concurrency를 제한하고, 배치 실행 시간을 분산시키는 스케줄링 전략을 사용합니다.
- 노드 모니터링(노드 레벨 메모리, swap 사용, 커널 OOM 로그)과 파드 이벤트 수집을 일상화합니다. 문제 발생 시 빠른 원인 파악에 필수적입니다.
- 롤링으로 kubelet 설정을 적용하고, 변경 전후 비교를 위한 벤치마크(메모리 사용 패턴)를 수행합니다.
- 클러스터 오토스케일러 및 노드 그룹 정책과의 상호작용을 검토하여 메모리 예약이 자동 스케일링 의사결정에 미치는 영향을 파악합니다.
FAQ
Q1: kubeReserved와 systemReserved의 차이는 무엇인가요?
A1: kubeReserved는 kubelet, kube-proxy 등 쿠버네티스 컴포넌트에 할당할 자원입니다. systemReserved는 OS 및 기타 데몬(예: 로그 수집기, 모니터링 에이전트)에 필요한 자원을 예약합니다. 둘 다 설정해 사용자 워크로드가 시스템 영역을 침범하지 않도록 합니다.
Q2: 파드에 requests만 설정하고 limits를 설정하지 않아도 괜찮나요?
A2: requests만 있으면 스케줄링 기준은 확보되지만, 런타임에서 파드가 메모리를 과다 사용하면 OOMKilled될 수 있습니다. 가능한 경우 requests와 limits를 모두 설정해 예측 가능한 메모리 사용을 만드는 것이 안전합니다.
Q3: evictionHard와 evictionSoft 중 어느 것을 먼저 설정해야 하나요?
A3: evictionHard는 즉시 강제되는 임계값이며, evictionSoft는 여유 기간을 주어 graceful하게 축소할 수 있게 합니다. 둘 다 필요하나 최소한 evictionHard(memory.available 등)는 반드시 설정해 두는 것이 좋습니다.
Q4: kubelet 설정을 변경하면 즉시 적용되나요?
A4: kubelet 설정은 대부분 재시작 또는 kubelet에 설정 재적용(reload)로 반영됩니다. 설정 변경 전 테스트 노드에서 적용해 문제 발생 여부를 확인하고, 점진적으로 롤아웃하세요.
Q5: 메모리 예약을 늘리면 노드의 사용 가능한 자원이 줄어들지 않나요?
A5: 네, 예약은 사용자 워크로드가 사용할 수 있는 자원을 줄입니다. 그러나 이는 시스템 안정성 확보를 위한 비용으로 보아야 합니다. 예약을 통해 OOM 발생과 서비스 불안정을 줄이면 전체 서비스 가용성은 향상됩니다.
엔터프라이즈 팀 리더 경험담
초기 진단과 kubelet 메모리 예약 적용
문제
쿠버네티스 노드에서 자주 발생하는 OOM(Out Of Memory)으로 인해 kube-system 컴포넌트가 재시작하거나 노드가 불안정해졌다. 장애 알림으로는 노드 OOM 이벤트와 함께 여러 파드의 강제 종료 로그가 연달아 확인되었다.
접근
- 문제 재현과 로그 수집: kubelet 로그, dmesg, cadvisor/metrics-server 메트릭을 수집해 OOM 시점의 프로세스와 메모리 사용 패턴을 분석했다.
- 노드 예약 설정 검토: kube-reserved, system-reserved, eviction-hard 설정이 없거나 부족한 노드가 다수였기에 기본값을 명시적으로 설정했다.
- 점진적 적용: 모든 노드에 한꺼번에 반영하지 않고, 한 AZ 내 테스트 노드군부터 kubelet 설정을 변경하고 모니터링을 24시간 집중 수행했다.
결과
노드 OOM 관련 장애 건수가 평균적으로 월 8건에서 월 1건으로 감소했고, 평균 복구 시간(MTTR)은 약 60분에서 약 20분으로 단축되었다.
회고
시스템 예약을 명시하지 않은 상태에서 베어메탈 혹은 VM의 운영 프로세스가 파드와 메모리를 경쟁하던 것이 근본 원인이었다. 예약을 도입한 뒤에도 메모리 사용량 상한을 명확히 하지 않은 워크로드는 여전히 위험하므로, 노드 변경은 필수지만 워크로드 정책과 함께 가야 한다.
테넌트 워크로드의 돌발 메모리 사용과 QoS 관리
문제
특정 배치성/테스트 워크로드가 갑작스러운 메모리 버스트를 일으켜 같은 노드의 중요한 서비스가 퇴출(evicted)되는 사례가 반복됐다.
접근
- 리소스 요청/한도(Request/Limit) 규정 강화: 네임스페이스별 LimitRange와 ResourceQuota를 통해 임의 배포가 노드 안정성에 미치는 영향을 제한했다.
- QoS 교육과 가이드 제공: 개발팀에게 QoS 클래스(Guaranteed, Burstable, BestEffort)에 따른 영향과 권장 설정을 문서화해 배포 파이프라인에 반영하도록 했다.
- 데몬셋 조정: 라벨링된 워크로드는 전용 노드풀로 유도해 중요 서비스와 분리했다.
결과
동일 노드에서의 중요 서비스 퇴출 사례가 눈에 띄게 줄었고, 개발팀 배포 실수로 인한 운영 확보 시간을 줄일 수 있었다.
회고
플랫폼 측에서 강제하는 정책(Quota/LimitRange)과 동시에 개발팀의 이해를 돕는 교육이 병행되어야 효과가 있다. 정책만 두고 방치하면 회피하는 배포가 생긴다는 점을 확인했다.
모니터링·알림 및 자동 회복으로 재발 방지
문제
초기 개선 후에도 드문 빈도로 OOM이 발생했고, 탐지와 수동 대응에 시간이 걸려 영향이 커졌다.
접근
- 메트릭과 경보 재정의: Prometheus 알림 규칙을 노드 할당량 임계치 기반으로 재정의해 선제 알림을 수신했다.
- 런북과 자동화: 특정 임계치 초과 시 조사용 로그를 자동 수집하고, 간단한 셀프 치유(예: 비정상 파드 재스케줄) 자동화 플레이북을 도입했다.
- 사후 분석 프로세스 확립: 모든 OOM 사건에 대해 원인 분석 템플릿을 적용하고 개선 항목을 분류해 우선순위를 정했다.
결과
초기 탐지 리드타임이 줄고 운영자의 수동 개입이 필요한 상황이 줄어들었으며, 동일 유형의 재발 빈도가 감소했다.
회고
기술적 조치뿐 아니라 운영 프로세스와 책임 분담(누가 어떤 경보를 보고, 어떤 조치를 취하는지)을 명확히 한 것이 효과적이었다. 자동화는 완전한 해법이 아니라 초동 대응 시간을 줄이고 사람의 실수를 줄이는 보완책이라는 점을 다시 확인했다.
문제 vs 해결 전략 요약
| 문제 | 해결 전략 |
|---|---|
| 조직마다 제각각인 쿠버네티스 노드 OOM 빈발, 메모리 예약으로 해결한 사례 운영 방식 | 표준 아키텍처와 운영 상용구를 정의하고 서비스별로 변형만 허용 |
| 장애 후에야 뒤늦게 쌓이는 인사이트 | 사전 지표 설계와 SLO/에러 버짓을 기반으로 한 사전 탐지 체계 구축 |
| 문서와 실제 운영 사이의 괴리 | Infrastructure as Code와 같은 실행 가능한 문서 형태로 관리 |
결론 및 다음 액션
노드 OOM 문제는 기술적 설정뿐 아니라 조직의 운영 관행(파드 리소스 설정, 배포 정책, 모니터링 및 알람 설계)까지 포함하는 종합적 문제입니다. 메모리 예약은 빠르게 효과를 낼 수 있는 수단이며, 재발 방지를 위해서는 정책과 프로세스 개선이 병행되어야 합니다.
다음 액션(우선순위 제안):
- 테스트 노드에 kubelet의 kubeReserved/systemReserved와 eviction 설정을 적용하고 1주일 모니터링을 수행합니다.
- 모든 파드 템플릿에 최소 요청(requests)과 권장 limits를 템플릿화하여 파이프라인 검사(CI)에서 강제합니다.
- 배치 작업의 동시성 제어 및 실행 스케줄을 검토해 특정 시간대에 자원 소모가 집중되지 않도록 조정합니다.
- 메모리 관련 알람(예: memory.available 임계치)과 OOM 이벤트를 중앙 로그/대시보드에 통합하여 탐지 시간을 단축합니다.
- 클러스터 오토스케일러와의 상호작용을 테스트해 예약 변경이 자동 스케일 정책에 미치는 영향을 평가합니다.
이 글은 현업에서 반복적으로 발생하는 노드 OOM 문제를 예방하기 위한 실무적 가이드를 제공하기 위해 작성했습니다. 조직 내 위키나 Runbook에 이 내용을 기반으로 절차를 남겨 두시면 문제 발생 시 대응 시간이 단축됩니다.
댓글
댓글 쓰기