금요일

Kubernetes OOMKilled 에러 완벽 해결 가이드

🔍 검색 키워드: k8s OOMKilled, 쿠버네티스 메모리 부족, 포드 강제 종료, 메모리 리소스 요청, 리미트 설정

Kubernetes OOMKilled 에러 완벽 해결 가이드

1. 증상: OOMKilled 에러 메시지

Pod이 갑자기 재시작되고 로그에 다음과 같이 나타난다:

$ kubectl describe pod my-app-5f8d4c9d7b-xyz12
...
Last State:     Terminated
  Reason:       OOMKilled
  Exit Code:    137
  Killed:       True
Events:
  Type    Reason     Age   Message
  ----    ------     ---   -------
  Normal  Created    2m    Created container app
  Normal  Started    2m    Started container app
  Normal  Killing    1m    Killing container app (killed due to memory limit exceeded)

Pod의 상태가 계속 Pending이거나 CrashLoopBackOff 상태에 빠진다. 다른 Pod들은 정상인데 특정 Pod만 반복적으로 재시작된다.

2. 원인 분석

메모리 리미트 설정 부족

애플리케이션이 사용하는 실제 메모리가 Pod에 할당된 메모리 한계를 초과했다. kubelet이 OOM(Out of Memory) 상태를 감지하면 강제로 Pod을 kill한다.

메모리 누수

애플리케이션 코드에 메모리 누수가 있어서 시간이 지날수록 메모리 사용량이 증가한다. Node의 물리적 메모리도 한계가 있어 OOMKilled 발생.

Node 자원 부족

Node 레벨의 메모리가 부족하면 kubelet은 eviction policy에 따라 Pod들을 순차적으로 종료한다.

사이드카 컨테이너

logging agent, service mesh proxy(Istio sidecar) 등 추가 컨테이너들의 메모리 누적.

3. 해결방법

방법 1: Memory Request/Limit 올바르게 설정

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
  - name: app
    image: my-app:latest
    resources:
      requests:
        memory: "256Mi"    # Pod 스케줄링 시 보장받는 메모리
      limits:
        memory: "512Mi"    # 절대 초과 불가능한 한계
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 30
      periodSeconds: 10

requests: Scheduler가 Node 할당 결정 시 사용
limits: cgroup을 통해 강제로 제한

방법 2: 메모리 사용량 모니터링

# Pod의 실제 메모리 사용량 확인
$ kubectl top pod my-app-5f8d4c9d7b-xyz12 -n default

NAME                     CPU(cores)   MEMORY(bytes)
my-app-5f8d4c9d7b-xyz12  150m         387Mi

# Node 전체 메모리 상태
$ kubectl top nodes
NAME          CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
worker-node1  1200m        60%    5432Mi          68%

Prometheus + Grafana로 시간대별 메모리 추이를 분석. 대부분 정상이지만 특정 시간에 메모리 스파이크 발생하는지 확인.

방법 3: 메모리 누수 디버깅 (Node.js 예시)

// Node.js 힙 덤프 수집
const heapdump = require('heapdump');

app.get('/heapdump', (req, res) => {
  const fileName = `/tmp/heapdump-${Date.now()}.heapsnapshot`;
  heapdump.writeSnapshot(fileName, (err, filename) => {
    if (err) return res.status(500).send(err);
    res.send(`Heap dump written to ${filename}`);
  });
});

// Kubernetes manifest에 보안 컨텍스트 추가
securityContext:
  readOnlyRootFilesystem: true
  runAsNonRoot: true
  allowPrivilegeEscalation: false
volumeMounts:
- name: tmp
  mountPath: /tmp
volumes:
- name: tmp
  emptyDir: {}

방법 4: HPA(Horizontal Pod Autoscaler) 설정

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 70

메모리 사용율이 70% 도달하면 자동으로 Pod 개수 증가.

4. 정리표

구분 내용 해결도
증상 Pod이 OOMKilled로 반복 재시작됨 -
원인 1 메모리 limit 설정 부족 ✓ 높음
원인 2 애플리케이션 메모리 누수 ✓ 중간
원인 3 Node 자원 전체 부족 ✓ 중간
원인 4 사이드카 컨테이너 누적 ✓ 낮음
해결 1 Request/Limit 재설정 즉시 효과
해결 2 메모리 모니터링 (top, Prometheus) 원인 파악 필수
해결 3 메모리 누수 디버깅 근본 해결
해결 4 HPA 자동 스케일링 장기 대책

핵심: k8s OOMKilled는 "메모리가 부족하다"는 신호. requests는 넉넉하게, limits는 필요한 만큼만 설정하고, 정기적으로 메모리 프로파일링을 해야 한다.

댓글 없음:

댓글 쓰기