Kubernetes Requests/Limits 설정

커버 이미지
직접 생성. 무단 사용 금지

Kubernetes 리소스 설정의 기본 구조

Kubernetes에서 모든 워크로드(Pod)는 CPU와 메모리에 대해 requestslimits를 설정할 수 있습니다. 이 두 값은 스케줄링과 런타임 동작에서 전혀 다른 역할을 합니다.

resources:
  requests:
    cpu: "250m"
    memory: "256Mi"
  limits:
    cpu: "1000m"
    memory: "512Mi"

requests: 스케줄러가 Pod를 노드에 배치할 때 사용하는 값입니다. “이 Pod는 최소한 이만큼의 리소스가 필요합니다”라고 선언하는 것입니다. 노드의 allocatable 리소스에서 requests 합계를 빼서 남은 공간이 충분한 노드에만 스케줄링됩니다.

limits: 런타임에서 컨테이너가 사용할 수 있는 리소스의 상한선입니다. 이 값을 초과하면 CPU는 쓰로틀링(throttling)되고, 메모리는 OOMKilled가 발생합니다.

QoS(Quality of Service) 클래스

Kubernetes는 requests와 limits 설정에 따라 Pod에 자동으로 QoS 클래스를 부여합니다. 이 클래스는 노드의 리소스가 부족할 때 어떤 Pod를 먼저 축출(evict)할지 결정합니다.

  • Guaranteed: 모든 컨테이너의 모든 리소스(CPU, 메모리)에 requests == limits. 가장 높은 우선순위.
  • Burstable: requests와 limits가 설정되었지만 같지 않음. 중간 우선순위.
  • BestEffort: requests도 limits도 설정되지 않음. 가장 먼저 축출됨.

실무 권장: 프로덕션 워크로드는 최소한 Burstable 이상을 유지하세요. BestEffort Pod는 노드 메모리 압박 시 가장 먼저 종료됩니다.

OOMKilled 이해와 대응

OOMKilled(Out Of Memory Killed)는 컨테이너가 메모리 limits를 초과했을 때 커널이 해당 프로세스를 강제 종료하는 것입니다.

확인 방법:

# Pod 상태에서 OOMKilled 확인
kubectl describe pod <pod-name>
# Last State: Terminated - Reason: OOMKilled

# 노드 레벨에서 OOM 이벤트 확인
kubectl get events --field-selector reason=OOMKilling

OOMKilled가 발생하는 주요 원인:

  • 메모리 limits이 애플리케이션 실제 필요량보다 낮게 설정됨
  • 메모리 누수(memory leak)가 있는 애플리케이션
  • JVM 힙 설정이 컨테이너 limits와 불일치 (JVM 총 메모리 = 힙 + 메타스페이스 + 네이티브 메모리)
  • 사이드카 컨테이너의 메모리 사용을 고려하지 않음

대응 전략:

  1. 모니터링 먼저: kubectl top pod 또는 Prometheus + Grafana로 실제 메모리 사용량 패턴을 확인
  2. limits 조정: 피크 사용량의 1.2~1.5배로 limits 설정
  3. JVM 워크로드: -XX:MaxRAMPercentage=75로 힙을 컨테이너 메모리의 75% 이내로 제한
  4. 메모리 누수 의심 시: 프로파일링 도구(pprof, JFR 등)로 원인 분석

CPU Throttling 이해와 대응

CPU limits을 초과하면 OOMKilled처럼 프로세스가 종료되지는 않습니다. 대신 커널의 CFS(Completely Fair Scheduler)가 해당 컨테이너의 CPU 시간을 제한합니다. 이것이 CPU 쓰로틀링입니다.

쓰로틀링의 영향:

  • 응답 시간(latency) 증가
  • 처리량(throughput) 감소
  • 간헐적인 타임아웃 발생
  • Probe 응답 지연으로 인한 Pod 재시작 (liveness probe 실패)

쓰로틀링 확인 방법:

# cAdvisor 메트릭으로 확인 (Prometheus 쿼리)
rate(container_cpu_cfs_throttled_periods_total[5m])
/ rate(container_cpu_cfs_periods_total[5m])

# 쓰로틀링 비율이 25% 이상이면 limits 조정 필요

requests와 limits 설정 실전 가이드

적절한 값을 찾는 것은 한 번에 되지 않습니다. 다음 절차를 따릅니다.

  1. 기본값으로 시작: requests = 예상 평균 사용량, limits = 예상 피크 사용량의 1.5배
  2. 1~2주 모니터링: 실제 사용량 데이터 수집
  3. 조정: P95 사용량 기준으로 requests 설정, P99 기준으로 limits 설정
  4. 반복: 트래픽 패턴이 바뀔 때마다 재검토

CPU 설정 팁:

  • requests는 평상시 사용량과 비슷하게
  • CPU limits를 아예 설정하지 않는 전략도 있음 (Google의 Best Practice). 쓰로틀링을 피하고 burstable하게 운영
  • 단, limits 미설정 시 QoS가 Burstable이 되므로 축출 우선순위에 영향

메모리 설정 팁:

  • 메모리 limits는 반드시 설정 (미설정 시 노드 전체 메모리를 소진할 위험)
  • requests와 limits를 같게 설정하면 Guaranteed QoS → 축출 방지
  • JVM: -XX:MaxRAMPercentage=75, Go: GOMEMLIMIT, Node.js: --max-old-space-size

LimitRange와 ResourceQuota

네임스페이스 레벨에서 리소스 정책을 강제할 수 있습니다.

# LimitRange: 컨테이너별 기본값/최대값 설정
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
spec:
  limits:
  - default:
      cpu: "500m"
      memory: "512Mi"
    defaultRequest:
      cpu: "100m"
      memory: "128Mi"
    max:
      cpu: "2"
      memory: "2Gi"
    type: Container

LimitRange를 설정하면 requests/limits를 명시하지 않은 Pod에 자동으로 기본값이 적용됩니다. 이는 BestEffort Pod가 생기는 것을 방지합니다.

실전 디버깅 체크리스트

리소스 관련 문제가 발생했을 때 확인할 순서:

  1. kubectl describe pod로 상태 및 이벤트 확인
  2. kubectl top pod으로 현재 사용량 확인
  3. OOMKilled면: limits 대비 실제 메모리 사용량 분석, JVM/런타임 설정 검토
  4. Pending 상태면: kubectl describe node로 allocatable vs requests 합계 비교
  5. 느려짐이면: CPU 쓰로틀링 메트릭 확인
  6. 축출(Evicted)이면: 노드의 메모리/디스크 압박 상태 및 Pod QoS 클래스 확인

마무리

리소스 설정은 Kubernetes 운영의 기본이면서 가장 자주 실수하는 영역입니다. “일단 넉넉하게”는 비용 낭비로, “최대한 아껴서”는 장애로 이어집니다. 모니터링 데이터를 기반으로 지속적으로 조정하는 것이 유일한 정답입니다.

7) QoS 클래스별 동작과 OOM Kill 시나리오

K8s는 메모리 부족 시 QoS 클래스에 따라 어떤 Pod을 먼저 죽일지 결정합니다. 이 우선순위를 모르면 중요한 Pod이 먼저 죽는 사고가 발생합니다.

# QoS 클래스 결정 규칙
# ━━━━━━━━━━━━━━━━━━━━━

# 1. Guaranteed — 모든 컨테이너가 requests = limits
apiVersion: v1
kind: Pod
spec:
  containers:
    - name: app
      resources:
        requests:
          memory: "512Mi"
          cpu: "500m"
        limits:
          memory: "512Mi"    # requests와 동일
          cpu: "500m"        # requests와 동일
# → QoS: Guaranteed (OOM Kill 우선순위 가장 낮음)

# 2. Burstable — requests < limits 또는 일부만 설정
spec:
  containers:
    - name: app
      resources:
        requests:
          memory: "256Mi"
          cpu: "250m"
        limits:
          memory: "512Mi"    # requests보다 큼
          cpu: "1"
# → QoS: Burstable (중간 우선순위)

# 3. BestEffort — requests/limits 미설정
spec:
  containers:
    - name: app
      image: myapp:latest
      # resources 섹션 없음
# → QoS: BestEffort (OOM Kill 1순위 ⚠️)

8) CPU Throttling 진단과 해결

CPU limits를 설정하면 CFS(Completely Fair Scheduler)가 throttling을 걸어 응답 지연이 발생합니다. 메모리와 달리 CPU는 limits를 아예 설정하지 않는 전략도 유효합니다.

# CPU Throttling 확인
kubectl top pod api-server-xxx
# CPU(cores)가 limits에 근접하면 throttling 발생 가능

# cAdvisor 메트릭으로 정밀 진단
# container_cpu_cfs_throttled_periods_total / container_cpu_cfs_periods_total
# → 10% 이상이면 throttling이 성능에 영향

# PromQL 쿼리
rate(container_cpu_cfs_throttled_periods_total[5m])
/ rate(container_cpu_cfs_periods_total[5m]) > 0.1

# 해결 전략:
# Option A: CPU limits 제거 (requests만 설정)
resources:
  requests:
    cpu: "500m"
    memory: "512Mi"
  limits:
    memory: "512Mi"   # 메모리 limits는 유지!
    # cpu limits 제거 → Burstable이지만 throttling 없음

# Option B: CPU limits를 넉넉하게
resources:
  requests:
    cpu: "500m"
  limits:
    cpu: "2"          # requests의 4배 여유

9) 관련 글

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux