PodGroup 스케줄링

PodGroup 스케줄링 (PodGroup Scheduling)

FEATURE STATE: Kubernetes v1.35 [alpha](기본적으로 비활성화)

표준 Kubernetes 스케줄러는 Pod를 순차적으로 평가해요. 머신러닝 학습 작업 같은 여러 워크로드가 동시에 제출되면, 이 순차적 평가가 리소스 교착 상태(deadlock)를 일으킬 수 있어요. 예를 들어 경쟁하는 두 워크로드가 각자 자기 Pod의 일부만 스케줄링해서 클러스터 용량을 소비하지만, 어느 워크로드도 완전히 시작하기에 충분한 리소스를 남기지 못할 수 있죠.

PodGroup 스케줄링 주기는 Pod 그룹을 하나의 단위로 평가해요. 스케줄러는 그룹의 모든 Pod에 대한 배치를 동시에 찾으려고 해요. 전체 그룹의 요구사항을 충족할 충분한 리소스를 찾지 못하면 어떤 Pod도 바인딩하지 않아요.

추가로, 그룹을 하나의 통합 엔티티로 취급하면 다른 그룹 기반 스케줄링 기능의 구현을 단순화하는 기반 아키텍처가 마련돼요.

이 기능은 Workload API에 의존해요. 클러스터에 GenericWorkload 기능 게이트와 scheduling.k8s.io/v1alpha1 API 그룹을 활성화했는지 확인하세요.

PodGroup 스케줄링 주기

Pod 그룹을 함께 스케줄링하기 위해 kube-scheduler는 PodGroup 스케줄링 주기를 사용해요. Pod를 개별적으로 처리하면서 WaitOnPermit 게이트에 붙잡아 두는 대신, 스케줄러는 특정 PodGroup에 속한 모든 pending Pod 그룹을 집합적으로 평가해요. 각 Pod에 대해 별도의 스케줄링 주기를 실행하는 대신 전체 그룹의 적합성을 평가하고 그 후에 직접 바인딩 단계로 넘어가요.

스케줄러가 PodGroup에 속한 Pod를 꺼내면, 그 그룹의 다른 대기 중인 Pod를 모두 검색해요. 그런 다음 우선순위와 스케줄러가 처음 관찰한 시간을 기준으로 결정적으로 정렬하고, 다음과 같이 PodGroup 스케줄링 주기를 시작해요.

클러스터 상태 스냅샷: 스케줄러가 PodGroup 평가를 시작할 때 주기 전체 동안 지속되는 클러스터 상태의 단일 스냅샷을 찍어요. 이렇게 하면 그룹 전체에 대해 평가가 일관되게 유지되고 다른 이벤트로 인한 경쟁 조건을 방지해요.

실현 가능한 배치 찾기: 스케줄러는 PodGroup 스케줄링 알고리즘을 실행해 그룹의 Pod에 대한 유효한 Node 배치를 찾아요.

원자적 결정: 알고리즘 결과에 따라 스케줄링 결정이 전체 PodGroup에 대해 원자적으로 적용돼요.

성공: 스케줄러가 Pod에 대해 충분한 리소스와 유효한 배치를 찾으면(예: 갱 스케줄링의 minCount 제약 충족), 해당 Pod는 선택된 노드와 함께 직접 바인딩 주기로 진행해요. 남은 스케줄링 불가 Pod는 스케줄링 큐로 돌아가서 가용 리소스를 기다리며 이미 스케줄링된 Pod에 합류하게 돼요.

게다가 다른 Pod가 이미 스케줄링된 후에 PodGroup에 새 Pod가 추가되면, 주기는 기존 Pod를 고려하면서 새 Pod를 평가해요.

실패: 스케줄러가 충분한 리소스를 찾지 못해 PodGroup을 실현 가능하게 만들지 못하면(예: minCount 제약을 충족하지 못하는 경우) 전체 PodGroup이 스케줄링 불가로 간주돼요.

스케줄러는 PodGroupPostFilter 확장점을 실행해 PodGroup을 스케줄링 가능하게 만들려고 시도해요. DefaultPreemption 플러그인의 PodGroupPostFilter 확장점이 워크로드 인지 선점(workload aware preemption)을 실행해요. PodGroupPostFilter 확장점을 구현한 어떤 플러그인이 Success를 반환하면, 더 이상 PodGroupPostFilter 확장점에 대한 플러그인을 실행하지 않아요.

PodGroupPostFilter 플러그인이 Success를 반환해도 어떤 Pod도 바인딩되지 않아요. 대신 모두 스케줄링 큐로 돌아가서, PodGroupPostFilter가 취한 조치가 다음 스케줄링 주기에서 PodGroup을 스케줄링 가능하게 만들기를 기대해요. 표준 스케줄링 백오프(backoff) 로직이 적용되어 PodGroup을 나중에 재시도할 수 있어요.

이 단일 주기 접근 방식을 통해 스케줄러는 부분적으로 스케줄링된 그룹이 그룹의 나머지가 들어맞기를 무한정 기다리면서 클러스터 용량을 예약하는 비효율적인 병목 현상을 피해요.

PodGroup 스케줄링 알고리즘

기본 PodGroup 스케줄링 알고리즘은 기본 Pod 기반 스케줄링 알고리즘에 크게 의존해요. 각 Pod를 반복하면서 다음을 수행해요.

  • 표준 Pod별 필터링과 점수(score) 단계로 실현 가능한 노드를 찾아요.
  • Pod가 들어맞으면 해당 Pod는 선택된 노드에 일시적으로 assumed되고 스케줄링 알고리즘이 끝날 때까지 예약돼요. Pod가 들어맞지 않으면 스케줄러는 PostFilter 확장점을 실행하지 않아요. 대신 전체 PodGroup에 대해 실행되는 PodGroupPostFilter 확장점에 의존해요.
  • 그룹의 각 Pod를 누적 스케줄링 결과에 대해 평가한 후에 PlacementFeasible 확장점을 호출해서 스케줄링 가능한 Pod가 그룹의 스케줄링 기준(예: 갱 스케줄링의 minCount)을 충족하는지 확인해요. 어떤 Pod에 대해 Success 상태를 반환하면 PodGroup이 실현 가능한 것으로 간주돼요. 알고리즘이 Success 상태 없이 모든 Pod를 처리했거나 이 주기 동안 어떤 Pod도 스케줄링되지 않으면 PodGroup은 스케줄링 불가로 간주돼요.

배치 스케줄링 알고리즘

FEATURE STATE: Kubernetes v1.36 [alpha](기본적으로 비활성화)

배치 스케줄링 알고리즘은 PodGroup 스케줄링 알고리즘의 대안으로, 스케줄링 플러그인을 사용해 고려 중인 PodGroup에 대한 최적의 배치를 찾아요. 사용자는 플러그인을 사용하고 구성해서 알고리즘을 자신의 특정 요구에 맞출 수 있어요.

알고리즘은 주어진 PodGroup에 대해 세 가지 주요 단계로 진행돼요.

1단계: 후보 배치 생성

PodGroup 할당에 이론적으로 실현 가능한 노드 부분집합인 후보 배치를 생성해요. 예를 들어 PodGroup의 스케줄링 제약(커스텀 리소스인 PodGroup 객체에 정의할 수 있음)을 기반으로 해요.

이 단계는 PlacementGeneratePlugin 확장점으로 실행돼요.

2단계: Pod 수준 필터링과 실현 가능성 검사

각 제안된 배치를 기본 PodGroup 스케줄링 알고리즘을 실행해 검증해서, PodGroup의 요구되는 수의 Pod가 들어맞을 수 있는지 확인해요. 들어맞을 수 있으면 해당 배치는 실현 가능한 것으로 표시돼요.

3단계: 배치 점수와 선택

모든 실현 가능한 배치에 점수를 매겨 PodGroup에 대한 최적의 도메인을 선택해요.

이 단계는 PlacementScorePlugin 확장점으로 실행돼요.

제한 사항

PodGroup 스케줄링 알고리즘은 특정 Pod 정렬에 의존하며, 그룹의 Pod를 다른 순서로 처리했다면 발견할 수 있었을 유효한 배치를 찾지 못할 수도 있어요. 특히:

  • 기본적인 동종 Pod 그룹(모든 Pod가 동일한 스케줄링 요구사항을 갖고 어피니티, 안티어피니티, 토폴로지 분산 제약 같은 Pod 간 의존성이 없는 경우)에서는 배치가 존재한다면 찾을 것으로 기대돼요.
  • 이종 Pod 그룹에서는 유효한 배치를 찾는다는 보장이 없어요.
  • Pod 간 의존성이 있는 Pod 그룹에서는 유효한 배치를 찾는다는 보장이 없어요.

위에 더해, 그룹 내 의존성(Pod 간 어피니티를 통해 한 Pod의 스케줄링 가능성이 다른 그룹 구성원에 의존하는 경우)이 있는 경우, 이 알고리즘은 결정적 처리 순서 때문에 클러스터 상태와 무관하게 배치를 찾지 못할 수 있어요.

주기 전체에서 일관된 동작을 위해, 알고리즘은 단일 PodGroup에 속한 모든 Pod가 같은 .spec.schedulerName을 공유해야 해요. 이 요구사항은 주기가 시작되기 전에 검증되며, 제약을 충족하지 못하면 PodGroup은 거부돼요.

CompositePodGroup을 사용한 계층적 스케줄링

FEATURE STATE: Kubernetes v1.37 [alpha](기본적으로 비활성화)

CompositePodGroup 기능 게이트와 scheduling.k8s.io/v1alpha3 API 그룹이 활성화되면, 스케줄러는 PodGroup 스케줄링 주기를 확장해 다중 수준 그룹 계층을 지원해요.

계층적 워크로드에서 Pod는 리프(leaf) PodGroup 객체에 속하고, 리프 PodGroup은 루트 CompositePodGroup까지 상위 CompositePodGroup 리소스를 지정해요. 스케줄러는 전체 그룹 계층을 하나의 통합된 스케줄링 단위로 평가해요.

계층적 스케줄링 주기 실행

루트 CompositePodGroup을 관찰한 후, 그 루트의 그룹 계층에 속한 pending Pod가 하나 이상 있는 한 스케줄러는 이를 스케줄링 큐에 넣어요.

루트 CompositePodGroup이 계층적 쿼럼을 충족하면 활성 스케줄링 큐에 들어가고, 거기서 스케줄링 주기에 의해 꺼내질 수 있어요. CompositePodGroup의 스케줄링 주기 흐름은 다음과 같아요.

통합 클러스터 스냅샷과 검증: 스케줄러는 클러스터 리소스의 스냅샷을 찍어 가장 최근에 관찰된 클러스터 상태를 반영해요. 큐에서 꺼낸 그룹 계층의 형태가 스냅샷된 계층과 일치하는지 확인하고, 계층 구성의 일관성(멤버 Pod 간 동일한 .spec.schedulerName과 우선순위 같은 것)을 검증해요.

계층 형태가 동시에 변경되었거나 검증에 실패하면 주기는 중단되고 루트 CompositePodGroup을 다시 큐에 넣어요.

하향식 후보 배치 생성: 계층의 각 수준에서, 하위 그룹을 평가하기 전에 스케줄러는 PlacementGeneratePlugin 플러그인을 호출해 평가 중인 그룹에 대한 후보 배치(노드 부분집합)를 생성해요.

하위 CompositePodGroup 또는 리프 PodGroup에 대한 후보 배치는 상위 그룹의 현재 평가 배치에 속한 노드 부분집합에서만 생성돼요. 루트 CompositePodGroup의 경우 후보 배치는 사용 가능한 모든 클러스터 노드에 걸쳐 생성돼요.

재귀적 하위 트리 시뮬레이션과 실현 가능성 검사: 스케줄러는 CompositePodGroup에 대한 각 후보 배치를 평가할 때 그 배치를 클러스터 스냅샷에 임시로 assumed하고 하위 그룹을 재귀적으로 스케줄링해요.

  • 재귀적 순회: 스케줄러는 사전 정렬된 순서로 하위 그룹을 순회하며, CompositePodGroup에서 리프 PodGroup 객체까지 스스로를 재귀적으로 호출해요. 각 리프 PodGroup에 대해 스케줄러는 상위의 후보 배치로 범위가 제한된 배치 스케줄링 알고리즘을 실행해 메모리에서 임시 Pod 할당을 만들어요.
  • 실현 가능성 검사: 각 하위 그룹을 평가한 후 스케줄러는 PlacementFeasible 플러그인을 호출해 상위 그룹의 스케줄링 정책을 여전히 충족할 수 있는지 확인해요. 정책 제약이 여전히 달성 가능하면(또는 이미 충족되었으면) 스케줄러는 이후 형제 그룹 평가를 계속해요. 정책 제약을 더 이상 충족할 수 없으면 스케줄러는 즉시 해당 CompositePodGroup의 평가를 중단하고 그 그룹에 대해 만든 모든 임시 메모리 Pod 할당을 되돌려요.
  • 시뮬레이션 롤백: 후보 배치를 시뮬레이션한 후(성공이든 실패든) 스케줄러는 다음 후보 배치를 평가하기 전에 그 시뮬레이션 동안 만든 임시 노드 예약을 되돌려요.

배치 점수와 하위 트리 커밋: CompositePodGroup에 대한 모든 후보 배치가 평가된 후:

  • 점수: 하나 이상의 후보 배치가 실현 가능하면 스케줄러는 PlacementScorePlugin 플러그인을 호출해 모든 실현 가능한 후보 배치에 점수를 매겨요. 점수 플러그인은 하위 트리 안의 모든 하위 리프 PodGroup 객체에 걸친 결합된 제안 Pod 할당을 평가하고 가장 높은 총점을 가진 배치를 선택해요.
  • 커밋: 승리한 배치를 선택한 후 스케줄러는 그 최적 배치에 해당하는 임시 Pod 할당을 메모리에서 커밋(assumed)해요. 이렇게 하면 이후의 형제 그룹 평가나 상위 수준 평가가 그 하위 트리에 대해 일관되고 최적의 스케줄링 결정을 관찰하도록 보장해요.

생성된 모든 후보 중 실현 가능한 배치를 찾지 못하면 전체 CompositePodGroup이 스케줄링 불가로 간주돼요.

원자적 바인딩: 루트 수준 재귀 평가가 성공하고 Pod 하나 이상이 성공적으로 스케줄링되면, 스케줄러는 Pod 할당을 커밋하며 바인딩 주기로 진행해요.

그렇지 않으면 전체 CompositePodGroup이 스케줄링 불가로 간주돼요.

제한 사항

PodGroup 스케줄링 알고리즘이 특정 Pod 정렬에 의존하는 것과 비슷하게, 계층적 스케줄링 알고리즘은 모든 CompositePodGroup 계층 안에서 하위 그룹의 특정 정렬에 의존해요. 결과적으로 하위 그룹을 다른 순서로 처리했다면 발견했을 유효한 배치를 찾지 못할 수 있어요.

또한 스케줄러가 탐욕적(greedy) 접근 방식으로 그룹 계층을 평가하기 때문에:

  • 클러스터에 배치가 존재하더라도 모든 경우에 유효한 배치를 찾는다는 보장이 없어요.
  • 스케줄러가 CompositePodGroup 계층에 대해 유효한 배치를 성공적으로 찾더라도, 그 배치가 클러스터 전체에서 최적이라는 보장은 없어요.

PodGroup 조건

PodGroup 스케줄링 주기가 끝나면 스케줄러는 PodGroup의 status.conditions에 조건을 갱신해요.

  • PodGroupInitiallyScheduled: PodGroup이 처음으로 성공적으로 스케줄링되었는지 보고해요.

PodGroupInitiallyScheduled

스케줄링 주기가 성공하면 조건은 reason Scheduled로 True로 설정돼요. 갱 정책 PodGroup의 경우 최소 minCount Pod가 배치되었음을 의미해요.

스케줄링이 실패하면 조건은 다음 이유 중 하나로 False로 설정돼요.

  • Unschedulable — 리소스 제약, 어피니티 또는 안티어피니티 규칙, 또는 갱에 대한 용량 부족 때문에 그룹을 배치할 수 없음.
  • SchedulerError — 내부 스케줄러 오류(예: nodeAffinity 같은 스케줄링 제약 구문 분석 중)로 스케줄링 실패.

이 조건이 한번 True로 설정되면 다시 바뀌지 않아요.

조건은 다음으로 확인할 수 있어요.

kubectl get podgroup <name> -o jsonpath='{.status.conditions}'

CompositePodGroup 조건

FEATURE STATE: Kubernetes v1.37 [alpha](기본적으로 비활성화)

CompositePodGroup API도 status.conditions 필드를 노출해요.

다만 v1.37에서 스케줄러는 이 필드를 채우지 않아요.

더 알아보기

  • Workload API에 대해 알아보세요.
  • CompositePodGroup API와 그 수명 주기를 읽어 보세요.
  • 토폴로지 인지 워크로드 스케줄링에 대해 알아보세요.
  • Pod에서 Workload를 참조하는 방법을 보세요.
  • 갱 스케줄링에 대해 읽어 보세요.