resources:
requests: { cpu: "250m", memory: "256Mi" }
limits: { cpu: "1", memory: "512Mi" }
-
requests — '스케줄링 기준' 이다
- 이만큼 확보 가능한 노드를 찾는다
- 실제 사용량과 무관하다 — 예약이지 소비가 아니다
-
limits — '실행 중 상한' 이다. cgroup 으로 강제된다
- CPU 초과 → 스로틀 (안 죽는다)
- 메모리 초과 → OOMKilled (죽는다)
가장 흔한 오해
"노드에 메모리가 남는데 왜 Pending 인가"
스케줄러는 '실제 사용량' 이 아니라 'requests 의 합' 을 본다 requests 를 크게 잡은 파드들이 실제로는 놀고 있어도 장부상으로는 노드가 꽉 찬 것이다
kubectl describe node <name> | grep -A8 'Allocated resources'
- 이것이 그 장부다
QoS 클래스 — 자원이 모자랄 때 누가 먼저 죽나
- Guaranteed — 모든 컨테이너에 requests == limits (cpu·memory 둘 다)
- 가장 나중에 퇴출된다
- Burstable — requests 는 있는데 limits 와 다르거나 일부만 있다
- BestEffort — requests·limits 가 아예 없다
- 가장 먼저 퇴출된다
kubectl get pod web -o jsonpath='{.status.qosClass}'
중요한 워크로드일수록 requests 를 명시해야 한다 아무것도 안 적으면 BestEffort 가 되어 제일 먼저 쫓겨난다
CPU 스로틀은 안 죽고 느려진다
--cpu limit 은 '주기당 상한' 이다
- 기본 주기 100ms 에 limits 만큼만 쓸 수 있다
멀티스레드 애플리케이션이 순간적으로 여러 코어를 쓰려 하면
- 할당량을 빨리 소진하고 나머지 주기를 강제로 쉰다
- 평균 사용률은 낮은데 p99 만 나빠진다
확인 — 파드 안에서 cat /sys/fs/cgroup/cpu.stat 의 nr_throttled
LimitRange 로 기본값을 강제한다
kind: LimitRange
spec:
limits:
- type: Container
default: { cpu: "500m", memory: "512Mi" }
defaultRequest: { cpu: "100m", memory: "128Mi" }
- 네임스페이스에 걸어 두면 requests 를 안 쓴 파드에도 기본값이 붙는다
- BestEffort 파드가 생기는 것을 구조적으로 막는다
ResourceQuota 가 걸린 네임스페이스에서는 requests/limits 없는 파드가 아예 '생성 거부' 된다
limit 을 걸지 말자는 주장도 있다
CPU limit 논쟁
-
걸자 — 노드의 다른 파드를 보호한다. 성능이 예측 가능해진다 걸지 말자 스로틀 때문에 여유 자원이 있어도 못 쓴다
- requests 로 이미 최소 몫이 보장되므로 상한은 낭비다
메모리는 다르다 — 넘으면 죽으므로 상한이 필요하다 메모리 limit 은 걸고, CPU limit 은 워크로드에 따라 판단하는 절충이 실무에서 흔하다
어느 쪽이든 requests 는 반드시 건다
값을 정하는 순서
- ① 아무것도 모르면 넉넉하게 걸고 시작한다 (limit 미설정보다 낫다)
- ② 실제 사용량을 며칠 관찰한다
- kubectl top pods --sort-by=memory
- 또는 Prometheus 의 container_memory_working_set_bytes
- ③ requests = 평상시 사용량 + 여유
- limits = 피크 사용량 + 여유
- ④ VPA 를 recommendation 모드로 돌려 제안값을 참고한다
- (자동 적용하지 않고 추천만 받는 모드가 있다)
면접 함정
- ❌ "limits만 걸면 된다" → 스케줄링은 requests가 정한다. 없으면 BestEffort다.
- ❌ "CPU limit을 넘으면 죽는다" → 스로틀될 뿐이다. 죽는 것은 메모리다.