Kubernetes Scheduler, 제대로 이해하기

2024. 3. 31. 23:59BACKEND

반응형

본 포스팅에서는 Kubernetes의 Scheduler의 개념을 이해하는 목표를 가집니다.

 

 

🔗  Kubernetes Series

모든 Kubernetes 시리즈를 확인하시려면 위를 참고해 주세요.

 

 


Pod가 특정 노드에 배치되는 이유를 알고 싶거나,

사용자 정의 스케줄러를 직접 구현할 계획이라면,

해당 포스팅에서 이해해보도록 하겠습니다.

 


Scheduling

Kubernetes에서 스케줄링은 Kublet이 Pods를 실행할 수 있도록 Nods와 매칭시키는 것을 의미합니다. 

 

Scheduler

스케줄러는 새로 생성된 Pod 중, 아직 노드에 할당되지 않은 Pod들을 찾아내고,

해당 Pod가 실행될 최적의 Node를 찾는 역할을 합니다.

 

 

kube-scheduler

🔗  kubernetes.io - Scheduling Framework

 

kube-scheduler는 워커 노드에서 Kubernetes pod의 스케줄링을 담당합니다.

 

한 Pod를 배포할 때 CPU, Memory, Affinity, Taints and Tolerations, Priority, Persistent Volumes (PV) 등과 같은 포드 요구 사항을 지정합니다.

 

스케줄러의 주된 역할은 생성된 요청을 식별하고,

요구 사항을 충족하는 Pod가 배치될 가장 적합한 노드를 선택하는 것입니다.

하나의 Kubernetes 클러스 내에는 하나 이상의 워커 노드가 존재할 수 있습니다.

 

 

그렇다면, 어떻게 스케줄러는 모든 워커 노드들 중에서 하나를 지정할까요?

 

스케줄러는 Scheduling cycleBinding cycle 두 단계로 되어 있고, 이를 둘을 합쳐 "scheduling context"라고 합니다.

 

Scheduling cycle최적의 노드를 선택하기 위해, filtering 과 scoring 작업을 수행하고,

Binding cyclebinding 이벤트를 생성하여 그 변화를 클러스터에 적용합니다.

 

 

 

 

Scheduling Cycle

1. Filtering

Filtering 단계에서는, Pod를 스케줄할 수 있는 가장 적합한 노드를 찾습니다.

 

가장 먼저 Pod 가 생성되면 Pod 는 Scheduling Queue 끝에 추가되어, 스케줄링 될 때까지 대기합니다.

스케줄러는 스케줄링을 위해 항상 우선순위가 높은 pod들을 우선순위가 낮은 pod들보다 먼저 배치합니다.

 

Scheduling Queue
1. ⚪️ ⚪️ ⚪️            ← 🟠  New pod
2. ⚪️ ⚪️ ⚪ 🟠       ← Sorting based on the priority defined on the pods 
3. 🟠 ⚪️ ⚪️ ⚪       (sorted)

 

 

이 때, 큐 내의 Pod 들은 Priority 에 따라 정렬됩니다.

우선 순위는 아래와 같이 PriorityClass 객체를 정의해서 Pod에 적용할 수 있습니다.

 

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

 

 

이제, 순차적으로 Pod를 스케줄할 수 있는 가장 적합한 노드를 찾습니다.

실행가능한 워커 노드를 모두 선택합니다.

Pod를 실행할 수 없는 노드는 여기서 필터링됩니다.

 

가령, 스케줄링할 차례가된 Pod인, 🟠 simple-webapp 에 대해 생각해봅시다.

🟠 simple-webapp 는 resource의 CPU request 가 10으로 지정되어 있습니다.

즉, 최소 CPU 10 을 실행할 수 있는 노드에 배치되어야 합니다.

 

🟠 :  simple-webapp(🔋10)

             ⬜️              ⬜️              ⬜️              ⬜️
          Node 1      Node 2     Node 3      Node 4
CPU :   ❌4           ❌4          🔋12           🔋16   

 

앞 두 노드는 리소스가 부족해 필터링 됩니다.

 

🟠 :  simple-webapp(🔋10)

                    ⬜️                ⬜️
               Node 3        Node 4
CPU :       🔋12              🔋16   

 

 

 

 

 

 

Scheduling Cycle

2.  Scoring

스케줄러가 필터링된 워커 노드들에 점수를 부여하여 노드들의 순위를 매깁니다.

 

그중, 가장 높은 순위를 가지는 노드가 Pod 스케줄링을 위해 선택됩니다.

만약 모든 노드들이 동일한 순위를 가지면, 한 노드가 임의로 선택됩니다.

 

스케줄러는 여러 스케줄링 플러그인들을 호출하여 스코어링을 합니다.

기본적으로, 각 노드에 해당 Pod에 필요한 CPU를 사용한 후 남은 공간을 수치로 점수를 매깁니다.

 

🟠 : simple-webapp (🔋10)

                    ⬜️                   ⬜️
               Node 3         Node 4
CPU :       🔋12              🔋16   
Weight:   🥈 2               🥇 6  

 

 

첫 번째 노드는 2, 두 번째 노드는 6 만큼의 CPU 사용량이 남습니다.
따라서, 두 번째 노드가 더 높은 점수를 받아 선택됩니다.

 

 

 

 

Binding cycle

3. Binding Event

노드가 선택되면, 스케줄러는 API서버에 Binding 이벤트를 생성합니다.

이때 Binding 이벤트란, Pod와 node를 Binding하는 이벤트를 의미합니다.

 

                    ⬜️                   ⬜️  ← 🟠 simple-webapp
               Node 3         Node 4
CPU :       🔋12              🔋16

 

 

 

 

 

 

Profile

스케줄링 프로파일을 사용하면 kube-scheduler에서 스케줄링의 각 단계들을 구성할 수 있다.

 

위에서 살펴본 각 단계마다, 플러그인이 연결될 수 있는 Extension Point이 존재하는데,

Extension Point 마다 플러그인을 자유롭게 커스터마이징하여 배치할 수 있습니다.

Extension Point들 중 하나 혹은 그 이상을 구현해서 스케줄링에 실행될 수 있게끔 제공합니다.

 

 

KubeSchedulerConfiguration

기본적으로, 플러그인에 대한 설정은 아래와 같은 정의를 통해 설정할 수 있습니다.

 

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - plugins:
      <<Extension Point>>:
        disabled:
        - name: <<Plugin Name>>
        enabled:
        - name: <<Plugin Name>>

 

 

.profiles.plugins 하위에 extension point 를 명시하고,

해당 지점에서 사용할/사용하지 않을 플러그인의 이름을 명시합니다.

 

가령 아래와 같이 score 단계에서 PodTopologySpread 는 비활성화하고,

MyCustomPluginA 와 MyCustomPluginB를 활성화 설정을 할 수 있습니다.

 

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - plugins:
      score:
        disabled:
        - name: PodTopologySpread
        enabled:
        - name: MyCustomPluginA
          weight: 2
        - name: MyCustomPluginB
          weight: 1
      preScore:
        disabled:
          - name: '*'

 

 

모든 디폴트 플러그인을 비활성화 시키고 싶다면, * 를 사용할 수 있습니다.

 

 

 

Extension Points

스케줄링은 아래의 순차적 단계로 이루어지며, Extension Point 으로 노출되어 커스텀할 수 있습니다.

 

https://kubernetes.io/docs/concepts/scheduling-eviction/scheduling-framework/

 

 

 

 

1. queueSort:

스케줄링 대기열에서 Pending 중인 파드를 정렬하는 데 사용되는 정렬 기능을 제공.

대기열 정렬 플러그인은 한 번에 정확히 하나만 활성화할 수 있음.


2. preFilter

필터링 전에 Pod 또는 클러스터에 대한 사전 처리나 정보를 확인하는 데 사용.

Pod에 unschedulable 표시를 할 수 있음.

unschedulable는 모든 Pod가 가지고 있는 속성이며, 기본 설정은 모두 true.

해당 노드에 false로 설정된 Pod가 없다는 걸 보장하는데 사용할 수 있음.


3. filter

스케줄링 정책의 Predicate와 동일하며 Pod를 실행할 수 없는 노드를 필터링하는 데 사용됨.

필터는 설정된 순서대로 호출.

모든 필터에 통과된 노드가 없으면, Pod는 unschedulable로 표시됨.

 

4. postFilter

Pod를 배치할 노드를 찾지 못했을 때, 구성된 순서대로 호출.

만약 어떤 postFilter 플러그인이 Pod를 schedulable로 표시한다면, 이후 플러그인들은 호출되지 않음.

 

5. preScore

Scoring 작업 수행 전, 사용할 수 있는 정보성 Extension Point.


6. score

필터링 단계를 통과한 각 노드에 점수를 매김.

이후 스케줄러는 가중치 합계가 가장 높은 노드를 선택.


7. reserve

지정된 Pod에 리소스가 예약된 경우, 플러그인에 알리는 정보성 Extension Point.

플러그인 구현 시, Reserve 실행 중 혹은 그 이후에 실패 시, 호출할 Unreserve 을 구현함.

 

8. permit

Pod 바인딩을 방지하거나 지연시킬 수 있음.

 

9. preBind

Pod가 바인딩되기 전, 필요한 모든 작업을 수행.


10. bind

Pod를 노드에 바인딩.

bind 플러그인은 순서대로 호출되며, 한 번 바인딩이 완료되면 나머지 플러그인은 건너 뜀.

적어도 하나 이상의 bind 플러그인 필요

 

11. postBind

Pod가 바인드된 후 호출되는 정보성 Extension Point

 

 

12. multiPoint

모든 적용 가능한 익스텐션 포인트에 대해, 플러그인들을 동시에 활성화하거나 비활성화할 수 있게 하는 환경 설정 전용 필드

 

 

 

 

Scheduling plugins

🔗 kubernetes.io: Scheduler Configuration

 

 

다음 플러그인은 기본적으로 사용하도록 설정되어 사용할 수 있습니다.


✔️ ImageLocality:  파드가 실행하는 컨테이너 이미지가 이미 있는 노드를 선호.

Extension points: score.


✔️ TaintToleration:  taints and tolerations 구현.

Implements extension points: filter, preScore, score.


✔️ NodeName: Pod 정의 시, .spec에 명시한 nodeName이 현재 노드와 일치하는지 확인 .

Extension points: filter.


✔️ NodePorts: Pod가 요구하는 포트들을 해당 노드에서 사용 가능한지 확인.

Extension points: preFilter, filter.


✔️ NodeAffinity:  node selectors 와 node affinity 구현.

Extension points: filter, score.


✔️ PodTopologySpread: Pod topology spread 구현.

Extension points: preFilter, filter, preScore, score.


✔️ NodeUnschedulable: .spec.unschedulable true로 설정된 노드를 필터링.

Extension points: filter.

 

✔️ NodeResourcesFit해당 노드에 파드가 요청하는 모든 리소스가 있는지 확인.

다음 3가지 전략 중 하나 사용 가능: LeastAllocated (default), MostAllocated and RequestedToCapacityRatio.

Extension points: preFilter, filter, score.

 

✔️ NodeResourcesBalancedAllocation: 파드가 스케줄된 경우, 해당 노드보다 균형잡힌 리소스 사용량을 얻을 수 있는 노드를 선호.

Extension points: score.

 

✔️ VolumeBinding: 노드에 요청된 Volume이 있는지 또는 바인딩할 수 있는지 확인.

Extension points: preFilter, filter, reserve, preBind, score.

 

Note: score Extension Point  VolumeCapacityPriority 기능이 활성화되어 있어야 활성화되며,

요청된 볼륨 사이즈를 만족하는 가장 작은 PV들을 우선순위 매깁니다.

 

 

✔️VolumeRestrictions: 노드에 마운트된 볼륨이 볼륨 제공자에 특정한 제한 사항을 충족하는지 확인.

Extension points: filter.


✔️ VolumeZone: 요청된 볼륨이 가질 수 있는 영역 요구 사항을 충족하는지 확인.

Extension points: filter.


✔️ NodeVolumeLimits:  노드에 대해 CSI 볼륨 제한을 충족할 수 있는지 확인.

Extension points: filter.

 

✔️EBSLimits: 노드에 대해 AWS EBS 볼륨 제한을 충족할 수 있는지 확인.

Extension points: filter.

 

✔️ GCEPDLimits: 노드에 대해 GCP-PD 볼륨 제한을 충족할 수 있는지 확인.

Extension points: filter.


✔️ AzureDiskLimits: 노드에 대해 Azure 디스크 볼륨 제한을 충족할 수 있는지 확인.

Extension points: filter.


✔️ InterPodAffinity: inter-Pod affinity and anti-affinity 구현.

Extension points: preFilter, filter, preScore, score.

 

✔️ PrioritySort: 기본 우선 순위 기반 정렬을 제공

Extension points: queueSort.

 

✔️ DefaultBinder: 기본 바인딩 메커니즘을 제공.

Extension points: bind.

 

✔️ DefaultPreemption: 기본 선점preemption 메커니즘을 제공

Extension points: postFilter.

 

기본적으로 활성화되지 않는 플러그인을 컴포넌트 구성 API를 통해 활성화할 수도 있습니다.


✔️ CinderLimits: 노드에 대해 OpenStack Cinder 볼륨 제한이 충족될 수 있는지 확인

Extension points: filter.

 

 

 

 

 

 

 

Multiple profiles

둘 이상의 프로파일을 실행하도록 kube-scheduler 를 구성할 수 있습니다.

여러 개의 KubeSchedulerConfiguration 객체를 정의할 수 있겠죠.

 

하지만, 각각 다른 프로세스이기 때문에, 결국 관리를 위한 추가적인 노력이 필요합니다.
더 중요한 건, 각각 다른 프로세스로 실행되면서, 스케줄링에 차질이 생길 수 있다는 것입니다.

가령, 한 스케줄러가 어떤 노드에서 작업을 스케줄링할 때,

다른 스케줄러가 동시에 같은 노드에서 작업 스케줄링을 하는 것을 모른다는 것인데요.

쿠버네티스 1.18 릴리즈에서 하나의 Scheduler에 대한 Multiple Profile 소개합니다.

한 scheduler 설정 파일 내에 다수의 프로파일을 구성할 수 있습니다.

 

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: default-scheduler
  - schedulerName: no-scoring-scheduler
    plugins:
      preScore:
        disabled:
        - name: '*'
      score:
        disabled:
        - name: '*'

 

 

하나의 관리 파일 내에 스케줄러 정책을 정의할 수 있게되었습니다.

 

여러 개의 스케줄러가 하나의 바이너리로 실행되는 것일 뿐,

각각의 스케줄러 자체도 분리된 스케줄러처럼 작동합니다.

 

각 프로파일에는 스케줄러 이름이 있으며,

Extension Point에 구성된 다른 플러그인 세트를 가질 수 있습니다.

 

 

 

 

 

 

 

그럼 여기까지, Node Scheduling에 대해 알아보았습니다.

 

 

 

 

 

| Reference |

 

🔗  Kubernetes.io - Official Docs

🔗  Udemy: certified-kubernetes-administrator-with-practice-tests

 

 

반응형