Theme:

Pod에 리소스 설정을 안 하면 편하긴 한데, 하나의 Pod이 노드의 CPU와 메모리를 다 써버리면 어떻게 될까요?

리소스 설정은 Kubernetes에서 가장 중요하면서도 가장 많이 놓치는 부분입니다. requests와 limits를 올바르게 설정하지 않으면 스케줄링 실패, OOMKill, CPU 쓰로틀링 같은 문제가 발생합니다. 이 두 값이 실제로 어떻게 동작하는지 정확하게 이해해야 합니다.

requests vs limits

YAML
spec:
  containers:
    - name: app
      image: my-app:1.0
      resources:
        requests:        # 최소 보장 리소스
          cpu: 200m      # 0.2 CPU 코어
          memory: 256Mi  # 256 MiB
        limits:          # 최대 사용 제한
          cpu: 500m      # 0.5 CPU 코어
          memory: 512Mi  # 512 MiB
항목requestslimits
용도스케줄링 기준런타임 상한
CPU 초과 시-쓰로틀링 (성능 저하)
메모리 초과 시-OOMKill (컨테이너 종료)
미설정 시스케줄러가 0으로 간주무제한 사용 가능

CPU 단위

표기의미
11 CPU 코어
500m0.5 코어 (밀리코어)
100m0.1 코어

메모리 단위

표기의미
128Mi128 MiB (메비바이트)
1Gi1 GiB
128M128 MB (메가바이트, Mi보다 약간 작음)

CPU — 압축 가능한 리소스

CPU는 시분할로 공유할 수 있는 "압축 가능한" 리소스입니다.

CPU requests의 역할

  1. 스케줄링: 스케줄러는 노드의 남은 CPU가 requests 이상인 노드를 선택합니다
  2. CPU 가중치: Linux cgroups에서 CPU shares로 변환됩니다 (requests가 클수록 더 많은 CPU 시간)

CPU limits와 쓰로틀링

limits를 초과하면 컨테이너가 종료되는 것이 아니라 쓰로틀링됩니다. 100ms 주기 중 limits에 해당하는 시간만큼만 CPU를 사용할 수 있습니다.

PLAINTEXT
limits: 200m → 100ms 중 20ms만 CPU 사용 가능
limits: 500m → 100ms 중 50ms만 CPU 사용 가능

쓰로틀링은 응답 지연을 유발합니다. P99 레이턴시가 갑자기 튀는 경우 CPU 쓰로틀링이 원인일 수 있습니다.

SHELL
# 쓰로틀링 확인
kubectl exec my-pod -- cat /sys/fs/cgroup/cpu/cpu.stat
# nr_throttled 1234       # 쓰로틀링된 횟수
# throttled_time 567890   # 쓰로틀링된 총 시간 (ns)

공부하다 보니 CPU limits를 너무 낮게 설정해서 불필요한 쓰로틀링이 발생하는 경우가 꽤 많더라고요. 일부에서는 CPU limits를 아예 설정하지 않는 것을 권장하기도 합니다.

메모리 — 압축 불가능한 리소스

메모리는 반환하지 않으면 회수할 수 없는 "비압축" 리소스입니다.

OOMKill

메모리 사용량이 limits를 초과하면 Linux OOM Killer가 컨테이너를 강제 종료합니다.

SHELL
# OOMKill 확인
kubectl describe pod my-pod
# Last State: Terminated
#   Reason: OOMKilled
#   Exit Code: 137

OOMKill이 발생하면 Pod의 restartPolicy에 따라 재시작되며, 반복되면 CrashLoopBackOff 상태가 됩니다.

메모리 requests의 역할

  1. 스케줄링: 노드의 남은 메모리가 requests 이상이어야 스케줄
  2. eviction 우선순위: 노드 메모리 부족 시 requests 대비 사용량이 높은 Pod이 먼저 퇴출

QoS (Quality of Service) 클래스

Kubernetes는 리소스 설정에 따라 Pod에 QoS 클래스를 자동 할당합니다. 노드 리소스가 부족하면 QoS가 낮은 Pod부터 퇴출합니다.

Guaranteed — 최우선

YAML
# 모든 컨테이너에 requests = limits (CPU, 메모리 모두)
resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: 500m
    memory: 512Mi

Burstable — 중간

YAML
# requests < limits 이거나, 일부만 설정
resources:
  requests:
    cpu: 200m
    memory: 256Mi
  limits:
    cpu: 500m
    memory: 512Mi

BestEffort — 최하위

YAML
# requests와 limits 모두 미설정
resources: {}

퇴출 우선순위: BestEffort → Burstable → Guaranteed

프로덕션 중요 워크로드는 Guaranteed 또는 Burstable을 사용하고, BestEffort는 절대 피해야 합니다.

SHELL
# Pod의 QoS 클래스 확인
kubectl get pod my-pod -o jsonpath='{.status.qosClass}'

LimitRange — 기본값과 범위 설정

네임스페이스에서 리소스 설정을 빠뜨린 Pod에 기본값을 적용하고, 과도한 요청을 제한합니다.

YAML
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: production
spec:
  limits:
    - type: Container
      default:           # limits 기본값
        cpu: 500m
        memory: 512Mi
      defaultRequest:    # requests 기본값
        cpu: 100m
        memory: 128Mi
      max:               # 최대값
        cpu: 2
        memory: 4Gi
      min:               # 최소값
        cpu: 50m
        memory: 64Mi
    - type: Pod
      max:
        cpu: 4
        memory: 8Gi

리소스를 설정하지 않은 Pod에 자동으로 defaultRequestdefault가 적용됩니다.

ResourceQuota — 네임스페이스 총량 제한

네임스페이스 전체가 사용할 수 있는 리소스의 총합을 제한합니다.

YAML
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "10"        # 총 CPU requests 합계
    requests.memory: 20Gi     # 총 메모리 requests 합계
    limits.cpu: "20"          # 총 CPU limits 합계
    limits.memory: 40Gi       # 총 메모리 limits 합계
    pods: "50"                # 최대 Pod 수
    persistentvolumeclaims: "10"  # 최대 PVC 수
SHELL
# 사용량 확인
kubectl describe resourcequota team-quota -n team-a
# Name:             team-quota
# Resource          Used    Hard
# --------          ----    ----
# limits.cpu        4       20
# limits.memory     8Gi     40Gi
# pods              15      50
# requests.cpu      2       10
# requests.memory   4Gi     20Gi

리소스 설정 가이드

권장하는 설정 방법

  1. 먼저 VPA를 Off 모드로 배포하여 추천값을 확인합니다
  2. requests는 일반적인 사용량으로, limits는 최대 사용량으로 설정합니다
  3. CPU limits는 신중하게: 불필요한 쓰로틀링을 유발할 수 있습니다
  4. 메모리 limits는 반드시 설정: OOMKill보다 메모리 무한 사용이 더 위험합니다
YAML
# 웹 서버 예제
resources:
  requests:
    cpu: 200m      # 일반적인 사용량
    memory: 256Mi
  limits:
    cpu: 1         # 피크 시 상한 (또는 미설정)
    memory: 512Mi  # 반드시 설정

# Java 애플리케이션 예제 (힙 메모리 고려)
resources:
  requests:
    cpu: 500m
    memory: 1Gi    # -Xmx 값보다 여유 있게
  limits:
    memory: 1536Mi # 힙 + 메타스페이스 + 네이티브 메모리

실무에서 자주 하는 실수

  • requests 미설정: 스케줄러가 0으로 간주하여 노드에 과도하게 배치됨
  • limits = requests: 안전하지만 리소스 활용률이 낮아짐
  • 메모리 limits 미설정: 메모리 누수가 노드 전체에 영향
  • CPU limits가 너무 낮음: 불필요한 쓰로틀링으로 레이턴시 증가

정리

requests는 스케줄링과 최소 보장의 기준, limits는 런타임 상한선입니다. CPU 초과는 쓰로틀링, 메모리 초과는 OOMKill로 이어집니다. QoS 클래스를 이해하고 LimitRange/ResourceQuota로 네임스페이스 수준의 관리를 추가하면, 클러스터 리소스를 효율적이고 안전하게 사용할 수 있습니다.

댓글 로딩 중...