리소스 관리 — requests와 limits가 실제로 하는 일
Pod에 리소스 설정을 안 하면 편하긴 한데, 하나의 Pod이 노드의 CPU와 메모리를 다 써버리면 어떻게 될까요?
리소스 설정은 Kubernetes에서 가장 중요하면서도 가장 많이 놓치는 부분입니다. requests와 limits를 올바르게 설정하지 않으면 스케줄링 실패, OOMKill, CPU 쓰로틀링 같은 문제가 발생합니다. 이 두 값이 실제로 어떻게 동작하는지 정확하게 이해해야 합니다.
requests vs limits
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
| 항목 | requests | limits |
|---|---|---|
| 용도 | 스케줄링 기준 | 런타임 상한 |
| CPU 초과 시 | - | 쓰로틀링 (성능 저하) |
| 메모리 초과 시 | - | OOMKill (컨테이너 종료) |
| 미설정 시 | 스케줄러가 0으로 간주 | 무제한 사용 가능 |
CPU 단위
| 표기 | 의미 |
|---|---|
1 | 1 CPU 코어 |
500m | 0.5 코어 (밀리코어) |
100m | 0.1 코어 |
메모리 단위
| 표기 | 의미 |
|---|---|
128Mi | 128 MiB (메비바이트) |
1Gi | 1 GiB |
128M | 128 MB (메가바이트, Mi보다 약간 작음) |
CPU — 압축 가능한 리소스
CPU는 시분할로 공유할 수 있는 "압축 가능한" 리소스입니다.
CPU requests의 역할
- 스케줄링: 스케줄러는 노드의 남은 CPU가 requests 이상인 노드를 선택합니다
- CPU 가중치: Linux cgroups에서 CPU shares로 변환됩니다 (requests가 클수록 더 많은 CPU 시간)
CPU limits와 쓰로틀링
limits를 초과하면 컨테이너가 종료되는 것이 아니라 쓰로틀링됩니다. 100ms 주기 중 limits에 해당하는 시간만큼만 CPU를 사용할 수 있습니다.
limits: 200m → 100ms 중 20ms만 CPU 사용 가능
limits: 500m → 100ms 중 50ms만 CPU 사용 가능
쓰로틀링은 응답 지연을 유발합니다. P99 레이턴시가 갑자기 튀는 경우 CPU 쓰로틀링이 원인일 수 있습니다.
# 쓰로틀링 확인
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가 컨테이너를 강제 종료합니다.
# OOMKill 확인
kubectl describe pod my-pod
# Last State: Terminated
# Reason: OOMKilled
# Exit Code: 137
OOMKill이 발생하면 Pod의 restartPolicy에 따라 재시작되며, 반복되면 CrashLoopBackOff 상태가 됩니다.
메모리 requests의 역할
- 스케줄링: 노드의 남은 메모리가 requests 이상이어야 스케줄
- eviction 우선순위: 노드 메모리 부족 시 requests 대비 사용량이 높은 Pod이 먼저 퇴출
QoS (Quality of Service) 클래스
Kubernetes는 리소스 설정에 따라 Pod에 QoS 클래스를 자동 할당합니다. 노드 리소스가 부족하면 QoS가 낮은 Pod부터 퇴출합니다.
Guaranteed — 최우선
# 모든 컨테이너에 requests = limits (CPU, 메모리 모두)
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 500m
memory: 512Mi
Burstable — 중간
# requests < limits 이거나, 일부만 설정
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
BestEffort — 최하위
# requests와 limits 모두 미설정
resources: {}
퇴출 우선순위: BestEffort → Burstable → Guaranteed
프로덕션 중요 워크로드는 Guaranteed 또는 Burstable을 사용하고, BestEffort는 절대 피해야 합니다.
# Pod의 QoS 클래스 확인
kubectl get pod my-pod -o jsonpath='{.status.qosClass}'
LimitRange — 기본값과 범위 설정
네임스페이스에서 리소스 설정을 빠뜨린 Pod에 기본값을 적용하고, 과도한 요청을 제한합니다.
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에 자동으로 defaultRequest와 default가 적용됩니다.
ResourceQuota — 네임스페이스 총량 제한
네임스페이스 전체가 사용할 수 있는 리소스의 총합을 제한합니다.
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 수
# 사용량 확인
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
리소스 설정 가이드
권장하는 설정 방법
- 먼저 VPA를 Off 모드로 배포하여 추천값을 확인합니다
- requests는 일반적인 사용량으로, limits는 최대 사용량으로 설정합니다
- CPU limits는 신중하게: 불필요한 쓰로틀링을 유발할 수 있습니다
- 메모리 limits는 반드시 설정: OOMKill보다 메모리 무한 사용이 더 위험합니다
# 웹 서버 예제
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로 네임스페이스 수준의 관리를 추가하면, 클러스터 리소스를 효율적이고 안전하게 사용할 수 있습니다.