
쿠버네티스 운영에서 장애가 “에러율”이 아니라 지연(p95/p99)로 먼저 드러나는 경우가 많습니다. 그 출발점이 의외로 단순한데, 바로 CPU/메모리 Requests/Limits를 어떻게 잡았는지입니다.
이 글은 Kubernetes 공식 문서의 정의를 기준으로, 운영자가 실제로 결정을 내려야 하는 지점(스케줄링, 쓰로틀링, OOMKilled, 노드 압박(eviction), QoS)을 한 장의 의사결정 규칙으로 정리합니다. 추정/허구 없이, 근거는 모두 Kubernetes 공식 문서 링크로만 확정합니다.
TL;DR (운영 의사결정 6줄 요약)
- Requests는 kube-scheduler가 노드에 “올릴 수 있는지”를 판단하는 기준입니다. (스케줄링/용량계획)
- Limits는 kubelet/컨테이너 런타임이 커널 cgroups로 “넘기면 어떻게 되는지”를 결정합니다. (런타임 거동)
- CPU limit은 커널이 throttling으로 강제합니다. (죽지는 않지만 느려질 수 있음)
- Memory limit은 메모리 압박 시 커널의 OOM kill로 종료될 수 있습니다. (재시작 루프 가능)
- QoS(Guaranteed/Burstable/BestEffort)는 노드 자원 압박 시 eviction 우선순위에 영향을 줍니다.
- 실무에서는 “Requests는 보수적으로, CPU limit은 서비스 성격에 따라, Memory limit은 스파이크를 감안”이 기본 출발점입니다.
1) Requests vs Limits: Kubernetes가 실제로 어떻게 쓰는가
Kubernetes 공식 문서에서 Requests/Limits의 역할은 명확히 나뉩니다.
- 컨테이너에 resource request를 지정하면, kube-scheduler가 그 값을 기반으로 파드를 어느 노드에 배치할지 결정합니다.
- resource limit은 kubelet이 컨테이너 런타임에 전달하고, 최종적으로 Linux 커널이 cgroups로 제한을 집행합니다.
운영 관점의 핵심 2가지
- Requests는 “예약”에 가깝고, 노드에 파드를 올릴 수 있는지(스케줄링)와 노드 수 산정에 직접 영향을 줍니다.
- Limits는 “장애 모드”를 결정합니다. CPU는 throttling, 메모리는 OOM kill 가능성이 커집니다.
2) CPU: 왜 limit이 걸리면 “에러 없이 느려지나”
Kubernetes 문서에 따르면 CPU limit은 커널이 CPU throttling으로 집행합니다. 즉, 컨테이너가 limit에 접근하면 커널이 해당 cgroup의 CPU 사용을 제한합니다. (하드 상한)
- 공식 근거: Resource Management for Pods and Containers의 “cpu limits are enforced by CPU throttling” 설명
- 실습/예제 근거: Assign CPU Resources에서 stress 컨테이너가 2 CPU를 쓰려 해도 limit에 의해 throttling 되는 예시
CPU request는 무엇을 의미하나 (공식 문서 기준)
Kubernetes 문서는 CPU request를 “가중치(weighting)”로 설명합니다. 여러 cgroup이 CPU를 원할 때 request가 큰 쪽이 더 많은 CPU 시간을 할당받는 식으로 작동할 수 있습니다.
- 공식 근거: How Kubernetes applies requests and limits 섹션의 “CPU request typically defines a weighting” 설명
3) Memory: limit이 “즉시”가 아니라 “압박 시” 반응적으로 집행되는 이유
메모리 limit은 CPU와 동일하게 “항상 즉시” 강제되는 방식이 아니라, 문서에서 메모리 압박이 감지될 때 OOM kill로 종료될 수 있다고 설명합니다. 따라서 “limit을 넘는 순간 바로 죽는다”라기보다, 운영에서는 스파이크 + 노드 상황에 따라 종료 타이밍이 달라질 수 있다는 점이 중요합니다.
- 공식 근거: Resource Management의 “memory limits are enforced … with out of memory (OOM) kills” 및 “memory limits are enforced reactively” 설명
- 실습/예제 근거: Assign Memory Resources의 OOMKilled 반복 예시
4) QoS(Guaranteed/Burstable/BestEffort): 노드 압박(eviction)에서 ‘누가 먼저 나가나’
Kubernetes는 파드에 대해 QoS 클래스를 부여하고, 이 분류를 노드 자원 압박 시 eviction 의사결정에 활용한다고 문서에 명시합니다.
- 공식 근거: Pod Quality of Service Classes 페이지의 정의 및 eviction 순서 설명
Guaranteed의 요건(공식 문서 그대로)
Guaranteed QoS는 “각 컨테이너의 CPU/메모리 request와 limit이 모두 존재하며, 서로 값이 동일”해야 부여됩니다.
- 공식 근거: Guaranteed criteria
Burstable의 함정: limit을 빼면 ‘노드 용량’이 기본 limit이 될 수 있다
QoS 문서에는 Burstable 파드에서 limit이 없으면 “노드 용량에 해당하는 limit이 기본값이 된다”는 설명이 있습니다. 즉, limit 미지정이 항상 “무제한” 느낌으로만 작동하는 게 아니라, 노드 환경/정책(LimitRange 등)과 결합해 거동이 달라질 수 있습니다.
- 공식 근거: Burstable 설명
5) (필수 보강요소 #1) 운영 체크리스트: 배포 전에 이것만 확인해도 장애가 준다
- Pod가 Pending으로 쌓이는가? → Requests 합이 노드 capacity를 넘는지 확인 (스케줄링은 Requests 기준).
- p95/p99 지연이 튀는데 에러는 없다? → CPU limit throttling 가능성(공식 문서: CPU limit은 throttling).
- OOMKilled 재시작 루프가 있는가? → Memory limit이 스파이크를 못 버티는지 확인 (공식 문서/예제: OOMKilled).
- 노드 압박 시 어떤 파드가 먼저 eviction 되는가? → QoS class(Guaranteed/Burstable/BestEffort) 확인.
- limit/request 미지정이 섞여 있는가? → QoS가 BestEffort로 떨어지거나, Burstable로 예상치 못한 거동을 만들 수 있음.
6) (필수 보강요소 #2) YAML 예시: “정답”이 아니라 ‘의도’를 드러내는 템플릿
아래는 공식 문서의 필드 구조를 따르는 예시입니다. 수치는 환경마다 다르므로, 이 예시는 구조와 의도를 보여주는 용도입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: example.com/api:1.0.0
resources:
requests:
cpu: "500m" # 스케줄링/용량계획의 기준 (kube-scheduler가 사용)
memory: "512Mi" # 스케줄링에서 중요한 기준
limits:
cpu: "1" # 커널이 throttling으로 강제 (지연 민감 서비스면 신중)
memory: "768Mi" # 메모리 압박 시 OOM kill 후보가 될 수 있음
필드 의미와 작동은 Kubernetes 공식 문서의 Requests/Limits 정의와 일치합니다.
7) (필수 보강요소 #3) 트러블슈팅 매트릭스: 증상 → 원인 후보 → 확인 포인트
| 증상 | 공식 문서 기반 원인 후보 | 우선 확인 |
|---|---|---|
| 에러 없이 느려짐(p95/p99 악화) | CPU limit → throttling (커널 강제) | CPU limit throttling 설명 |
| 컨테이너가 OOMKilled로 재시작 | Memory limit 초과 시 OOM kill 후보 | OOMKilled 예제 |
| 파드가 Pending에서 멈춤 | 스케줄러는 Requests 합이 노드 capacity 이하인지 검사 | Insufficient cpu 예제 |
| 노드 압박 시 특정 파드부터 내려감 | QoS class에 따라 eviction 우선순위 영향 | QoS와 eviction |
8) (필수 보강요소 #4) FAQ: 현장에서 자주 나오는 질문 5개
Q1. CPU limit을 꼭 걸어야 하나요?
필수 규칙은 아닙니다. Kubernetes 문서는 CPU limit이 없을 때 “상한이 없거나, 네임스페이스에 default limit이 있으면 자동 할당”될 수 있다고 설명합니다. 즉, 조직 정책(LimitRange)까지 함께 보고 결정하는 게 안전합니다. (공식 근거: CPU 리소스 할당 문서의 “If you do not specify a CPU limit”)
Q2. Memory limit만 걸면 안전해지나요?
반대로, 너무 타이트한 memory limit은 OOMKilled 재시작 루프를 만들 수 있습니다. Kubernetes 문서의 예제처럼 limit을 넘으면 컨테이너가 종료되고(재시작 가능), 반복될 수 있습니다.
공식 문서: Assign Memory Resources
Q3. request만 있고 limit이 없으면 QoS는 어떻게 되나요?
QoS는 request/limit의 조합으로 결정됩니다. Guaranteed는 request=limit이 필수이고, 그렇지 않으면 Burstable/BestEffort 쪽으로 분류됩니다. 정확한 분류 기준은 QoS 공식 문서의 “Criteria”를 기준으로 판단하면 됩니다.
Q4. Requests는 “보수적으로 크게” 잡는 게 맞나요?
Requests를 크게 잡으면 스케줄링이 더 보수적으로 되어 Pending이 늘 수 있고, 작게 잡으면 과밀 스케줄링으로 피크 시 지연이 커질 수 있습니다. 문서가 말하는 핵심은 “스케줄링은 Requests 기준”이라는 점이며, 결국 운영 목표(비용 vs 안정성)에 맞춘 선택입니다.
Q5. Requests/Limits는 어디까지 “쿠버네티스”가 보장하나요?
문서 기준으로, kubelet은 요청/제한을 런타임에 전달하고, Linux에서는 커널 cgroups가 최종 집행합니다. 즉, 동작의 바닥은 커널 레벨에 있습니다.
공식 문서: How Kubernetes applies requests and limits
9) 다음 글 예고 (인접 심화 주제)
- LimitRange/ResourceQuota로 “조직 기본값”을 강제할 때 생기는 운영 패턴
- 프로브(readiness/liveness/startupProbe)와 리소스 조정이 롤아웃 실패를 만드는 메커니즘
참고(공식 문서 원문)
- Kubernetes: Resource Management for Pods and Containers
- Kubernetes: Assign CPU Resources to Containers and Pods
- Kubernetes: Assign Memory Resources to Containers and Pods
- Kubernetes: Pod Quality of Service Classes
CTA: 운영 기준(리소스/QoS/eviction)을 문서로 고정해드릴 수 있습니다
Requests/Limits는 “수치 조정”이 아니라 장애 모드와 비용 구조를 결정하는 설계입니다. 팀 내 기준(예: 지연 민감 서비스는 CPU limit 정책을 어떻게 할지, memory limit headroom을 어떻게 둘지, QoS를 어디까지 Guaranteed로 가져갈지)을 문서로 정리하고, 실제 배포/모니터링 루틴까지 연결하면 운영이 훨씬 단단해집니다.
필요하시면 문의 페이지에 현재 상황(서비스 유형, 장애 증상, 클러스터 규모)을 남겨주세요.