중단

중단 (Disruptions)

이 가이드는 고가용성(highly available) 애플리케이션을 구축하려는 애플리케이션 소유자를 위해, Pod에 어떤 종류의 중단이 일어날 수 있는지 이해할 필요가 있는 분들을 대상으로 합니다.

또한 업그레이드와 오토스케일링 같은 자동화된 클러스터 작업을 수행하려는 클러스터 관리자에게도 유용합니다.

출처: 문서

본문

자발적 및 비자발적 중단 (Voluntary and involuntary disruptions)

Pod는 누군가(사람 또는 컨트롤러)가 파괴하거나, 피할 수 없는 하드웨어 또는 시스템 소프트웨어 오류가 있을 때까지 사라지지 않습니다.

우리는 이런 피할 수 없는 경우를 애플리케이션에 대한 **비자발적 중단(involuntary disruptions)**이라고 부릅니다. 예시는 다음과 같습니다.

  • 노드를 뒷받침하는 물리 머신의 하드웨어 고장
  • 클러스터 관리자가 실수로 VM(인스턴스)을 삭제
  • 클라우드 제공자 또는 하이퍼바이저 오류로 VM이 사라짐
  • 커널 패닉(kernel panic)
  • 클러스터 네트워크 파티션으로 노드가 클러스터에서 사라짐
  • 노드가 리소스 부족(out-of-resources)이 되어 Pod가 축출됨

리소스 부족 상태를 제외하면 이 모든 조건은 대부분의 사용자에게 익숙할 것입니다. Kubernetes에 특정한 것은 아닙니다.

우리는 다른 경우를 **자발적 중단(voluntary disruptions)**이라고 부릅니다. 여기에는 애플리케이션 소유자가 시작한 작업과 클러스터 관리자가 시작한 작업이 모두 포함됩니다. 전형적인 애플리케이션 소유자 작업은 다음과 같습니다.

  • Pod를 관리하는 deployment나 다른 컨트롤러를 삭제
  • deployment의 pod 템플릿을 업데이트해 재시작 유발
  • (예: 실수로) Pod를 직접 삭제

클러스터 관리자 작업은 다음과 같습니다.

  • 수리나 업그레이드를 위해 노드를 드레인(drain)
  • 노드 오토스케일링(Node Autoscaling)을 위해 클러스터에서 노드를 드레인해 클러스터를 축소
  • 노드에서 Pod를 제거해 다른 무언가가 그 노드에 들어맞게 허용

이 작업들은 클러스터 관리자가 직접, 클러스터 관리자가 실행하는 자동화가, 또는 클러스터 호스팅 제공자가 수행할 수 있습니다.

클러스터 관리자에게 묻거나 클라우드 제공자 또는 배포 문서를 참조해, 클러스터에 어떤 자발적 중단 소스가 활성화되어 있는지 확인하세요. 아무것도 활성화되지 않았다면 Pod 중단 예산(PDB)을 만들지 않아도 됩니다.

주의: 모든 자발적 중단이 Pod Disruption Budgets에 의해 제약되는 것은 아닙니다. 예를 들어 deployment나 pod를 삭제하는 것은 Pod Disruption Budgets를 우회합니다.

중단 처리 (Dealing with disruptions)

비자발적 중단을 완화하는 몇 가지 방법은 다음과 같습니다.

자발적 중단의 빈도는 다양합니다. 기본 Kubernetes 클러스터에는 자동화된 자발적 중단이 없습니다(사용자가 촉발한 것만 있음). 그러나 클러스터 관리자나 호스팅 제공자가 자발적 중단을 유발하는 추가 서비스를 실행할 수 있어요. 예를 들어 노드 소프트웨어 업데이트 롤아웃이 자발적 중단을 유발할 수 있습니다. 또한 클러스터(노드) 오토스케일링의 일부 구현은 노드를 조각 모음하고 압축하기 위해 자발적 중단을 유발할 수 있습니다. 클러스터 관리자나 호스팅 제공자는 기대할 수 있는 자발적 중단 수준(있다면)을 문서화해야 합니다. Pod 스펙에서 PriorityClasses를 사용하는 것 같은 특정 구성 옵션도 자발적(및 비자발적) 중단을 유발할 수 있습니다.

Pod 중단 예산 (Pod disruption budgets)

Kubernetes는 빈번한 자발적 중단을 도입하더라도 고가용성 애플리케이션을 실행하는 데 도움이 되는 기능을 제공합니다.

애플리케이션 소유자로서 각 애플리케이션에 대해 **PodDisruptionBudget(PDB)**을 만들 수 있어요. PDB는 자발적 중단으로 인해 복제된 애플리케이션의 Pod 중 동시에 다운되는 수를 제한합니다. 예를 들어 쿼럼(quorum) 기반 애플리케이션은 실행 중인 레플리카 수가 쿼럼에 필요한 수보다 아래로 내려가지 않도록 보장하고 싶을 것입니다. 웹 프론트 엔드는 부하를 서빙하는 레플리카 수가 전체의 특정 백분율 아래로 내려가지 않도록 보장하고 싶을 수 있어요.

클러스터 관리자와 호스팅 제공자는 Pod나 deployment를 직접 삭제하지 않고 Eviction API를 호출해 PodDisruptionBudgets을 존중하는 도구를 사용해야 합니다.

예를 들어 kubectl drain 하위 명령은 노드를 서비스에서 제외(going out of service)로 표시하게 해줍니다. kubectl drain을 실행하면 그 도구는 서비스에서 제외하는 노드의 모든 Pod를 축출하려고 시도합니다. kubectl이 대신 제출하는 축출 요청은 일시적으로 거부될 수 있으므로, 도구는 대상 노드의 모든 Pod가 종료되거나 구성 가능한 제한 시간에 도달할 때까지 모든 실패한 요청을 주기적으로 재시도합니다.

PDB는 애플리케이션이 의도한 수에 비해 견딜 수 있는 레플리카 수를 지정합니다. 예를 들어 .spec.replicas: 5를 가진 Deployment는 특정 시점에 5개의 Pod가 있어야 합니다. PDB가 한 번에 4개를 허용한다면, Eviction API는 한 번에 하나의(둘은 아닌) Pod에 대한 자발적 중단을 허용합니다.

애플리케이션을 구성하는 Pod 그룹은 애플리케이션의 컨트롤러(deployment, stateful-set 등)가 사용하는 것과 같은 라벨 셀렉터로 지정됩니다.

"의도한" Pod 수는 그 Pod를 관리하는 워크로드 리소스의 .spec.replicas에서 계산됩니다. 컨트롤 플레인은 Pod의 .metadata.ownerReferences를 검사해 소유 워크로드 리소스를 발견합니다.

비자발적 중단은 PDB로 방지할 수 없습니다. 그러나 예산에는 계산됩니다.

애플리케이션에 대한 롤링 업그레이드로 인해 삭제되거나 사용 불가능해진 Pod는 중단 예산에 계산되지만, Deployment와 StatefulSet 같은 워크로드 리소스는 롤링 업그레이드 중 PDB에 제한되지 않습니다. 대신 애플리케이션 업데이트 중 실패 처리 방식이 특정 워크로드 리소스의 스펙에 구성됩니다.

PodDisruptionBudgets에 AlwaysAllow Unhealthy Pod Eviction Policy를 설정해 노드 드레인 중 오작동하는 애플리케이션의 축출을 지원하는 것을 권장합니다. 기본 동작은 드레인이 진행되기 전에 애플리케이션 Pod가 정상이 되기를 기다리는 것입니다.

Pod가 eviction API를 사용해 축출되면 PodSpec의 terminationGracePeriodSeconds 설정을 존중하며 우아하게 종료됩니다.

PodDisruptionBudget 예시 (PodDisruptionBudget example)

node-1부터 node-3까지 3개 노드가 있는 클러스터를 고려해보세요. 클러스터는 여러 애플리케이션을 실행 중입니다. 그중 하나는 처음에 pod-a, pod-b, pod-c라고 불리는 3개의 레플리카가 있습니다. PDB가 없는 또 다른 관련 없는 Pod인 pod-x도 표시됩니다. 처음에 Pod는 다음과 같이 배치되어 있습니다.

node-1 node-2 node-3
pod-a available pod-b available pod-c available
pod-x available

3개의 Pod는 모두 deployment의 일부이며, 셋 다 항상 2개 이상 사용 가능해야 한다고 요구하는 PDB를 함께 가지고 있습니다.

예를 들어 클러스터 관리자가 커널의 버그를 고치기 위해 새 커널 버전으로 재부팅하려 한다고 가정해보세요. 클러스터 관리자는 먼저 kubectl drain 명령으로 node-1을 드레인하려고 합니다. 그 도구는 pod-a와 pod-x를 축출하려고 시도합니다. 이는 즉시 성공합니다. 두 Pod는 동시에 종료(terminating) 상태로 들어갑니다. 이로써 클러스터는 이 상태가 됩니다.

node-1 draining node-2 node-3
pod-a terminating pod-b available pod-c available
pod-x terminating

deployment는 Pod 중 하나가 종료 중임을 알아차리고 pod-d라는 교체를 만듭니다. node-1이 cordon되었으므로 다른 노드에 배치됩니다. 또한 무언가가 pod-x의 교체로 pod-y를 만들었습니다.

(참고: StatefulSet의 경우 pod-0 같은 이름을 가질 pod-a는 교체(또한 pod-0이라고 불리지만 UID가 다른)가 생성되기 전에 완전히 종료되어야 합니다. 그렇지 않으면 이 예시는 StatefulSet에도 적용됩니다.)

이제 클러스터는 이 상태입니다.

node-1 draining node-2 node-3
pod-a terminating pod-b available pod-c available
pod-x terminating pod-d starting pod-y

어느 시점에 Pod들이 종료되고, 클러스터는 다음과 같이 보입니다.

node-1 drained node-2 node-3
pod-b available pod-c available
pod-d starting pod-y

이 시점에 서두르는 클러스터 관리자가 node-2나 node-3을 드레인하려고 하면, 드레인 명령이 차단됩니다. deployment에 대해 사용 가능한 Pod가 2개뿐이고 PDB가 최소 2개를 요구하기 때문입니다. 시간이 지나면 pod-d가 사용 가능해집니다.

이제 클러스터 상태는 다음과 같습니다.

node-1 drained node-2 node-3
pod-b available pod-c available
pod-d available pod-y

이제 클러스터 관리자가 node-2를 드레인하려고 합니다. 드레인 명령은 두 Pod를 어떤 순서로 축출하려고 시도합니다. 예를 들어 pod-b 먼저, 그다음 pod-d입니다. pod-b를 축출하는 것은 성공합니다. 그러나 pod-d를 축출하려고 하면 거부됩니다. deployment에 대해 한 Pod만 남게 만들기 때문입니다.

deployment는 pod-b의 교체로 pod-e를 만듭니다. pod-e를 스케줄할 충분한 리소스가 클러스터에 없기 때문에 드레인이 다시 차단됩니다. 클러스터는 이 상태로 끝날 수 있습니다.

node-1 drained node-2 node-3 no node
pod-b terminating pod-c available pod-e pending
pod-d available pod-y

이 시점에 클러스터 관리자는 업그레이드를 진행하려면 노드 하나를 클러스터에 다시 추가해야 합니다.

Kubernetes가 중단이 발생할 수 있는 속도를 어떻게 변화시키는지 다음에 따라 알 수 있습니다.

  • 애플리케이션이 필요한 레플리카 수
  • 인스턴스가 우아하게 종료되는 데 걸리는 시간
  • 새 인스턴스가 시작되는 데 걸리는 시간
  • 컨트롤러의 유형
  • 클러스터의 리소스 용량

Pod 중단 컨디션 (Pod disruption conditions)

이 기능은 Kubernetes에서 안정(stable) 기능이며 1.31 릴리스부터 적용됐어요. 더 이상 이 기능을 토글할 수 없습니다(관련 기능 게이트가 제거됨).

전용 DisruptionTarget 컨디션이 추가돼 Pod가 중단으로 인해 곧 삭제될 것임을 나타냅니다. 컨디션의 reason 필드는 Pod 종료에 대한 다음 이유 중 하나를 추가로 나타냅니다.

다른 모든 중단 시나리오, 예를 들어 Pod 컨테이너 limit 초과로 인한 축출에서는, 중단이 아마도 Pod에 의해 발생했고 재시도 시 다시 발생할 것이므로 Pod가 DisruptionTarget 컨디션을 받지 않습니다.

참고: Pod 중단은 중단될 수 있습니다. 컨트롤 플레인이 같은 Pod의 중단을 계속하기 위해 재시도할 수 있지만 보장되지는 않습니다. 결과적으로 DisruptionTarget 컨디션이 Pod에 추가되었지만 그 Pod가 실제로 삭제되지 않을 수도 있어요. 그런 상황에서는 시간이 지난 후 Pod 중단 컨디션이 지워집니다.

Pod를 정리하는 것과 함께 Pod 가비지 컬렉터(PodGC)는 Pod가 비-터미널 단계에 있으면 그것들을 실패한 것으로 표시합니다(Pod 가비지 컬렉션 참고).

Job(또는 CronJob)을 사용할 때는 이러한 Pod 중단 컨디션을 Job의 Pod 실패 정책(pod failure policy)의 일부로 사용하고 싶을 수 있어요.

클러스터 소유자와 애플리케이션 소유자 역할 분리 (Separating Cluster Owner and Application Owner Roles)

종종 클러스터 관리자와 애플리케이션 소유자를 서로에 대한 지식이 제한된 별도의 역할로 생각하는 것이 유용합니다. 이 책임 분리는 다음 시나리오에서 타당할 수 있습니다.

  • 많은 애플리케이션 팀이 Kubernetes 클러스터를 공유하고, 역할의 자연스러운 전문화가 있을 때
  • 클러스터 관리를 자동화하기 위해 타사 도구나 서비스가 사용될 때

Pod Disruption Budgets는 역할 간의 인터페이스를 제공함으로써 이 역할 분리를 지원합니다.

조직에 이런 책임 분리가 없다면 Pod Disruption Budgets를 사용할 필요가 없을 수도 있습니다.

클러스터에서 파괴적 작업을 수행하는 방법 (How to perform Disruptive Actions on your Cluster)

클러스터 관리자이고 노드 또는 시스템 소프트웨어 업그레이드 같은 클러스터의 모든 노드에 파괴적 작업을 수행해야 한다면, 몇 가지 옵션이 있습니다.

  • 업그레이드 중 다운타임을 받아들입니다.
  • 다른 완전한 복제 클러스터로 장애 조치(failover)합니다. 다운타임은 없지만 중복 노드와 전환을 조율하기 위한 인적 노력 모두에 비용이 들 수 있어요.
  • 중단에 내성이 있는 애플리케이션을 작성하고 PDB를 사용합니다. 다운타임이 없습니다. 리소스 중복이 최소화됩니다. 클러스터 관리의 더 많은 자동화를 허용합니다. 중단 내성 애플리케이션을 작성하는 것은 까다롭지만, 자발적 중단을 견디는 작업은 오토스케일링 지원과 비자발적 중단 견디기를 지원하는 작업과 크게 겹칩니다.

다음 단계 (What's next)

더 알아보기 (Learn more)