왜 문제가 되는가
노드 자원이 부족해지면 새 파드는 Pending 상태로 멈춘다. 이때 모든 파드를 동등하게 취급하면, 배치 잡이 노드를 선점하고 있어서 결제·인증 같은 핵심 서비스가 스케줄되지 못하는 상황이 발생한다. 쿠버네티스는 이 문제를 우선순위(Priority)와 preemption(선점)으로 해결한다. 높은 우선순위 파드가 스케줄될 자리가 없으면, 스케줄러가 낮은 우선순위 파드를 축출(evict)해 자리를 만든다.
PriorityClass 정의
먼저 우선순위 등급을 클러스터 리소스로 선언한다. value가 클수록 우선순위가 높다. globalDefault: true인 클래스는 우선순위를 지정하지 않은 파드에 자동 적용된다(클러스터당 하나만).
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: critical-service
value: 1000000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: "결제/인증 등 축출되면 안 되는 워크로드"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: batch-low
value: 100
preemptionPolicy: Never
description: "선점 대상이 될 수 있는 배치 잡"
파드에서는 priorityClassName으로 연결한다. 지정 시 스케줄러가 spec.priority 필드를 자동으로 채운다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-api
spec:
replicas: 3
selector: { matchLabels: { app: payment-api } }
template:
metadata:
labels: { app: payment-api }
spec:
priorityClassName: critical-service
containers:
- name: api
image: registry.internal/payment:1.4.2
resources:
requests: { cpu: "500m", memory: "512Mi" }
preemptionPolicy의 두 얼굴
preemptionPolicy는 "이 파드가 남을 선점하는가"를 결정한다. 우선순위 값(value)이 스케줄 순서에 쓰이는 것과는 별개다.
| preemptionPolicy | 스케줄 우선권 | 남을 축출 | 용도 |
|---|---|---|---|
| PreemptLowerPriority(기본) | 있음 | 함 | 핵심 서비스 |
| Never | 있음(대기열 앞) | 안 함 | 먼저 스케줄되되 남을 죽이진 않는 잡 |
Never는 우선순위 큐에서 앞자리를 얻지만 다른 파드를 축출하진 않는다. 배치 잡에 유용하다.
선점이 실제로 일어나는 순서
스케줄러는 고우선 파드가 Pending일 때 노드별로 "이 노드에서 나보다 낮은 파드 몇 개를 지우면 스케줄 가능한가"를 계산한다. 후보가 정해지면 해당 파드에 삭제 요청을 보내고, terminationGracePeriodSeconds 동안 graceful 종료를 기다린다. 이 유예 시간 때문에 선점은 즉시 완료되지 않는다. 축출된 파드가 Deployment 소속이면 컨트롤러가 다시 스케줄을 시도한다.
주의점: PDB와 유예 시간
선점은 PodDisruptionBudget(PDB)을 best-effort로만 존중한다. 다른 노드에서 자리를 못 찾으면 PDB를 위반하면서까지 축출할 수 있다. 따라서 PDB만 믿고 가용성을 보장하면 안 된다. 또한 축출 대상이 긴 terminationGracePeriodSeconds를 가지면 선점 지연이 커진다. 다음 명령으로 선점 이벤트를 확인한다.
# Pending 파드의 선점 시도 이벤트 확인
kubectl describe pod payment-api-xxxx | grep -A3 Preempt
# 최근 축출/선점 이벤트만 필터
kubectl get events --field-selector reason=Preempting \
--sort-by=.lastTimestamp -A
흔한 함정
- DaemonSet 파드는 기본 스케줄러가 아닌 경우가 많아 선점 계산에서 예외 처리에 주의해야 한다.
- 노드 자원 자체가 부족하면 선점으로도 해결되지 않는다. 선점은 우선순위 재배치일 뿐 용량을 늘리진 않는다.
- 모든 워크로드에 높은
value를 부여하면 우선순위 체계가 무력화된다. 등급은 3~4개로 제한하라. - 선점과 노드 오토스케일러를 함께 쓰면, 오토스케일러가 노드를 늘리기 전에 선점이 먼저 발생할 수 있다. 배치 잡에는
Never를 주어 불필요한 축출을 막는 편이 안전하다.
정리
PriorityClass로 워크로드 등급을 명시하고, 핵심 서비스에는 기본 선점 정책을, 배치성 워크로드에는 낮은 값과 Never를 부여하는 것이 기본 골격이다. 선점은 PDB와 유예 시간을 완전히 보장하지 않으므로, 가용성은 노드 용량과 anti-affinity로 별도 확보해야 한다.