K8s PDB 가용성 보장 전략

PodDisruptionBudget(PDB)란?

Kubernetes 클러스터를 운영하다 보면 노드 업그레이드, 스케일 다운, 유지보수 등 자발적 중단(voluntary disruption)이 발생합니다. 이때 파드가 한꺼번에 종료되면 서비스 장애로 이어질 수 있습니다. PodDisruptionBudget(PDB)은 자발적 중단 시 동시에 중단될 수 있는 파드 수를 제한하여 가용성을 보장하는 Kubernetes 네이티브 리소스입니다.

PDB는 kubectl drain, Cluster Autoscaler 스케일 다운, Karpenter consolidation 등 자발적 중단에만 적용되며, 노드 장애 같은 비자발적 중단에는 적용되지 않습니다.

PDB의 핵심 파라미터

PDB는 세 가지 핵심 파라미터를 제공합니다. 이 중 minAvailablemaxUnavailable은 동시에 사용할 수 없습니다.

파라미터 설명 예시
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의 maxUnavailableeviction 시 중단 허용 수입니다.

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은 설정이 간단하지만, 클러스터 운영의 안정성을 크게 높여주는 핵심 리소스입니다. 노드 유지보수, 오토스케일링, 클러스터 업그레이드 등 모든 자발적 중단 시나리오에서 서비스 가용성을 보장하는 첫 번째 방어선으로 활용하세요.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux