infra
Platform

모듈 맵

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

0 / 29 완료

펼치기
0 / 29 완료0%

쿠버네티스 & GitOps · 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과 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, 클러스터 업그레이드, Eviction API적용됨
Involuntary (비계획)노드 OOM, 하드웨어 장애, 커널 패닉적용 안 됨
💡개념

minAvailable vs maxUnavailable 설정

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

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

PDB minAvailable vs maxUnavailable — PodDisruptionBudget은 노드 드레인·업그레이드 같은 자발적 중단 시 동시에 죽일 수 있는 파드 수를 제한. minAvailable(항상 살아있어야 할 최소 수)·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
🔍실행 후 확인할 것
  • kubectl describe pdb <name>에서 Disruptions Allowed 값을 먼저 확인 — 0이면 현재 drain이 차단됨. Current Healthy가 minAvailable과 같거나 낮으면 파드를 한 개도 더 제거할 수 없는 상태
  • Disruptions Allowed 수치 기준: minAvailable: 2이고 전체 파드 수가 3이면 1개 drain 가능, 2이면 0개 drain 가능. kubectl drain이 멈추면 Disruptions Allowed=0인 상태로 kubectl describe pdb로 즉시 확인
  • kubectl drain이 오래 걸리고 "Cannot evict pod as it would violate PDB" 가 반복되면 → readinessProbe 실패로 일부 파드가 Healthy 카운트에서 제외된 것. kubectl get pods -l <selector>로 Running이지만 READY=0/1인 파드 찾기
💡개념

축출 요청이 PDB 게이트를 통과하는 법 — 요청에서 재배치까지 5단계

PDB를 걸어두면 노드를 drain해도 서비스가 한꺼번에 죽지 않습니다. 비결은 kubectl drain이 파드를 직접 delete하지 않고 Eviction API를 거치며, 그 앞에 PDB라는 게이트가 있기 때문입니다. 축출 요청 하나가 이 게이트에서 허용될지 거부될지를 단계로 알면 "drain이 왜 멈췄지", "왜 한 개씩만 빠지지", "노드 고장은 왜 못 막지"를 정확히 설명할 수 있습니다.

TEXT
[운영자]  kubectl drain node-3   (또는 업그레이드·스케일다운의 축출)
   │
   ① 축출 요청: 대상 파드마다 Eviction API 호출 (직접 delete가 아님)
   │
   ② PDB 매칭: API server가 파드 라벨로 매칭되는 PDB를 selector로 찾음
   │    → 매칭되는 PDB가 없으면 게이트 없이 바로 축출 (③④ 건너뜀)
   │
   ③ 예산 계산: ALLOWED = Current Healthy − Desired Healthy(minAvailable 환산)
   │
   ④ 판정:
   │    ALLOWED > 0  → 축출 허용, 파드 종료 → 예산 1 감소
   │    ALLOWED = 0  → 429 Too Many Requests로 거부 → drain 대기(재시도)
   │
   ⑤ 재배치·회복: 축출된 파드가 다른 노드에서 Ready → Current Healthy 회복
   │    → 예산이 다시 >0 → 다음 파드 축출 (항상 minAvailable만큼은 생존)
   ▼
[결과]  노드 유지보수 중에도 최소 가용 파드 수 유지
        (자발적 중단만 보호 · 노드 고장 같은 비자발적 중단은 못 막음)

각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:

단계하는 일막히면 증상
① 축출 요청drain·업그레이드·스케일다운이 대상 파드에 Eviction API 호출노드 고장·커널 패닉 같은 비자발적 중단은 이 경로를 안 거쳐 PDB가 못 막음
② PDB 매칭API server가 파드 라벨로 매칭 PDB를 selector로 탐색selector 라벨이 파드와 안 맞으면 보호가 아예 안 걸림(게이트 없이 통과)
③ 예산 계산ALLOWED = Current Healthy − Desired Healthy(minAvailable 환산)unready·CrashLoop 파드가 Current Healthy를 깎아 예산이 0으로 떨어짐
④ 판정ALLOWED>0이면 축출 진행(예산 1 감소), 0이면 429로 거부·대기minAvailable=레플리카 수·단일 레플리카+minAvailable:1이면 예산이 처음부터 0 → 영구 대기
⑤ 재배치·회복축출 파드가 다른 노드에서 Ready → 예산 회복 → 다음 축출다른 노드 여유 없음·anti-affinity로 Pending이면 재배치 실패 → 다음 축출도 막힘

즉 PDB는 drain을 막는 게 아니라 **"한 번에 minAvailable만큼은 항상 살려두는 속도 제한"**입니다. drain이 멈추면 kubectl describe pdb로 ALLOWED가 0인지 보고, 0이면 과조임(④: minAvailable을 낮추거나 maxUnavailable로 전환·이중화)인지 건강 문제(③: unready 파드 복구 또는 unhealthyPodEvictionPolicy: AlwaysAllow)인지 가릅니다. 그리고 이 게이트는 자발적 중단에만 작동하므로(①), 노드 고장 대비는 PDB가 아니라 레플리카 수·멀티노드 분산의 몫입니다.

💡개념

PDB와 kubectl drain의 상호작용

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

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

⚠️ kubectl delete pod 같은 일반 삭제는 Eviction API를 거치지 않으므로 PDB가 차단하지 않습니다. PDB는 강제 삭제나 노드 장애를 막는 방화벽이 아니라, drain·Eviction API 같은 자발적 중단 요청에만 적용되는 보호 장치입니다. 운영 검증은 kubectl drain <node> --dry-run=serverkubectl get/describe pdb로 먼저 수행하세요.

PDB와 kubectl drain의 상호작용 — web 3 replica에 PDB minAvailable=2인 상태에서 node-3을 드레인하면, 파드 A는 evict되지만(2개 유지, PDB 만족) 파드 B evict는 1개가 되어 minAvailable 위반이라 Eviction API가 429 Too Many Requests로 거부하고 drain이 대기한다. A가 다른 노드에 재배치돼 다시 3개가 되면 ALLOWED DISRUPTIONS가 1로 늘어 B를 evict한다. PDB는 drain·업그레이드 같은 자발적 중단만 보호하며, 노드 고장·OOM 같은 비자발적 중단은 복제본 수·멀티 노드 분산으로 대비한다확대

위험 명령어노드의 기존 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가 드레인을 '영원히' 멈추게 만들 때

💡개념

심화: ALLOWED DISRUPTIONS의 산수 — 조이면 문이 잠긴다

PDB를 붙였는데 노드 업그레이드가 몇 시간째 안 끝나거나, 단일 레플리카 서비스가 드레인을 아예 영구 차단하는 일이 흔합니다. PDB는 가드레일이지만, 산수를 틀리면 스스로 문을 잠급니다. 앞 TroubleCase가 'PDB가 없어서' 생긴 문제였다면, 여기서는 'PDB가 있어서, 그것도 잘못 조여서' 생기는 정반대의 함정을 봅니다.

핵심은 ALLOWED DISRUPTIONS = Current Healthy − Desired Healthy라는 계산입니다(minAvailable 기준). 이 값이 0이면 어떤 파드도 evict할 수 없고, Eviction API는 429 Too Many Requests를 돌려주며 drain은 기본적으로 타임아웃 없이 무한 대기합니다.

  • 함정 1 — 예산이 처음부터 0: minAvailable을 레플리카 수와 같게(예: replicas 3에 minAvailable 3) 걸면 3 − 3 = 0이라 아무것도 못 내립니다. 특히 단일 레플리카에 minAvailable: 1은 보호가 아니라 영구 잠금입니다. 파드가 하나뿐이면 그걸 내리는 순간 0이 되니, 무중단 드레인 자체가 불가능합니다. 이건 PDB로 풀 문제가 아니라 레플리카를 늘려야 하는 문제입니다.
  • 함정 2 — 건강하지 않은 파드가 예산을 갉아먹음: 파드 하나가 CrashLoop이거나 readiness에 실패하면 Current Healthy가 줄어 Desired 밑으로 떨어지고 ALLOWED가 0이 됩니다. 그러면 정상 파드를 지키느라, 정작 그 고장 난 파드가 있는 노드조차 드레인하지 못하는 교착이 생깁니다. 기본 정책(IfHealthyBudget)은 예산이 있을 때만 unready 파드를 evict하기 때문입니다. K8s 1.26+의 unhealthyPodEvictionPolicy: AlwaysAllow를 주면 "어차피 고장 난 파드는 예산과 무관하게 내보낸다"로 이 교착을 풉니다.

그래서 PDB는 "지나치게 조이면 유지보수를 막고, 너무 느슨하면 의미가 없다"의 균형입니다. 대개 maxUnavailable: 1(또는 %)이 레플리카 수 변화에 더 안전하고, 단일 레플리카는 PDB가 아니라 이중화가 답입니다.

상황: 클러스터 업그레이드로 노드를 드레인하는데 Cannot evict pod as it would violate the pod's disruption budget만 반복되며 몇 시간째 진행이 없습니다. PDB는 분명히 존재하고 설정도 그럴듯합니다 — 앞의 'PDB 미설정' 사례와는 정반대 상황입니다.

원인: ALLOWED DISRUPTIONS = Current Healthy − Desired Healthy = 0이라 evict가 원천 봉쇄된 것입니다. 두 갈래 중 하나입니다. (a) minAvailable이 현재 레플리카 수와 같거나 커서 예산이 처음부터 0(특히 단일 레플리카 + minAvailable: 1), 또는 (b) 파드 하나가 readiness 실패·CrashLoop라 Current Healthy가 Desired 밑으로 떨어져 예산이 0이 된 경우입니다.

진단: describe로 산수를 직접 확인하고, 두 갈래 중 어느 쪽인지 가릅니다.

Kubernetes
kubectl describe pdb <name> -n <ns> | grep -E "Allowed disruptions|Current healthy|Desired healthy|Expected pods"
# Allowed disruptions: 0 / Current healthy: 2 / Desired healthy: 2  → 여유 0 확정

kubectl get deploy <app> -n <ns>          # replicas와 minAvailable 관계 확인 (함정 1)
kubectl get pods -l app=<app> -n <ns>     # READY 0/1 인 unready 파드 존재 여부 (함정 2)

replicas == minAvailable이면 함정 1, READY가 0/1인 파드가 있으면 함정 2입니다.

해결: 원인에 따라 다릅니다. **함정 1(과조임)**은 minAvailable을 레플리카보다 낮추거나 maxUnavailable: 1로 바꿔 여유 1을 만들고, 단일 레플리카 서비스는 PDB로 풀 수 없으니 replicas ≥ 2 + PodAntiAffinity로 이중화합니다(정말 단일뿐인 스테이트풀은 유지보수 창을 따로 잡습니다). **함정 2(건강 문제)**는 먼저 unready 파드의 probe·크래시 원인을 고쳐(Liveness, Readiness, Startup Probe 헬스 체크 설정) Current Healthy를 회복시키면 예산이 되살아나고, 이미 회복 불가한 파드가 드레인을 막는다면 그 워크로드 PDB에 unhealthyPodEvictionPolicy: AlwaysAllow(1.26+)를 설정합니다. 급하면 --timeout으로 무한 대기를 끊고, 최후의 수단으로만 --disable-eviction(PDB 무시)을 씁니다. 재발 방지로 업그레이드 전 kubectl get pdb -A에서 ALLOWED DISRUPTIONS가 0인 항목을 미리 걸러냅니다.

💼
실무 맥락
현업 패턴

시나리오: 클러스터 버전 업그레이드 전 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만 제대로 있으면 업그레이드 중 서비스 중단 없이 노드를 순서대로 교체할 수 있습니다.

실습 단계
1

테스트 Deployment 생성

kubectl create deployment web --image=nginx --replicas=3 -n pdb-demo kubectl get pods -n pdb-demo -l app=web

예상 출력

NAME                   READY   STATUS    RESTARTS   AGE
web-...                1/1     Running   0          ...
web-...                1/1     Running   0          ...
web-...                1/1     Running   0          ...
2

minAvailable PDB 생성 및 상태 확인

kubectl create pdb web-pdb --selector=app=web --min-available=2 -n pdb-demo kubectl get pdb -n pdb-demo

예상 출력

NAME      MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
web-pdb   2               N/A               1                     ...
3

PDB 상세 상태 — Disruptions Allowed 확인

kubectl describe pdb web-pdb -n pdb-demo | grep -E 'Disruptions Allowed|Current Healthy|Desired Healthy'

예상 출력

Disruptions Allowed:  1
Current Healthy:      3
Desired Healthy:      2
4

파드 수를 줄여 PDB 경계 조건 관찰

kubectl scale deployment/web --replicas=2 -n pdb-demo kubectl get pdb -n pdb-demo

예상 출력

NAME      MIN AVAILABLE   ALLOWED DISRUPTIONS
web-pdb   2               0
5

실습 리소스 정리

kubectl delete pdb web-pdb -n pdb-demo kubectl delete deployment web -n pdb-demo echo cleaned

예상 출력

poddisruptionbudget.policy "web-pdb" deleted
deployment.apps "web" deleted
cleaned

핵심 요약

개념명령/설정실무 사용 빈도
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 매칭업그레이드 전

명령어·단축키 빠른 참조

이 모듈에서 다룬 PDB 조회·드레인 제어 명령을 실전 옵션과 함께 모았습니다. ALLOWED DISRUPTIONS = Current Healthy − Desired Healthy가 0이면 드레인이 대기합니다.

명령어/단축키용도자주 쓰는 예
kubectl create pdbPDB 빠르게 생성kubectl create pdb web-pdb --selector=app=web --min-available=2 -n pdb-demo
kubectl get pdbALLOWED DISRUPTIONS 값 확인kubectl get pdb -A(0이면 drain 차단 상태)
kubectl describe pdb예산 산수(Current/Desired Healthy) 확인kubectl describe pdb web-pdb | grep -E "Allowed disruptions|Current|Desired"
kubectl get pdb -w드레인 중 ALLOWED 변화 추적kubectl get pdb payment-pdb -n pdb-demo -w
kubectl drain노드 유지보수(Eviction API)kubectl drain node-3 --ignore-daemonsets --delete-emptydir-data --timeout=5m
kubectl drain --dry-run실제 evict 전 영향 미리 보기kubectl drain node-2 --ignore-daemonsets --dry-run
--disable-evictionPDB 무시 강제 드레인(비권장)교착 긴급 해소 최후 수단
kubectl scale deployment레플리카 조정으로 예산 경계 관찰kubectl scale deployment/web --replicas=2 -n pdb-demo
kubectl get pods -o wide | grep파드 노드 분산·같은 노드 집중 확인kubectl get pods -n <ns> -o wide | grep payment
kubectl get pods -l <selector>unready(READY 0/1) 파드로 예산 잠김 확인kubectl get pods -l app=web -n pdb-demo
kubectl get pdb -A | grep -v N/A업그레이드 전 ALLOWED=0 항목 사전 점검kubectl get pdb -A | grep -v "N/A"
kubectl get events --sort-by동시 evict된 파드 사후 분석kubectl get events -n production --sort-by='.lastTimestamp' | tail -20

관련 모듈로 더 깊이:

다음 모듈 hpa-autoscaling에서는 파드 수를 수동으로 조정하지 않고 CPU 사용률에 따라 자동 스케일링하는 HPA를 다룹니다. metrics-server 설치부터 scaleDown stabilizationWindow 튜닝까지, 실무 HPA 설정의 핵심을 실습합니다.

지식 확인

퀴즈 — 8문제

Q1

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

Q2

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

Q3

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

Q4

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

Q5

PDB가 보호하는 것은 voluntary disruption이다. voluntary와 involuntary disruption의 차이는?

Q6

노드 업그레이드로 kubectl drain을 할 때 PDB minAvailable: 2(파드 3개)가 하는 역할은?

Q7

[심화] PDB가 걸린 서비스에서 kubectl drain이 한 파드도 evict하지 못하고 무한 대기한다. describe pdb의 ALLOWED DISRUPTIONS 값이 0이라는 것의 의미는?

Q8

[심화] PDB를 제대로 걸었는데도 노드 드레인이 몇 시간째 ALLOWED DISRUPTIONS=0으로 멈춰 있다. 가장 가능성 높은 두 원인과 대응으로 옳은 것은?

0 / 8 답변

🧪 실습으로 확인하기

K8s 기초 — Pod/Deployment/Service 생성

초급

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

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

이것도 배워보세요