infra
Platform

모듈 맵

[Kubernetes] PDB(PodDisruptionBudget) 설정으로 가용성 지키며 드레인하기

0 / 29 완료

펼치기
0 / 29 완료0%

Kubernetes · 18 / 29

[Kubernetes] PDB(PodDisruptionBudget) 설정으로 가용성 지키며 드레인하기

PDB로 노드 드레인 시 최소 가용 파드 수를 보장하여 유지보수 중에도 서비스 가용성을 유지합니다

🚨INCIDENT ALERT
HIGH

노드 패치를 위해 kubectl drain을 실행했더니 결제 파드가 동시에 내려가 서비스가 끊겼습니다. 계획된 유지보수라도 최소 가용 파드 수를 지키지 않으면 장애와 다르지 않습니다. PDB는 운영 작업 중에도 서비스 가용성을 지키도록 Eviction을 제어합니다.

PodDisruptionBudget — 무중단 노드 유지보수

새벽 2시, 클러스터 노드 패치를 위해 kubectl drain node-3을 실행했습니다. 잠시 후 슬랙 알림이 울립니다. "결제 서비스 다운, 주문 처리 불가." 나중에 확인해보니 결제 파드가 node-3에 2개, 다른 노드에 1개 있었는데 드레인으로 node-3의 파드 2개가 동시에 제거되면서 남은 파드 1개가 트래픽을 감당 못하고 OOM으로 죽은 것이었습니다. PodDisruptionBudget(PDB)는 이런 상황을 막기 위한 안전장치입니다. "이 서비스는 최소 2개의 파드가 항상 실행 중이어야 한다"고 클러스터에 선언하면, 노드 드레인이나 클러스터 업그레이드 같은 계획된 중단(voluntary disruption) 시 K8s가 이 조건을 지키면서 파드를 이동시킵니다.

이번 챕터에서 배울 것
  • 1Voluntary Disruption vs Involuntary Disruption 차이
  • 2minAvailable과 maxUnavailable 설정 방식
  • 3PDB가 kubectl drain에 미치는 영향
  • 4퍼센트(%) 표기로 동적 보호 설정
  • 5PDB 상태 모니터링과 드레인 진행 추적
  • 6무중단 클러스터 업그레이드 체크리스트
실습 환경 준비
현재 PDB 확인
kubectl get pdb -A
실습 네임스페이스 생성
kubectl create namespace pdb-demo
테스트 Deployment 배포
kubectl create deployment web --image=nginx --replicas=3 -n pdb-demo
노드 목록 확인
kubectl get nodes

Voluntary vs Involuntary Disruption

PDB는 **계획된 중단(Voluntary Disruption)**에만 적용됩니다. 노드 장애로 파드가 죽는 것(Involuntary)은 PDB가 막을 수 없습니다.

구분예시PDB 적용
Voluntary (계획됨)kubectl drain, 클러스터 업그레이드, kubectl delete pod적용됨
Involuntary (비계획)노드 OOM, 하드웨어 장애, 커널 패닉적용 안 됨
💡개념

minAvailable vs maxUnavailable 설정

결제 서비스 파드가 3개 실행 중인데 노드 드레인 중 2개가 동시에 evict되면 남은 1개가 모든 트래픽을 받다 OOM으로 죽을 수 있습니다. PDB는 "이 서비스는 항상 최소 N개가 살아있어야 한다"는 제약을 클러스터에 선언하는 리소스입니다. 드레인이나 업그레이드 같은 계획된 중단 상황에서 Kubernetes가 이 조건을 지키면서 파드를 이동시킵니다. minAvailable과 maxUnavailable 두 가지 방식으로 보호 수준을 표현할 수 있으며, 서비스 특성에 따라 적합한 쪽을 선택합니다.

두 설정 모두 절대값과 퍼센트(%) 표기를 지원합니다.

minAvailable vs maxUnavailable 설정

YAML
# minAvailable: 절대값 — 항상 최소 2개 실행 보장
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb-min
  namespace: pdb-demo
spec:
  minAvailable: 2        # 절대값: 최소 2개
  selector:
    matchLabels:
      app: web
---
# minAvailable: 퍼센트 — 전체의 66% 이상 실행 보장
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb-percent
  namespace: pdb-demo
spec:
  minAvailable: "66%"    # 3개 중 2개 (66.6% → 올림)
  selector:
    matchLabels:
      app: web
---
# maxUnavailable: 한 번에 최대 1개만 중단 허용
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb-max
  namespace: pdb-demo
spec:
  maxUnavailable: 1      # 동시에 최대 1개까지 중단 가능
  selector:
    matchLabels:
      app: web
Kubernetes
kubectl apply -f web-pdb-min.yaml

# PDB 상태 확인
kubectl get pdb -n pdb-demo
# NAME          MIN AVAILABLE  MAX UNAVAILABLE  ALLOWED DISRUPTIONS  AGE
# web-pdb-min   2              N/A              1                    30s
#                                               ↑ 현재 3개 중 1개까지 내릴 수 있음

kubectl describe pdb web-pdb-min -n pdb-demo
# Status:
#   Observed Generation:  1
#   Disruptions Allowed:  1   ← 현재 Evict 가능한 파드 수
#   Current Healthy:      3
#   Desired Healthy:      2   (= minAvailable)
#   Expected Pods:        3
🔍실행 후 확인할 것
  • NAME조회 대상 리소스 이름이 예상한 대상과 일치하는지 확인합니다.
  • STATUS/READYRunning, Ready, Available처럼 정상 상태를 나타내는 필드가 있는지 봅니다.
  • RESTARTS/EVENTS재시작 횟수나 Warning 이벤트가 증가하지 않는지 확인합니다.
💡개념

PDB와 kubectl drain의 상호작용

노드 유지보수를 위해 kubectl drain을 실행했는데 파드가 예상보다 빠르게 모두 내려가면서 서비스가 중단된 경험이 있다면, PDB 설정이 없었기 때문일 가능성이 높습니다. PDB가 설정된 환경에서 drain은 단순히 파드를 삭제하지 않고 Eviction API를 통해 PDB 조건을 확인하면서 순서대로 파드를 옮깁니다. 이 동작을 이해하면 drain이 멈추는 이유, 언제 다음 파드를 내릴 수 있는지를 정확히 예측할 수 있습니다.

kubectl drain은 내부적으로 Eviction API를 호출합니다. PDB 위반 시 429 Too Many Requests를 반환하고, drain은 이를 받아 대기합니다.

위험 명령어노드의 기존 Pod가 Eviction되어 PDB가 없거나 복제본이 부족한 서비스는 중단될 수 있습니다.

노드 드레인

안전한 실행 조건: PDB, 복제본 수, 다른 노드의 여유 리소스를 확인한 유지보수 상황에서만 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl drain node-3

위 항목을 모두 확인한 후 복사할 수 있습니다

Kubernetes
# PDB가 있는 상태에서 노드 드레인
kubectl drain node-3 \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --timeout=5m

# 드레인 진행 출력 예시:
# evicting pod pdb-demo/web-6d8b4b5c9-abcde
# evicting pod pdb-demo/web-6d8b4b5c9-fghij
# error when evicting pods/"web-6d8b4b5c9-fghij" -n "pdb-demo"
#   (will retry after 5s): Cannot evict pod as it would violate
#   the pod's disruption budget.
# pod/web-6d8b4b5c9-abcde evicted    ← 첫 번째 파드 성공적으로 evict
# waiting for eviction of pod/web-6d8b4b5c9-fghij...  ← PDB 대기 중

# 다른 터미널에서 파드 재배치 확인
kubectl get pods -n pdb-demo -w
# web-6d8b4b5c9-abcde   0/1  Terminating  → node-1로 재배치
# web-6d8b4b5c9-xyzab   0/1  Pending      → Running 후 두 번째 drain 진행
로컬 터미널
# PDB 상태 실시간 확인 (드레인 진행 중)
watch kubectl get pdb -n pdb-demo
# NAME          MIN AVAILABLE  ALLOWED DISRUPTIONS  AGE
# web-pdb-min   2              0                    2m   ← 0이면 현재 drainable 없음
# web-pdb-min   2              1                    2m   ← 재배치 완료 후 1로 늘어남

실습: PDB 없을 때와 있을 때 비교

위험 명령어노드의 기존 Pod가 Eviction되어 PDB가 없거나 복제본이 부족한 서비스는 중단될 수 있습니다.

노드 드레인

안전한 실행 조건: PDB, 복제본 수, 다른 노드의 여유 리소스를 확인한 유지보수 상황에서만 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl drain node-2

위 항목을 모두 확인한 후 복사할 수 있습니다

Kubernetes
# 1. PDB 없이 드레인 시뮬레이션
kubectl create deployment payment \
  --image=nginx --replicas=2 -n pdb-demo

# 파드 2개가 같은 노드에 있는 경우를 가정
kubectl get pods -n pdb-demo -o wide | grep payment
# payment-xxx  Running  node-2
# payment-yyy  Running  node-2

# PDB 없이 node-2 드레인 → 파드 2개 동시 제거 → 서비스 다운
kubectl drain node-2 --ignore-daemonsets --delete-emptydir-data --dry-run
# (실제로는 실행 않고 dry-run만)

# 2. PDB 추가 후 동일한 상황
cat <<EOF | kubectl apply -f -
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: payment-pdb
  namespace: pdb-demo
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: payment
EOF

# 이제 drain 시도 → 1개 Evict 후 재배치 완료될 때까지 대기
kubectl drain node-2 \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --timeout=3m

# 드레인 완료 후 파드 분산 확인
kubectl get pods -n pdb-demo -o wide | grep payment
# payment-xxx  Running  node-1   ← 재배치됨
# payment-yyy  Running  node-3   ← 재배치됨
Kubernetes
# 3. PDB Disruptions Allowed 변화 모니터링
kubectl get pdb payment-pdb -n pdb-demo -w
# NAME          MIN AVAILABLE  ALLOWED DISRUPTIONS
# payment-pdb   1              1   ← 드레인 전
# payment-pdb   1              0   ← 첫 파드 Evict 중
# payment-pdb   1              1   ← 재배치 완료 후 복구

클러스터 보안 패치를 위해 노드를 순서대로 드레인했습니다. node-2를 드레인하는 순간 API 에러율이 급상승하고 결제 서비스가 15초간 다운되었습니다.

Kubernetes
# 사후 분석
kubectl get events -n production --sort-by='.lastTimestamp' | tail -20
# Warning  Killing  payment-pod-abc  Stopping container payment
# Warning  Killing  payment-pod-def  Stopping container payment
# ← 두 파드가 거의 동시에 삭제됨

# PDB 확인
kubectl get pdb -n production
# (출력 없음 — PDB 미설정)

# 파드 배치 분산 여부 확인
kubectl get pods -n production -o wide | grep payment
# payment-abc  node-2   ← 두 파드가 같은 노드에 있었음
# payment-def  node-2

# 대응 1: PDB 즉시 추가 (다음 유지보수 전에)
cat <<EOF | kubectl apply -f -
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: payment-pdb
  namespace: production
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: payment
EOF

# 대응 2: 파드 반분산 (PodAntiAffinity) 추가
# Deployment spec.template.spec에 추가:
# affinity:
#   podAntiAffinity:
#     preferredDuringSchedulingIgnoredDuringExecution:
#       - weight: 100
#         podAffinityTerm:
#           labelSelector:
#             matchLabels:
#               app: payment
#           topologyKey: kubernetes.io/hostname

# 대응 3: 드레인 전 PDB 상태 확인을 SOP에 추가
kubectl get pdb -A | grep -v "N/A"   # ALLOWED DISRUPTIONS가 0인 항목 확인

예방 체크리스트:

  1. 모든 프로덕션 Deployment에 PDB 설정 확인: kubectl get pdb -n production
  2. PDB의 ALLOWED DISRUPTIONS가 0이면 드레인 전에 조건 확인
  3. --timeout 옵션으로 드레인 대기 시간 제한 설정
  4. 드레인 후 파드 재배치 완료 확인 후 다음 노드로 진행
💼
실무 맥락
현업 패턴

시나리오: 클러스터 버전 업그레이드 전 PDB 점검

EKS를 1.28에서 1.29로 업그레이드하기 전, SRE 온콜이 점검 항목 리스트를 줬습니다. "PDB 없는 프로덕션 서비스 목록 뽑아서 팀에 공유해줘."

Kubernetes
# 프로덕션 네임스페이스의 Deployment 목록
kubectl get deployments -n production -o json | \
  jq '.items[].metadata.name' -r | sort > /tmp/deployments.txt

# PDB의 selector와 매칭되는 Deployment 추출
kubectl get pdb -n production -o json | \
  jq '.items[].spec.selector.matchLabels.app' -r | sort > /tmp/pdb-covered.txt

# PDB 없는 Deployment 식별
comm -23 /tmp/deployments.txt /tmp/pdb-covered.txt
# catalog-service    ← PDB 없음
# notification-svc   ← PDB 없음

# 일괄 PDB 생성 스크립트
for svc in catalog-service notification-svc; do
cat <<EOF | kubectl apply -f -
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: ${svc}-pdb
  namespace: production
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: ${svc}
EOF
done

# 업그레이드 중 PDB 상태 모니터링
watch -n 5 "kubectl get pdb -n production"

실무 포인트: 클러스터 업그레이드 전 PDB 점검은 "가드레일 설치"입니다. 업그레이드 툴(eksctl, kubeadm)은 PDB를 존중하며 노드를 드레인하므로, PDB만 제대로 있으면 업그레이드 중 서비스 중단 없이 노드를 순서대로 교체할 수 있습니다.

핵심 요약

개념명령/설정실무 사용 빈도
PDB 생성kubectl create pdb <name> --selector=app=<app> --min-available=1서비스 배포 시
PDB 상태 확인kubectl get pdb -n <ns>유지보수 전
Disruptions Allowedkubectl describe pdb <name>드레인 전 확인
노드 드레인kubectl drain <node> --ignore-daemonsets --delete-emptydir-data노드 유지보수
드레인 타임아웃--timeout=5m장시간 대기 방지
강제 드레인 (PDB 무시)--disable-eviction (비권장)긴급 상황만
PDB 커버리지 확인kubectl get pdb -A + Deployment 매칭업그레이드 전

지식 확인

퀴즈 — 4문제

Q1

minAvailable: 2 로 설정된 PDB가 있을 때, 현재 파드가 3개라면 동시에 Evict 가능한 파드 수는?

Q2

maxUnavailable: 1 PDB가 있고 파드가 5개 실행 중일 때, 동시에 드레인 불가능한 파드 수는?

Q3

PDB가 설정된 상태에서 kubectl drain이 중단 없이 완료되지 않는 이유는?

Q4

minAvailable과 maxUnavailable 중 어느 것을 사용해야 하나요?

0 / 4 답변

🧪 실습으로 확인하기

K8s 기초 — Pod/Deployment/Service 생성

초급

kubectl로 nginx Pod를 생성하고 Deployment와 Service를 차례로 만들어 클러스터 외부에서 접근 가능한 상태까지 구성한다. K8s 3대 리소스의 역할과 관계를 직접 손으로 익힌다.

40📋 5단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

kubernetes고급 · 55
[Kubernetes] VPA(Vertical Pod Autoscaler) 기반 실시간 리소스 최적화
Kubernetes 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점