애플리케이션의 중단 예산(Disruption Budget) 지정하기

애플리케이션의 중단 예산(Disruption Budget) 지정하기

기능 상태: Kubernetes v1.21부터 Stable.

이 페이지에서는 애플리케이션이 겪는 동시 중단(concurrent disruptions) 횟수를 제한하는 방법을 보여드려요. 이를 통해 높은 가용성을 유지하면서도 클러스터 관리자가 클러스터 노드를 관리할 수 있게 해 줘요.

출처: 문서

본문

시작하기 전에 (Before you begin)

쿠버네티스 서버가 최소 v1.21 버전이어야 해요. 버전을 확인하려면 kubectl version을 입력하세요.

  • 여러분은 쿠버네티스 클러스터에서 실행되는 애플리케이션의 소유자이고, 그 애플리케이션은 높은 가용성이 필요해요.
  • 복제된 무상태(Replicated Stateless) 애플리케이션 및/또는 복제된 유상태(Replicated Stateful) 애플리케이션을 배포하는 방법을 알아야 해요.
  • 파드 중단(Pod Disruptions)에 대해 읽어 보셨어야 해요.
  • 클러스터 소유자나 서비스 제공업체가 Pod Disruption Budget을 존중한다는 것을 확인하세요.

PodDisruptionBudget으로 애플리케이션 보호하기 (Protecting an Application with a PodDisruptionBudget)

  • PodDisruptionBudget(PDB)으로 보호하고 싶은 애플리케이션을 식별하세요.
  • 애플리케이션이 중단에 어떻게 반응하는지 생각해 보세요.
  • PDB 정의를 YAML 파일로 작성하세요.
  • YAML 파일에서 PDB 객체를 생성하세요.

보호할 애플리케이션 식별하기 (Identify an Application to Protect)

가장 흔한 사용 사례는 내장된 쿠버네티스 컨트롤러 중 하나로 정의된 애플리케이션을 보호하는 경우예요.

  • Deployment
  • ReplicationController
  • ReplicaSet
  • StatefulSet

이 경우 컨트롤러의 .spec.selector를 기록해 두고, 동일한 selector를 PDB의 .spec.selector에 넣어요.

1.15 버전부터 PDB는 scale 하위 리소스가 활성화된 커스텀 컨트롤러를 지원해요.

위의 컨트롤러 중 하나에 의해 제어되지 않는 파드나 임의의 파드 그룹에도 PDB를 사용할 수 있어요. 다만 몇 가지 제한 사항이 있는데, 이는 "임의의 워크로드와 임의의 셀렉터(Arbitrary workloads and arbitrary selectors)"에서 설명해요.

애플리케이션이 중단에 어떻게 반응하는지 생각하기 (Think about how your application reacts to disruptions)

자발적 중단(voluntary disruption) 때문에 짧은 시간 동안 동시에 몇 개의 인스턴스가 내려가 있어도 되는지 결정하세요.

  • 무상태 프론트엔드: 우려 — 서빙 용량이 10% 이상 줄어들지 않게 하기. 해결 — 예를 들어 minAvailable을 90%로 설정한 PDB 사용.
  • 단일 인스턴스 유상태 애플리케이션: 우려 — 나와 상의하지 않고 이 애플리케이션을 종료하지 않기. 가능한 해결 1 — PDB를 사용하지 않고 가끔의 다운타임을 허용하기. 가능한 해결 2 — maxUnavailable=0으로 PDB 설정. 클러스터 운영자가 종료 전에 나와 상의해야 한다는 합의(쿠버네티스 외부에서) 만들기. 운영자가 연락하면 다운타임을 준비한 뒤, PDB를 삭제해 중단 준비가 됐음을 알리고, 이후 다시 생성하기.
  • Consul, ZooKeeper, etcd 같은 다중 인스턴스 유상태 애플리케이션: 우려 — 인스턴스 수를 쿼럼(quorum) 아래로 줄이지 않기. 그렇지 않으면 쓰기가 실패해요. 가능한 해결 1 — maxUnavailable을 1로 설정(애플리케이션 규모가 달라져도 동작). 가능한 해결 2 — minAvailable을 쿼럼 크기로 설정(예: 규모가 5일 때 3). (더 많은 동시 중단 허용).
  • 재시작 가능한 배치 Job: 우려 — 자발적 중단이 발생해도 Job은 완료돼야 함. 가능한 해결 — PDB를 만들지 않기. Job 컨트롤러가 대체 파드를 생성해 줘요.

백분율 지정 시 반올림 로직 (Rounding logic when specifying percentages)

minAvailable이나 maxUnavailable의 값은 정수 또는 백분율로 표현할 수 있어요.

  • 정수를 지정하면 파드 수를 의미해요. 예를 들어 minAvailable을 10으로 설정하면 중단 중에도 10개의 파드가 항상 사용 가능해야 해요.
  • 백분율을 문자열로 지정하면(예: "50%") 전체 파드 수의 백분율을 의미해요. 예를 들어 minAvailable"50%"로 설정하면 중단 중에도 파드의 최소 50%가 사용 가능 상태를 유지해야 해요.

백분율로 값을 지정하면 정확한 파드 수로 매핑되지 않을 수 있어요. 예를 들어 파드가 7개인데 minAvailable"50%"로 설정하면, 3개가 사용 가능해야 하는지 4개가 사용 가능해야 하는지 바로 명확하지 않아요. 쿠버네티스는 가장 가까운 정수로 올림하기 때문에, 이 경우 4개의 파드가 사용 가능해야 해요. maxUnavailable을 백분율로 지정하면, 쿠버네티스는 중단될 수 있는 파드 수를 올림해요. 그래서 중단이 여러분이 정의한 maxUnavailable 백분율을 초과할 수 있어요. 이 동작을 제어하는 코드를 확인해 볼 수 있어요.

PodDisruptionBudget 지정하기 (Specifying a PodDisruptionBudget)

PodDisruptionBudget은 세 가지 필드를 가져요.

  • 라벨 셀렉터 .spec.selector — 적용 대상 파드 집합을 지정해요. 필수 필드예요.
  • .spec.minAvailable — 퇴거된 파드가 없더라도 퇴거 이후 그 집합에서 여전히 사용 가능해야 하는 파드 수를 설명해요. minAvailable은 절대 숫자나 백분율 중 하나가 될 수 있어요.
  • .spec.maxUnavailable (Kubernetes 1.7 이상에서 사용 가능) — 퇴거 이후 그 집합에서 사용 불가능해질 수 있는 파드 수를 설명해요. 절대 숫자나 백분율 중 하나가 될 수 있어요.

참고: 빈 셀렉터의 동작은 PodDisruptionBudget의 policy/v1beta1과 policy/v1 API에서 달라요. policy/v1beta1에서 빈 셀렉터는 0개의 파드와 일치하지만, policy/v1에서 빈 셀렉터는 네임스페이스의 모든 파드와 일치해요.

단일 PodDisruptionBudget에서는 maxUnavailableminAvailable 중 하나만 지정할 수 있어요. maxUnavailable은 모두 동일한 관련 컨트롤러가 관리하는 파드의 퇴거를 제어하는 데에만 사용할 수 있어요. 아래 예시에서 "desired replicas"는 PodDisruptionBudget이 선택한 파드를 관리하는 컨트롤러의 scale이에요.

  • 예시 1: minAvailable이 5라면, PodDisruptionBudgetselector가 선택한 파드 중 건강한(healthy) 파드가 5개 이상 남게 하는 한 퇴거가 허용돼요.
  • 예시 2: minAvailable이 30%라면, desired replicas 수의 최소 30%가 건강한 상태인 한 퇴거가 허용돼요.
  • 예시 3: maxUnavailable이 5라면, desired replicas 총수 중 건강하지 않은 복제본이 최대 5개인 한 퇴거가 허용돼요.
  • 예시 4: maxUnavailable이 30%라면, 건강하지 않은 복제본 수가 desired replicas 총수의 30% 이하(가장 가까운 정수로 올림)인 한 퇴거가 허용돼요. desired replicas 총수가 1개뿐이라면 그 단일 복제본도 중단이 허용돼서 실질적으로 100% 사용 불가가 될 수 있어요.

일반적인 사용에서 단일 예산은 컨트롤러가 관리하는 파드 모음(예: 단일 ReplicaSet 또는 StatefulSet의 파드)에 사용돼요.

참고: 중단 예산은 지정된 수/백분율의 파드가 항상 떠 있을 것이라고 진짜로 보장하지는 않아요. 예를 들어 예산에 지정된 최소 크기에 있을 때 그 모음의 파드를 호스팅하는 노드가 실패하면, 모음에서 사용 가능한 파드 수가 지정된 크기 아래로 내려갈 수 있어요. 예산은 오직 자발적 퇴거만 보호할 수 있어요. 모든 사용 불가 원인을 보호하지는 못해요.

maxUnavailable을 0%나 0으로 설정하거나, minAvailable을 100%나 복제본 수로 설정하면 자발적 퇴거를 0으로 요구하는 것이에요. ReplicaSet 같은 워크로드 객체에 자발적 퇴거를 0으로 설정하면, 그 파드 중 하나를 실행하는 노드를 성공적으로 drain할 수 없어요. 퇴거 불가한 파드가 실행되는 노드를 drain하려고 하면 drain이 끝나지 않아요. 이것은 PodDisruptionBudget의 의미론에 따라 허용되는 동작이에요.

아래에 정의된 파드 중단 예산 예시를 볼 수 있어요. 이 예시들은 app: zookeeper 라벨이 있는 파드와 일치해요.

minAvailable을 사용하는 PDB 예시: (policy/zookeeper-pod-disruption-budget-minavailable.yaml)

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

maxUnavailable을 사용하는 PDB 예시: (policy/zookeeper-pod-disruption-budget-maxunavailable.yaml)

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: zookeeper

예를 들어 위의 zk-pdb 객체가 크기 3의 StatefulSet 파드를 선택한다면, 두 사양 모두 정확히 같은 의미를 가져요. maxUnavailable 사용이 권장되는데, 해당 컨트롤러의 복제본 수 변경에 자동으로 반응하기 때문이에요.

PDB 객체 생성하기 (Create the PDB object)

kubectl을 사용해 PDB 객체를 생성하거나 업데이트할 수 있어요.

kubectl apply -f mypdb.yaml

PDB 상태 확인하기 (Check the status of the PDB)

kubectl을 사용해 PDB가 생성됐는지 확인하세요. 네임스페이스에 실제로 app: zookeeper와 일치하는 파드가 없다면 다음과 같은 출력을 볼 수 있어요.

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               0                     7s

일치하는 파드가 있다면(예: 3개), 다음과 같은 출력을 볼 수 있어요.

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               1                     7s

ALLOWED DISRUPTIONS의 0이 아닌 값은 중단 컨트롤러(disruption controller)가 파드를 보고, 일치하는 파드를 세고, PDB의 상태를 업데이트했음을 의미해요.

이 명령으로 PDB 상태에 대한 더 자세한 정보를 얻을 수 있어요.

kubectl get poddisruptionbudgets zk-pdb -o yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  annotations:
…
  creationTimestamp: "2020-03-04T04:22:56Z"
  generation: 1
  name: zk-pdb
…
status:
  currentHealthy: 3
  desiredHealthy: 2
  disruptionsAllowed: 1
  expectedPods: 3
  observedGeneration: 1

파드의 건강 상태 (Healthiness of a Pod)

현재 구현은 .status.conditionstype="Ready"이고 status="True"인 항목이 있는 파드를 건강한 파드로 간주해요. 이 파드들은 PDB 상태의 .status.currentHealthy 필드를 통해 추적돼요.

건강하지 않은 파드의 퇴거 정책 (Unhealthy Pod Eviction Policy)

이것은 쿠버네티스의 안정적인 기능으로, 1.31 릴리스부터 그랬어요. 더 이상 이 기능을 토글할 수 없어요(관련 기능 게이트가 제거됨).

애플리케이션을 보호하는 PodDisruptionBudget은 건강한 파드의 퇴거를 허용하지 않음으로써 .status.currentHealthy 파드 수가 .status.desiredHealthy에 지정된 수 아래로 내려가지 않도록 보장해요. .spec.unhealthyPodEvictionPolicy를 사용하면 건강하지 않은 파드를 언제 퇴거 대상으로 간주할지 기준을 정의할 수도 있어요. 정책을 지정하지 않았을 때의 기본 동작은 IfHealthyBudget 정책에 해당해요.

정책:

IfHealthyBudget — 실행 중(.status.phase="Running")이지만 아직 건강하지 않은 파드는, 보호 중인 애플리케이션이 중단되지 않은 경우에만(.status.currentHealthy.status.desiredHealthy 이상) 퇴거될 수 있어요. 이 정책은 이미 중단된 애플리케이션의 실행 중인 파드가 건강해질 최선의 기회를 갖도록 보장해요. 이는 노드 drain에 부정적인 영향을 줄 수 있어요. 왜냐하면 PDB로 보호되는 잘못 동작하는 애플리케이션에 의해 drain이 막힐 수 있기 때문이에요. 특히 (버그나 잘못된 구성으로) CrashLoopBackOff 상태의 파드를 가진 애플리케이션이나, Ready 상태를 보고하지 못하는 파드의 경우 그렇죠.

AlwaysAllow — 실행 중(.status.phase="Running")이지만 아직 건강하지 않은 파드는 중단된 것으로 간주되며, PDB의 기준을 충족하는지와 무관하게 퇴거될 수 있어요. 이는 중단된 애플리케이션의 향후 실행 중인 파드가 건강해질 기회를 얻지 못할 수 있다는 뜻이에요. 이 정책을 사용하면 클러스터 관리자가 PDB로 보호되는 잘못 동작하는 애플리케이션을 쉽게 퇴거할 수 있어요. 특히 (버그나 잘못된 구성으로) CrashLoopBackOff 상태의 파드를 가진 애플리케이션이나, Ready 상태를 보고하지 못하는 파드의 경우 그렇죠.

참고: Pending, Succeeded 또는 Failed 상태의 파드는 항상 퇴거 대상으로 간주돼요.

임의의 워크로드와 임의의 셀렉터 (Arbitrary workloads and arbitrary selectors)

내장 워크로드 리소스(Deployment, ReplicaSet, StatefulSet, ReplicationController) 또는 scale 하위 리소스를 구현하는 커스텀 리소스에서만 PDB를 사용하고, PDB 셀렉터가 파드의 소유 리소스 셀렉터와 정확히 일치한다면 이 섹션은 건너뛰어도 돼요.

다른 리소스, "operator", 또는 배어(bare) 파드가 제어하는 파드에도 PDB를 사용할 수 있어요. 다만 다음 제한 사항이 있어요.

  • .spec.maxUnavailable이 아닌 .spec.minAvailable만 사용할 수 있어요.
  • .spec.minAvailable에는 백분율이 아닌 정수 값만 사용할 수 있어요.

다른 가용성 구성은 불가능해요. 지원되는 소유 리소스가 없으면 쿠버네티스가 총 파드 수를 알 수 없기 때문이에요.

워크로드 리소스에 속한 파드의 부분집합이나 상위집합을 선택하는 셀렉터를 사용할 수 있어요. 퇴거 API는 여러 PDB로 덮이는(covered by multiple PDBs) 파드의 퇴거를 허용하지 않아요. 그래서 대부분의 사용자는 겹치는 셀렉터를 피하고 싶을 거예요. 겹치는 PDB의 합리적인 사용 사례 중 하나는 파드가 한 PDB에서 다른 PDB로 전환되는 경우예요.

더 알아보기 (Learn more)