개념

Edit This Page

파드 우선순위(priority)와 선점(preemption)

FEATURE STATE: Kubernetes v1.14 [stable]

파드우선순위 를 가질 수 있다. 우선순위는 다른 파드에 대한 상대적인 파드의 중요성을 나타낸다. 파드를 스케줄링할 수 없는 경우, 스케줄러는 우선순위가 낮은 파드를 선점(축출)하여 보류 중인 파드를 스케줄링할 수 있게 한다.

경고:

모든 사용자를 신뢰할 수 없는 클러스터에서, 악의적인 사용자가 우선순위가 가장 높은 파드를 생성하여 다른 파드가 축출되거나 스케줄링되지 않을 수 있다. 관리자는 리소스쿼터를 사용하여 사용자가 우선순위가 높은 파드를 생성하지 못하게 할 수 있다.

자세한 내용은 기본적으로 프라이어리티 클래스(Priority Class) 소비 제한을 참고한다.

우선순위와 선점을 사용하는 방법

우선순위와 선점을 사용하려면 다음을 참고한다.

  1. 하나 이상의 프라이어리티클래스를 추가한다.

  2. 추가된 프라이어리티클래스 중 하나에 priorityClassName이 설정된 파드를 생성한다. 물론 파드를 직접 생성할 필요는 없다. 일반적으로 디플로이먼트와 같은 컬렉션 오브젝트의 파드 템플릿에 priorityClassName 을 추가한다.

이 단계에 대한 자세한 내용은 계속 읽어보자.

참고: 쿠버네티스는 이미 system-cluster-criticalsystem-node-critical, 두 개의 프라이어리티클래스를 제공한다. 이들은 일반적인 클래스이며 중요한(critical) 컴포넌트가 항상 먼저 스케줄링이 되도록 하는 데 사용된다.

기능을 사용해 본 후 사용하지 않기로 했다면, PodPriority 커맨드-라인 플래그를 제거하거나 false 로 설정한 후, API 서버와 스케줄러를 다시 시작해야 한다. 기능이 비활성화된 후, 기존 파드는 우선순위 필드를 유지하지만, 선점은 비활성화되며, 우선순위 필드는 무시된다. 이 기능이 비활성화되면, 새로운 파드에서 priorityClassName 을 설정할 수 없다.

선점을 비활성화하는 방법

주의: 중요 파드는 클러스터에 리소스 압박(resource pressure)이 가해지면 스케줄러 선점에 따라 스케줄링된다. 이런 이유로, 선점을 비활성화하지 않는 것을 권장한다.
참고: 쿠버네티스 1.15 이상에서, NonPreemptingPriority 기능이 활성화된 경우, 프라이어리티클래스는 옵션을 preemptionPolicy: Never 로 설정할 수 있다. 이렇게 하면 해당 프라이어리티클래스의 파드가 다른 파드를 축출할 수 없다.

선점은 기본값이 false로 설정된 disablePreemption kube-scheduler 플래그에 의해 제어된다. 위의 주의에도 불구하고 선점을 비활성화하려는 경우, disablePreemptiontrue 로 설정할 수 있다.

이 옵션은 컴포넌트 구성에서만 사용할 수 있으며 이전 스타일의 커맨드 라인 옵션에서는 사용할 수 없다. 다음은 선점을 비활성화하는 샘플 컴포넌트 구성이다.

apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
algorithmSource:
  provider: DefaultProvider

...

disablePreemption: true

프라이어리티클래스

프라이어리티클래스는 프라이어리티 클래스 이름에서 우선순위의 정수 값으로의 매핑을 정의하는 네임스페이스가 아닌(non-namespaced) 오브젝트이다. 이름은 프라이어리티클래스 오브젝트의 메타데이터의 name 필드에 지정된다. 값은 필수 value 필드에 지정되어 있다. 값이 클수록, 우선순위가 높다. 프라이어리티클래스 오브젝트의 이름은 유효한 DNS 서브 도메인 이름이어야 하며, system- 접두사를 붙일 수 없다.

프라이어리티클래스 오브젝트는 10억 이하의 32비트 정수 값을 가질 수 있다. 일반적으로 선점하거나 축출해서는 안되는 중요한 시스템 파드에는 더 큰 숫자가 예약되어 있다. 클러스터 관리자는 원하는 각 매핑에 대해 프라이어리티클래스 오브젝트를 하나씩 생성해야 한다.

프라이어리티클래스에는 globalDefaultdescription 두 개의 선택적인 필드도 있다. globalDefault 필드는 이 프라이어리티클래스의 값을 priorityClassName 이 없는 파드에 사용해야 함을 나타낸다. 시스템에서 globalDefaulttrue 로 설정된 프라이어리티클래스는 하나만 존재할 수 있다. globalDefault 가 설정된 프라이어리티클래스가 없을 경우, priorityClassName 이 없는 파드의 우선순위는 0이다.

description 필드는 임의의 문자열이다. 이 필드는 이 프라이어리티클래스를 언제 사용해야 하는지를 클러스터 사용자에게 알려주기 위한 것이다.

PodPriority와 기존 클러스터에 대한 참고 사항

  • 기존 클러스터를 업그레이드하고 이 기능을 활성화하면, 기존 파드의 우선순위는 사실상 0이다.

  • globalDefaulttrue 로 설정된 프라이어리티클래스를 추가해도 기존 파드의 우선순위는 변경되지 않는다. 이러한 프라이어리티클래스의 값은 프라이어리티클래스를 추가한 후 생성된 파드에만 사용된다.

  • 프라이어리티클래스를 삭제하면, 삭제된 프라이어리티클래스의 이름을 사용하는 기존 파드는 변경되지 않고 남아있지만, 삭제된 프라이어리티클래스의 이름을 사용하는 파드는 더 이상 생성할 수 없다.

프라이어리티클래스 예제

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "이 프라이어리티 클래스는 XYZ 서비스 파드에만 사용해야 한다."

비-선점 프라이어리티클래스

FEATURE STATE: Kubernetes v1.15 [alpha]

PreemptionPolicy: Never 를 가진 파드는 낮은 우선순위 파드의 스케줄링 대기열의 앞쪽에 배치되지만, 그 파드는 다른 파드를 축출할 수 없다. 스케줄링 대기 중인 비-선점 파드는 충분한 리소스가 확보되고 스케줄링될 수 있을 때까지 스케줄링 대기열에 대기한다. 다른 파드와 마찬가지로, 비-선점 파드는 스케줄러 백오프(back-off)에 종속된다. 이는 스케줄러가 이러한 파드를 스케줄링하려고 시도하고 스케줄링할 수 없는 경우, 더 적은 횟수로 재시도하여, 우선순위가 낮은 다른 파드를 미리 스케줄링할 수 있음을 의미한다.

비-선점 파드는 다른 우선순위가 높은 파드에 의해 축출될 수 있다.

PreemptionPolicy 는 기본값으로 PreemptLowerPriority 로 설정되어, 해당 프라이어리티클래스의 파드가 우선순위가 낮은 파드를 축출할 수 있다(기존의 기본 동작과 동일). PreemptionPolicyNever 로 설정된 경우, 해당 프라이어리티클래스의 파드는 비-선점될 것이다.

PreemptionPolicy 필드를 사용하려면 NonPreemptingPriority 기능 게이트가 활성화되어야 한다.

예제 유스케이스는 데이터 과학 관련 워크로드이다. 사용자는 다른 워크로드보다 우선순위가 높은 잡(job)을 제출할 수 있지만, 실행 중인 파드를 축출하여 기존의 작업을 삭제하지는 않을 것이다. 클러스터 리소스가 “자연스럽게” 충분히 사용할 수 있게 되면, PreemptionPolicy: Never 의 우선순위가 높은 잡이 다른 대기 중인 파드보다 먼저 스케줄링된다.

비-선점 프라이어리티클래스 예제

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "이 프라이어리티 클래스는 다른 파드를 축출하지 않는다."

파드 우선순위

프라이어리티클래스가 하나 이상 있으면, 그것의 명세에서 이들 프라이어리티클래스 이름 중 하나를 지정하는 파드를 생성할 수 있다. 우선순위 어드미션 컨트롤러는 priorityClassName 필드를 사용하고 우선순위의 정수 값을 채운다. 프라이어리티 클래스를 찾을 수 없으면, 파드가 거부된다.

다음의 YAML은 이전 예제에서 생성된 프라이어리티클래스를 사용하는 파드 구성의 예이다. 우선순위 어드미션 컨트롤러는 명세를 확인하고 파드의 우선순위를 1000000으로 해석한다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

스케줄링 순서에 대한 파드 우선순위의 영향

파드 우선순위가 활성화되면, 스케줄러가 우선순위에 따라 보류 중인 파드를 주문하고 보류 중인 파드는 스케줄링 대기열에서 우선순위가 낮은 다른 보류 중인 파드보다 우선한다. 결과적으로, 스케줄링 요구 사항이 충족되는 경우 우선순위가 더 낮은 파드보다 우선순위가 높은 파드가 더 빨리 스케줄링될 수 있다. 이러한 파드를 스케줄링할 수 없는 경우, 스케줄러는 계속 진행하고 우선순위가 낮은 다른 파드를 스케줄링하려고 한다.

선점

파드가 생성되면, 대기열로 이동하여 스케줄링을 기다린다. 스케줄러가 대기열에서 파드를 선택하여 노드에 스케줄링하려고 한다. 파드의 지정된 모든 요구 사항을 충족하는 노드가 없으면, 보류 중인 파드에 대해 선점 로직이 트리거된다. 보류 중인 파드를 P라 하자. 선점 로직은 P보다 우선순위가 낮은 하나 이상의 파드를 제거하면 해당 노드에서 P를 스케줄링할 수 있는 노드를 찾는다. 이러한 노드가 발견되면, 하나 이상의 우선순위가 낮은 파드가 노드에서 축출된다. 파드가 축출된 후, P는 노드에 스케줄링될 수 있다.

사용자 노출 정보

파드 P가 노드 N에서 하나 이상의 파드를 축출할 경우, 파드 P의 상태 nominatedNodeName 필드는 노드 N의 이름으로 설정된다. 이 필드는 스케줄러가 파드 P에 예약된 리소스를 추적하는 데 도움이 되고 사용자에게 클러스터의 선점에 대한 정보를 제공한다.

파드 P는 반드시 “지정된 노드"로 스케줄링되지는 않는다. 피해자 파드가 축출된 후, 그것은 정상적(graceful)으로 종료되는 기간을 갖는다. 스케줄러가 종료될 피해자 파드를 기다리는 동안 다른 노드를 사용할 수 있게 되면, 스케줄러는 파드 P를 스케줄링하기 위해 다른 노드를 사용한다. 그 결과, 파드 스펙의 nominatedNodeNamenodeName 은 항상 동일하지 않다. 또한, 스케줄러가 노드 N에서 파드를 축출했지만, 파드 P보다 우선순위가 높은 파드가 도착하면, 스케줄러가 노드 N에 새로운 우선순위가 높은 파드를 제공할 수 있다. 이러한 경우, 스케줄러는 파드 P의 nominatedNodeName 을 지운다. 이렇게하면, 스케줄러는 파드 P가 다른 노드에서 파드를 축출할 수 있도록 한다.

선점의 한계

선점 피해자의 정상적인 종료

파드가 축출되면, 축출된 피해자 파드는 정상적인 종료 기간을 가진다. 피해자 파드는 작업을 종료하고 빠져나가는 데(exit) 많은 시간을 가진다. 그렇지 않으면, 파드는 강제종료(kill) 된다. 이 정상적인 종료 기간은 스케줄러가 파드를 축출하는 지점과 보류 중인 파드(P)를 노드(N)에서 스케줄링할 수 있는 시점 사이의 시간 간격을 만든다. 그 동안, 스케줄러는 보류 중인 다른 파드를 계속 스케줄링한다. 피해자 파드가 빠져나가거나 종료되면, 스케줄러는 보류 대기열에서 파드를 스케줄하려고 한다. 따라서, 일반적으로 스케줄러가 피해자 파드를 축출하는 시점과 파드 P가 스케줄링된 시점 사이에 시간 간격이 있다. 이러한 차이를 최소화하기 위해, 우선순위가 낮은 파드의 정상적인 종료 기간을 0 또는 작은 수로 설정할 수 있다.

PodDisruptionBudget을 지원하지만, 보장하지 않음

Pod Disruption Budget(PDB)은 애플리케이션 소유자가 자발적 중단에서 동시에 다운된 복제된 애플리케이션의 파드 수를 제한할 수 있다. 쿠버네티스는 파드를 선점할 때 PDB를 지원하지만, PDB를 따르는 것이 최선의 노력이다. 스케줄러는 선점에 의해 PDB를 위반하지 않은 피해자 파드를 찾으려고 하지만, 그러한 피해자 파드가 발견되지 않으면, 선점은 여전히 발생하며, PDB를 위반하더라도 우선순위가 낮은 파드는 제거된다.

우선순위가 낮은 파드에 대한 파드-간 어피니티

이 질문에 대한 답변이 ‘예’인 경우에만 노드가 선점 대상으로 간주된다. “대기 중인 파드보다 우선순위가 낮은 모든 파드가 노드에서 제거되면, 보류 중인 파드를 노드에 스케줄링할 수 있습니까?”

참고: 선점으로 우선순위가 낮은 모든 파드를 반드시 제거할 필요는 없다. 우선순위가 낮은 모든 파드보다 적은 수를 제거하여 보류 중인 파드를 스케줄링할 수 있는 경우, 우선순위가 낮은 파드의 일부만 제거된다. 그럼에도 불구하고, 앞의 질문에 대한 대답은 ‘예’여야 한다. 답변이 ‘아니오’인 경우, 노드가 선점 대상으로 간주되지 않는다.

보류 중인 파드가 노드에 있는 하나 이상의 우선순위가 낮은 파드에 대한 파드-간 어피니티를 가진 경우에, 우선순위가 낮은 파드가 없을 때 파드-간 어피니티 규칙을 충족할 수 없다. 이 경우, 스케줄러는 노드의 파드를 축출하지 않는다. 대신, 다른 노드를 찾는다. 스케줄러가 적합한 노드를 찾거나 찾지 못할 수 있다. 보류 중인 파드를 스케줄링할 수 있다는 보장은 없다.

이 문제에 대한 권장 솔루션은 우선순위가 같거나 높은 파드에 대해서만 파드-간 어피니티를 생성하는 것이다.

교차 노드 선점

보류 중인 파드 P가 노드 N에 스케줄링될 수 있도록 노드 N이 선점 대상으로 고려되고 있다고 가정한다. 다른 노드의 파드가 축출된 경우에만 P가 N에서 실행 가능해질 수 있다. 예를 들면 다음과 같다.

  • 파드 P는 노드 N에 대해 고려된다.
  • 파드 Q는 노드 N과 동일한 영역의 다른 노드에서 실행 중이다.
  • 파드 P는 파드 Q(topologyKey: failure-domain.beta.kubernetes.io/zone)와 영역(zone) 전체의 안티-어피니티를 갖는다.
  • 영역에서 파드 P와 다른 파드 간의 안티-어피니티에 대한 다른 경우는 없다.
  • 노드 N에서 파드 P를 스케줄링하기 위해, 파드 Q를 축출할 수 있지만, 스케줄러는 교차-노드 선점을 수행하지 않는다. 따라서, 파드 P가 노드 N에서 스케줄링할 수 없는 것으로 간주된다.

파드 Q가 노드에서 제거되면, 파드 안티-어피니티 위반이 사라지고, 파드 P는 노드 N에서 스케줄링될 수 있다.

수요가 충분하고 합리적인 성능의 알고리즘을 찾을 경우 향후 버전에서 교차 노드 선점의 추가를 고려할 수 있다.

문제 해결

파드 우선순위와 선점은 원하지 않는 부작용을 가질 수 있다. 다음은 잠재적 문제의 예시와 이를 해결하는 방법이다.

파드가 불필요하게 선점(축출)됨

선점은 우선순위가 높은 보류 중인 파드를 위한 공간을 만들기 위해 리소스 압박을 받고 있는 클러스터에서 기존 파드를 제거한다. 실수로 특정 파드에 높은 우선순위를 부여하면, 의도하지 않은 높은 우선순위 파드가 클러스터에서 선점을 유발할 수 있다. 파드 우선순위는 파드 명세에서 priorityClassName 필드를 설정하여 지정한다. 그런 다음 우선순위의 정수 값이 분석되어 podSpecpriority 필드에 채워진다.

문제를 해결하기 위해, 해당 파드가 우선순위가 낮은 클래스를 사용하도록 priorityClassName 을 변경하거나, 해당 필드를 비워둘 수 있다. 빈 priorityClassName 은 기본값이 0으로 해석된다.

파드가 축출되면, 축출된 파드에 대한 이벤트가 기록된다. 선점은 클러스터가 파드에 대한 리소스를 충분히 가지지 못한 경우에만 발생한다. 이러한 경우, 선점은 보류 중인 파드(선점자)의 우선순위가 피해자 파드보다 높은 경우에만 발생한다. 보류 중인 파드가 없거나, 보류 중인 파드의 우선순위가 피해자 파드와 같거나 낮은 경우 선점이 발생하지 않아야 한다. 그러한 시나리오에서 선점이 발생하면, 이슈를 올리기 바란다.

파드가 축출되었지만, 선점자는 스케줄링되지 않음

파드가 축출되면, 요청된 정상적인 종료 기간(기본적으로 30초)이 주어진다. 이 기간 내에 대상 파드가 종료되지 않으면, 강제 종료된다. 모든 피해자 파드가 사라지면, 선점자 파드를 스케줄링할 수 있다.

선점자 파드가 피해자 파드가 없어지기를 기다리는 동안, 동일한 노드에 적합한 우선순위가 높은 파드가 생성될 수 있다. 이 경우, 스케줄러는 선점자 대신 우선순위가 높은 파드를 스케줄링한다.

이것은 예상된 동작이다. 우선순위가 높은 파드는 우선순위가 낮은 파드를 대체해야 한다. 클러스터 오토스케일링과 같은 다른 컨트롤러 작업은, 결국 보류 중인 파드를 스케줄링할 수 있는 용량을 제공할 수 있다.

우선순위가 높은 파드는 우선순위가 낮은 파드보다 우선함

스케줄러가 보류 중인 파드를 실행할 수 있는 노드를 찾으려고 한다. 노드를 찾지 못하면, 스케줄러는 임의의 노드에서 우선순위가 낮은 파드를 제거하여 보류 중인 파드를 위한 공간을 만든다. 우선순위가 낮은 파드가 있는 노드가 보류 중인 파드를 실행할 수 없는 경우, 스케줄러는 선점을 위해 우선순위가 높은 다른 노드(다른 노드의 파드와 비교)를 선택할 수 있다. 피해자 파드는 여전히 선점자 파드보다 우선순위가 낮아야 한다.

선점할 수 있는 여러 노드가 있는 경우, 스케줄러는 우선순위가 가장 낮은 파드 세트를 가진 노드를 선택하려고 한다. 그러나, 이러한 파드가 위반될 PodDisruptionBudget을 가지고 있고 축출된 경우 스케줄러는 우선순위가 높은 파드를 가진 다른 노드를 선택할 수 있다.

선점을 위해 여러 개의 노드가 존재하고 위의 시나리오 중 어느 것도 적용되지 않는 경우, 스케줄러는 우선순위가 가장 낮은 노드를 선택한다.

파드 우선순위와 서비스 품질 간의 상호 작용

파드 우선순위와 QoS 클래스QoS 클래스(서비스 품질 클래스)는 쿠버네티스가 클러스터 안의 파드들을 여러 클래스로 구분하고, 스케줄링과 축출(eviction)에 대한 결정을 내리는 방법을 제공한다. 는 상호 작용이 거의 없고 QoS 클래스를 기반으로 파드 우선순위를 설정하는 데 대한 기본 제한이 없는 두 개의 직교(orthogonal) 기능이다. 스케줄러의 선점 로직은 선점 대상을 선택할 때 QoS를 고려하지 않는다. 선점은 파드 우선순위를 고려하고 우선순위가 가장 낮은 대상 세트를 선택하려고 한다. 우선순위가 가장 높은 파드는 스케줄러가 선점자 파드를 스케줄링할 수 없거나 우선순위가 가장 낮은 파드가 PodDisruptionBudget 으로 보호되는 경우에만, 우선순위가 가장 낮은 파드를 축출 대상으로 고려한다.

QoS와 파드 우선순위를 모두 고려하는 유일한 컴포넌트는 kubelet 리소스 부족 축출이다. kubelet은 부족한 리소스의 사용이 요청을 초과하는지 여부에 따라, 그런 다음 우선순위에 따라, 파드의 스케줄링 요청에 대한 부족한 컴퓨팅 리소스의 소비에 의해 먼저 축출 대상 파드의 순위를 매긴다. 더 자세한 내용은 엔드유저 파드 축출을 참조한다.

kubelet 리소스 부족 축출은 사용량이 요청을 초과하지 않는 경우 파드를 축출하지 않는다. 우선순위가 낮은 파드가 요청을 초과하지 않으면, 축출되지 않는다. 요청을 초과하는 우선순위가 더 높은 다른 파드가 축출될 수 있다.

다음 내용

피드백