학습 목표
requests·limits가 스케줄링과 런타임에서 각각 어떤 역할을 하는지 설명할 수 있다.
Guaranteed·Burstable·BestEffort QoS class 기준과 eviction 우선순위를 설명할 수 있다.
nodeSelector·affinity·taint/toleration으로 Pod 배치를 제어할 수 있다.
HPA가 metrics를 보고 replicas를 조절하는 흐름을 설명할 수 있다.
문제 상황
- Pod가 Pending인데
Events에Insufficient cpu만 반복된다- 노드 allocatable 대비 requests 합이 부족
- 한 노드에 Pod가 몰려 한 VM 장애 시 서비스 전체가 내려간다
- anti-affinity·노드 분산이 필요
- CPU limit 없이 배포했더니 noisy neighbor가 다른 Pod를 굶긴다
- limits·QoS 미설정
- memory limit 없이 OOM이 나면 어떤 Pod가 죽을지 예측 불가
- limits 없으면 BestEffort — eviction 최우선
- 트래픽 급증에 수동으로
kubectl scale만 반복한다- HPA + metrics-server 자동 스케일
- GPU·SSD 노드에 워크로드를 강제 배치하고 싶다
- nodeSelector·node affinity·taint
앞서 StatefulSet·스토리지를 봤다. 이번 편은 어느 노드에·얼마나 Pod를 올릴지, 부하에 따라 몇 개 둘지다. Part 2 Kubernetes 마지막 편이다.
1. 스케줄러와 리소스 모델
kube-scheduler — Pending Pod를 적합한 노드 하나에 바인딩.
Pending Pod → Filter (필수 조건) → Score (선호) → Bind → kubelet 시작
| 단계 | 예시 |
|---|---|
| Filter | requests 합 ≤ 노드 여유, nodeSelector, taint/toleration |
| Score | affinity 가중치, 리소스 균형, spread |
| Bind | Pod → Node 할당 (API server 기록) |
- kubelet — 바인딩된 노드에서 컨테이너 실행·limits 집행
- allocatable — 노드 전체 CPU/mem − 시스템·kube-reserved
kubectl describe node— Allocated resources로 requests/limit 합 확인
2. requests와 limits

| requests | limits | |
|---|---|---|
| 목적 | 스케줄링 — 노드에 “예약” | 런타임 — 상한 |
| CPU | 스케줄 가능 여부 | CFS quota — 초과 시 throttle |
| memory | 스케줄 가능 여부 | 초과 시 OOMKilled |
| 미설정 | 0 (BestEffort 쪽) | 컨테이너 default (노드 한도까지) |
resources:
requests:
cpu: "500m" # 0.5 core
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
| 단위 | CPU | memory |
|---|---|---|
| 표기 | 500m = 0.5 core, 1 = 1 core |
512Mi, 1Gi |
| 정수 | 1 = 1 core | Ki, Mi, Gi (2진) |
- requests ≤ limits — 일반적 (limits만 크게 잡는 패턴도 있음)
- Burstable — requests < limits (유휴 CPU 빌려 씀, limit까지 burst)
- limit = request — CPU/mem Guaranteed에 가깝게
- Java·Go 앱 — heap·GC 고려해 memory limit 여유 (OOM 방지)
kubectl top pods
kubectl describe node | grep -A5 "Allocated resources"
3. QoS Class
스케줄러가 아니라 kubelet이 Pod spec으로 QoS를 자동 분류 — eviction·OOM 우선순위에 영향.

| QoS | 조건 | eviction |
|---|---|---|
| Guaranteed | 모든 컨테이너: limits = requests (cpu·mem 둘 다) | 마지막 |
| Burstable | requests 설정, Guaranteed 아님 | 중간 |
| BestEffort | requests·limits 없음 | 최우선 |
Guaranteed — limits == requests (all containers, both cpu & mem)
Burstable — some requests set
BestEffort — no requests/limits
- 노드 메모리 압박 — kubelet이 BestEffort → Burstable → Guaranteed 순 evict
- 프로덕션 — 최소 requests 설정, 핵심 워크로드는 Guaranteed 검토
- LimitRange — namespace 기본 min/max/default requests·limits
- ResourceQuota — namespace 총 requests/limits 상한
4. nodeSelector와 affinity
nodeSelector — 노드 label 필수 매칭 (단순, legacy).
nodeSelector:
disktype: ssd
node.kubernetes.io/instance-type: m5.large
affinity — required vs preferred, Pod 간 co-location / anti-affinity.

| 종류 | 키 | 의미 |
|---|---|---|
| nodeAffinity | requiredDuringScheduling... |
반드시 만족 (Filter) |
| nodeAffinity | preferredDuringScheduling... |
가능하면 (Score) |
| podAffinity | 같은 topology에 함께 | 캐시·sidecar colocate |
| podAntiAffinity | 같은 topology에 분산 | AZ·노드 장애 격리 |
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: api
topologyKey: kubernetes.io/hostname
| topologyKey | 효과 |
|---|---|
kubernetes.io/hostname |
노드당 Pod 분산 |
topology.kubernetes.io/zone |
AZ 분산 |
IgnoredDuringExecution— 스케줄 후 label 변경해도 추방하지 않음RequiredDuringScheduling— 조건 깨지면 Pending (과도한 required는 주의)
5. taint와 toleration
taint — 노드에 “이 Pod는 받지 않음” 표시 (전용 노드·GPU·system).
toleration — Pod가 taint를 허용 — 스케줄 가능.
# Node (예시)
# kubectl taint nodes node1 dedicated=api:NoSchedule
tolerations:
- key: dedicated
operator: Equal
value: api
effect: NoSchedule
| effect | 동작 |
|---|---|
| NoSchedule | 새 Pod 스케줄 거부 (toleration 없으면) |
| PreferNoSchedule | 가급적 거부 |
| NoExecute | 기존 Pod도 추방 (toleration + tolerationSeconds) |
- 전용 노드 풀 — taint
dedicated=workload+ 해당 toleration만 배치 - system node — 기본 taint로 일반 워크로드 차단
6. HPA (Horizontal Pod Autoscaler)
HPA — Deployment·StatefulSet 등의 replicas를 메트릭에 맞춰 조절.

metrics-server (CPU/mem) → HPA controller → scale Deployment replicas
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
| 항목 | 설명 |
|---|---|
| metrics-server | kubelet summary API → 클러스터 메트릭 (HPA 필수) |
| averageUtilization | Pod requests cpu 대비 사용률 (requests 없으면 동작 불안) |
| min/maxReplicas | floor·ceiling — 비용·용량 한도 |
| scale down | cooldown·stabilization — flapping 방지 |
kubectl apply -f code/scheduling-resources.yaml
kubectl get hpa
kubectl describe hpa api
- Custom metrics — Prometheus adapter, QPS·queue depth 기반 스케일 (Observability 연동)
- VPA — Pod requests 자동 조정 (HPA와 동시 사용 시 주의)
- Cluster Autoscaler — 노드 풀 확장 (Pending + requests 합 기준)
7. 실무 체크리스트
| 증상 | 확인 |
|---|---|
| Pending | describe pod Events, node requests 여유, PVC·affinity |
| OOMKilled | memory limits, JVM -Xmx, QoS |
| CPU throttle | limits vs 실제 사용, kubectl top |
| 한 노드 몰림 | podAntiAffinity, topology spread |
| HPA Unknown | metrics-server, requests.cpu 설정 |
| 워크로드 | requests/limits | 스케줄링 | 스케일 |
|---|---|---|---|
| Stateless API | cpu/mem 설정 | anti-affinity | HPA |
| Batch Job | burst 허용 limits | spot/taint 노드 | Job parallelism |
| StatefulSet DB | Guaranteed 검토 | RWO + AZ spread | 수동 또는 operator |
| DaemonSet | node 전체 고려 | 모든 노드 | replicas = node 수 |
8. 정리
- requests — 스케줄 · limits — 런타임 상한 (CPU throttle, mem OOM)
- QoS — Guaranteed / Burstable / BestEffort → eviction 순서
- nodeSelector·affinity·taint — 어디에 배치할지
- HPA — metrics → replicas (requests·metrics-server 전제)
- Part 2 Kubernetes 핵심 오브젝트 흐름: Pod → Deployment → Service → Config → Storage → Scheduling
다음에 다룰 것
- CI/CD 개요
- build·test·deploy 파이프라인, CI vs CD
해당 내용은 Kubernetes Documentation (kubernetes.io) 의 내용을 기반으로 합니다.
'플랫폼, 데브옵스와 클라우드' 카테고리의 다른 글
| GitHub Actions와 GitLab CI (0) | 2026.06.29 |
|---|---|
| CI/CD 개요 (0) | 2026.06.28 |
| 스토리지와 StatefulSet (0) | 2026.06.26 |
| ConfigMap과 Secret (0) | 2026.06.25 |
| Service와 Ingress (0) | 2026.06.24 |