PodDisruptionBudget(PDB)란?
Kubernetes 클러스터를 운영하다 보면 노드 업그레이드, 스케일 다운, 유지보수 등 자발적 중단(voluntary disruption)이 발생합니다. 이때 파드가 한꺼번에 종료되면 서비스 장애로 이어질 수 있습니다. PodDisruptionBudget(PDB)은 자발적 중단 시 동시에 중단될 수 있는 파드 수를 제한하여 가용성을 보장하는 Kubernetes 네이티브 리소스입니다.
PDB는 kubectl drain, Cluster Autoscaler 스케일 다운, Karpenter consolidation 등 자발적 중단에만 적용되며, 노드 장애 같은 비자발적 중단에는 적용되지 않습니다.
PDB의 핵심 파라미터
PDB는 세 가지 핵심 파라미터를 제공합니다. 이 중 minAvailable과 maxUnavailable은 동시에 사용할 수 없습니다.
| 파라미터 | 설명 | 예시 |
|---|---|---|
minAvailable |
중단 중에도 최소 유지해야 할 파드 수 | 3개 중 2 → 1개만 동시 중단 가능 |
maxUnavailable |
동시에 중단 가능한 최대 파드 수 | 3개 중 1 → 1개까지 동시 중단 허용 |
selector |
PDB가 보호할 파드를 선택하는 레이블 셀렉터 | app: api-server |
minAvailable vs maxUnavailable 선택 기준
minAvailable은 “최소 N개는 반드시 살아있어야 한다”는 관점입니다. 레플리카 수가 변해도 최소 보장 수가 고정되므로, 서비스 SLA가 명확할 때 적합합니다. 반면 maxUnavailable은 “최대 N개까지 죽어도 된다”는 관점으로, 롤링 업데이트와 조합할 때 더 직관적입니다.
# minAvailable 방식: 최소 2개 파드 유지
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: api-server
---
# maxUnavailable 방식: 최대 1개까지 중단 허용
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: api-server
퍼센트(%)도 지원됩니다. minAvailable: "50%"는 전체 레플리카의 절반 이상을 유지하라는 의미입니다. 오토스케일링 환경에서 레플리카 수가 유동적일 때 퍼센트 방식이 유리합니다.
실전 시나리오: kubectl drain과 PDB
kubectl drain은 노드의 모든 파드를 축출(evict)합니다. PDB가 설정되어 있으면 Kubernetes API 서버가 eviction 요청을 검증하고, PDB 조건을 위반하는 경우 요청을 거부합니다.
# 노드 유지보수 시나리오
# 1. 노드를 스케줄 불가로 설정
kubectl cordon node-worker-02
# 2. drain 실행 — PDB가 eviction 속도를 제어
kubectl drain node-worker-02 --ignore-daemonsets --delete-emptydir-data
# PDB 위반 시 아래와 같은 메시지 출력:
# error when evicting pods/"api-server-xyz" -n default:
# Cannot evict pod as it would violate the pod's disruption budget.
# 3. 유지보수 완료 후 복구
kubectl uncordon node-worker-02
drain이 PDB 때문에 진행되지 않을 때는 --timeout 플래그로 최대 대기 시간을 설정할 수 있습니다. 타임아웃 이후에도 eviction이 불가능하면 drain이 실패합니다.
unhealthyPodEvictionPolicy: 비정상 파드 처리
Kubernetes 1.27+에서 도입된 unhealthyPodEvictionPolicy 필드는 PDB가 비정상 파드를 어떻게 처리할지 결정합니다. 기존에는 Running 상태가 아닌 파드도 PDB 카운트에 포함되어, 비정상 파드가 drain을 블로킹하는 문제가 있었습니다.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: api-server
# 비정상 파드는 PDB 보호 대상에서 제외
unhealthyPodEvictionPolicy: AlwaysAllow
| 정책 | 동작 | 권장 상황 |
|---|---|---|
IfHealthy (기본값) |
정상 파드만 보호, 비정상 파드는 disruption budget 충족 시에만 evict | 보수적 운영 |
AlwaysAllow |
비정상 파드(CrashLoopBackOff 등)는 PDB 무시하고 즉시 evict | drain 블로킹 방지 |
Deployment RollingUpdate와 PDB 조합
Deployment의 strategy.rollingUpdate와 PDB는 별개의 메커니즘이지만, 함께 설정할 때 주의가 필요합니다. rollingUpdate의 maxUnavailable은 배포 시 중단 허용 수이고, PDB의 maxUnavailable은 eviction 시 중단 허용 수입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 배포 중 최대 1개 중단
maxSurge: 1 # 배포 중 최대 1개 초과 생성
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
# Pod Anti-Affinity: 같은 노드에 파드 집중 방지
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values: ["api-server"]
topologyKey: kubernetes.io/hostname
containers:
- name: api
image: api-server:v2.1.0
resources:
requests:
cpu: 250m
memory: 256Mi
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
maxUnavailable: 1 # eviction 시 최대 1개 중단
selector:
matchLabels:
app: api-server
Pod Anti-Affinity와 함께 사용하면 파드가 여러 노드에 분산되어, 단일 노드 drain 시 영향받는 파드 수를 최소화할 수 있습니다.
StatefulSet과 PDB
StatefulSet 워크로드(데이터베이스, 메시지 큐 등)에서 PDB는 특히 중요합니다. 순서 보장이 필요한 StatefulSet의 특성상, PDB 없이 drain하면 쿼럼(quorum)이 깨질 수 있습니다.
# Redis Cluster 3-replica PDB
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: redis-cluster-pdb
spec:
# 3개 노드 중 최소 2개 유지 → 쿼럼 보장
minAvailable: 2
selector:
matchLabels:
app: redis-cluster
---
# etcd 클러스터 PDB (쿼럼: N/2+1)
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: etcd-pdb
spec:
# 5개 중 최소 3개 유지 (쿼럼 = 3)
minAvailable: 3
selector:
matchLabels:
app: etcd
PDB 모니터링과 트러블슈팅
PDB 상태를 모니터링하여 drain 블로킹 상황을 사전에 감지할 수 있습니다.
# PDB 상태 조회
kubectl get pdb -A
# NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
# api-server-pdb 2 N/A 1 5d
# redis-pdb 2 N/A 0 3d
# ALLOWED DISRUPTIONS가 0이면 현재 eviction 불가!
# 상세 정보 확인
kubectl describe pdb api-server-pdb
# Status:
# Current Healthy: 3
# Desired Healthy: 2
# Disruptions Allowed: 1
# Expected Pods: 3
# Prometheus 메트릭으로 알림 설정
# kube_poddisruptionbudget_status_pod_disruptions_allowed == 0
흔한 트러블슈팅 패턴:
- drain이 영원히 멈춤:
ALLOWED DISRUPTIONS가 0인 PDB 확인 → 레플리카 수 증가 또는 PDB 조건 완화 - PDB 셀렉터 불일치: PDB selector와 파드 레이블이 다르면 PDB가 작동하지 않음
- 단일 레플리카 + minAvailable: 1: 절대 evict 불가 → 운영 환경에서 레플리카 2+ 권장
Cluster Autoscaler·Karpenter와 PDB
Cluster Autoscaler와 Karpenter 모두 노드 스케일 다운 시 PDB를 존중합니다. PDB 조건이 충족되지 않으면 해당 노드의 스케일 다운이 지연됩니다.
# Karpenter NodePool + PDB 조합 예시
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 30s
# Karpenter는 consolidation 시 PDB를 자동 확인
# PDB 위반 파드가 있는 노드는 consolidation 대상에서 제외
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand", "spot"]
Spot 인스턴스 회수는 비자발적 중단이므로 PDB가 적용되지 않습니다. Spot 환경에서는 PDB와 함께 Pod Topology Spread Constraints로 AZ 분산을 병행해야 합니다.
운영 베스트 프랙티스
- 모든 프로덕션 워크로드에 PDB 설정: Deployment, StatefulSet 모두 PDB가 있어야 안전한 drain이 가능합니다
- 퍼센트 기반 PDB 선호: HPA로 레플리카가 변동하는 환경에서는
maxUnavailable: "20%"같은 퍼센트 방식이 유연합니다 - Helm 차트에 PDB 포함: 배포 템플릿에 PDB를 기본 포함시켜 누락을 방지합니다
- CI/CD 파이프라인에서 PDB 검증: OPA/Kyverno 정책으로 PDB 없는 Deployment 배포를 차단합니다
- 단일 레플리카 워크로드 주의: replicas: 1 + minAvailable: 1은 drain을 영구 블로킹합니다
# Kyverno 정책: PDB 없는 Deployment 차단
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-pdb
spec:
validationFailureAction: Enforce
rules:
- name: check-pdb-exists
match:
any:
- resources:
kinds: ["Deployment"]
namespaces: ["production"]
preconditions:
all:
- key: "{{request.object.spec.replicas}}"
operator: GreaterThan
value: 1
validate:
message: "Production Deployments with 2+ replicas must have a PDB."
deny:
conditions:
all:
- key: "{{request.object.metadata.labels.pdb-configured}}"
operator: NotEquals
value: "true"
PodDisruptionBudget은 설정이 간단하지만, 클러스터 운영의 안정성을 크게 높여주는 핵심 리소스입니다. 노드 유지보수, 오토스케일링, 클러스터 업그레이드 등 모든 자발적 중단 시나리오에서 서비스 가용성을 보장하는 첫 번째 방어선으로 활용하세요.