PodDisruptionBudget이란?
Kubernetes 클러스터를 운영하면 노드 업그레이드, 스케일 다운, 커널 패치 등 자발적 중단(Voluntary Disruption)이 빈번하게 발생한다. 이때 Pod가 무분별하게 종료되면 서비스 가용성이 떨어진다. PodDisruptionBudget(PDB)은 동시에 중단될 수 있는 Pod 수를 제한하여 가용성을 보장하는 리소스다.
PDB가 없으면 kubectl drain 한 번에 모든 Pod가 한꺼번에 축출(evict)될 수 있다. PDB를 설정하면 Kubernetes API 서버가 축출 요청을 거부하여 최소 가용 Pod 수를 유지한다.
PDB 핵심 필드: minAvailable vs maxUnavailable
PDB는 두 가지 방식으로 가용성 기준을 설정한다. 둘 중 하나만 사용할 수 있다.
# 방식 1: minAvailable — 최소 유지 Pod 수
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
minAvailable: 2 # 항상 최소 2개 Pod 유지
selector:
matchLabels:
app: api-server
---
# 방식 2: maxUnavailable — 최대 중단 허용 수
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: worker-pdb
spec:
maxUnavailable: 1 # 동시에 최대 1개만 중단 허용
selector:
matchLabels:
app: worker
| 필드 | 의미 | 절대값 | 백분율 |
|---|---|---|---|
| minAvailable | 최소 유지해야 할 Pod 수 | minAvailable: 3 |
minAvailable: "80%" |
| maxUnavailable | 동시 중단 허용 Pod 수 | maxUnavailable: 1 |
maxUnavailable: "25%" |
프로덕션 권장: maxUnavailable이 더 유연하다. minAvailable은 스케일 업/다운 시 계산이 복잡해지고, replica 수 변경 시 PDB도 함께 수정해야 하는 번거로움이 있다.
자발적 중단 vs 비자발적 중단
PDB는 자발적 중단만 제어한다는 점이 중요하다. 노드 하드웨어 장애 같은 비자발적 중단에는 PDB가 적용되지 않는다.
| 자발적 중단 (PDB 적용) | 비자발적 중단 (PDB 미적용) |
|---|---|
kubectl drain |
노드 하드웨어 장애 |
| Cluster Autoscaler 스케일 다운 | 커널 패닉 |
| 노드 업그레이드 (cordon + drain) | OOM Kill |
| Spot/Preemptible 인스턴스 회수 | Pod 직접 삭제 (kubectl delete pod) |
kubectl delete pod는 Eviction API를 거치지 않으므로 PDB를 우회한다. 프로덕션에서 Pod를 안전하게 제거하려면 kubectl drain을 사용해야 한다.
실전: Deployment + PDB 조합
3-replica Deployment에 PDB를 적용하는 전형적인 패턴이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-api
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # PDB와 함께 사용 시 0 권장
selector:
matchLabels:
app: payment-api
template:
metadata:
labels:
app: payment-api
spec:
topologySpreadConstraints: # 노드 분산 필수
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: payment-api
containers:
- name: api
image: payment-api:1.5.0
readinessProbe:
httpGet:
path: /health
port: 8080
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: payment-api-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: payment-api
topologySpreadConstraints로 Pod를 여러 노드에 분산 배치하는 것이 PDB 효과를 극대화하는 핵심이다. 모든 Pod가 같은 노드에 있으면 해당 노드 drain 시 PDB가 축출을 막아 drain이 영원히 완료되지 않는 상황이 발생한다.
PDB 상태 확인과 모니터링
# PDB 상태 확인
$ kubectl get pdb
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
payment-api-pdb N/A 1 1 5d
# 상세 정보
$ kubectl describe pdb payment-api-pdb
Status:
Current Healthy: 3
Desired Healthy: 2
Disruptions Allowed: 1
Expected Pods: 3
핵심 지표는 Disruptions Allowed다. 이 값이 0이면 추가 축출이 불가능한 상태이며, kubectl drain이 대기(pending) 상태로 멈춘다.
# Prometheus 메트릭으로 모니터링
kube_poddisruptionbudget_status_current_healthy
kube_poddisruptionbudget_status_desired_healthy
kube_poddisruptionbudget_status_pod_disruptions_allowed
# AlertManager 알림 규칙
- alert: PDBDisruptionsAllowedZero
expr: kube_poddisruptionbudget_status_pod_disruptions_allowed == 0
for: 10m
labels:
severity: warning
annotations:
summary: "PDB {{ $labels.poddisruptionbudget }}의 허용 중단이 0입니다"
흔한 실수와 안티패턴
1. minAvailable = replicas (데드락)
# ❌ replicas와 minAvailable이 같으면 drain 불가
spec:
replicas: 3
---
spec:
minAvailable: 3 # 절대 축출 불가 → drain 영원히 대기
2. maxUnavailable: 0 (마찬가지로 데드락)
# ❌ 중단 허용이 0이면 아무 Pod도 축출 불가
spec:
maxUnavailable: 0
3. selector 불일치
# ❌ PDB selector가 Deployment label과 불일치 → PDB 무효
# Deployment: app: payment-api
# PDB: app: payment ← 오타!
PDB selector가 어떤 Pod도 매치하지 않으면 조용히 무시된다. kubectl get pdb에서 Expected Pods: 0이 보이면 selector를 점검해야 한다.
Cluster Autoscaler와의 상호작용
리소스 Right-Sizing과 함께 Cluster Autoscaler를 운영할 때 PDB는 스케일 다운 결정에 직접 영향을 미친다.
# Cluster Autoscaler의 PDB 관련 동작:
# 1. 노드 스케일 다운 후보 선정
# 2. 해당 노드의 모든 Pod에 대해 PDB 확인
# 3. PDB 위반 시 → 해당 노드 스케일 다운 건너뜀
# 4. PDB 충족 시 → Pod 축출 후 노드 제거
# 강제 스케일 다운이 필요하면 (비권장):
cluster-autoscaler --skip-nodes-with-system-pods=false
# 또는 annotation으로 특정 노드 제외:
cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
K8s 1.27+: Unhealthy Pod Eviction Policy
Kubernetes 1.27부터 PDB에 unhealthyPodEvictionPolicy 필드가 추가되었다. 기본값은 IfHealthy로, 비정상 Pod가 있어도 PDB를 존중한다. AlwaysAllow로 설정하면 비정상 Pod는 PDB를 무시하고 즉시 축출할 수 있다.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
maxUnavailable: 1
unhealthyPodEvictionPolicy: AlwaysAllow # 비정상 Pod는 즉시 축출
selector:
matchLabels:
app: api-server
이 설정은 CrashLoopBackOff 상태의 Pod가 PDB 예산을 소진하여 정상적인 운영을 방해하는 문제를 해결한다.
정리
PodDisruptionBudget은 프로덕션 K8s 클러스터에서 필수 리소스다. maxUnavailable: 1을 기본으로 설정하고, topologySpreadConstraints로 노드 분산을 보장하면 노드 유지보수 중에도 서비스 가용성을 유지할 수 있다. minAvailable=replicas 데드락, selector 불일치, Cluster Autoscaler 상호작용을 이해하고, K8s 1.27+에서는 unhealthyPodEvictionPolicy: AlwaysAllow를 활용하면 더욱 안정적인 클러스터를 운영할 수 있다.