스케줄링 빌딩 블록 API와 workloadbuilder 라이브러리
스케줄링 빌딩 블록 API와 workloadbuilder 라이브러리 (Scheduling Building Block APIs and the workloadbuilder Library)
이 기능을 사용하려면 (클러스터 관리자나) 클러스터의 모든 관련 구성 요소에서 GenericWorkload 기능 게이트를 활성화해야 해요.
기능 게이트 활성화/비활성화에 대한 자세한 내용은 '기능 게이트 활성화 또는 비활성화' 문서를 참고하세요.
워크로드 인식 스케줄링(Workload-aware Scheduling)은 scheduling.k8s.io API 그룹 아래 재사용 가능한 API 빌딩 블록 집합을 정의해요. 컨트롤러 작성자는 이러한 프리미티브를 자신의 API에 임베드해서, 사용자가 생태계 전반의 일관된 스키마로 스케줄링 의도를 표현하게 해요(갱 스케줄링, 토폴로지, 중단 동작). 그리고 공유 workloadbuilder 라이브러리가 그 의도를 스케줄러 직면의 Workload, PodGroup, CompositePodGroup 객체로 컴파일해요.
오늘날 이러한 빌딩 블록의 내장 소비자는 WorkloadWithJob 기능 게이트로 게이팅된 Job 컨트롤러예요.
출처: 문서
본문
재사용 가능한 빌딩 블록 (Reusable building blocks)
빌딩 블록은 scheduling.k8s.io/v1alpha3 API 그룹의 강타입(strongly-typed) Go 구조체예요. 이들은 컨트롤러 작성자를 위한 것이에요. 컨트롤러는 이 구조체를 자신의 API 타입에 필드로 임베드하고, workloadbuilder 라이브러리가 그들을 컴파일해요. 타입 이름은 두 가지 규칙을 따르죠. 리프 수준 타입은 WorkloadPodGroup... 접두사(예: WorkloadPodGroupSchedulingPolicy)를, 다단계 변형은 WorkloadCompositePodGroup... 접두사를 사용해요.
각 컨트롤러는 자신의 API에 관용적인 필드 이름과 구조를 선택하므로, 이러한 블록은 고정된 최상위 형태를 부과하지 않아요. 엄격히 강제되지는 않지만 표준 필드 이름과 구조를 재사용하는 것이 권장돼요. 그래야 한 컨트롤러의 리소스에서 갱 스케줄링을 구성한 사용자가 다른 컨트롤러에서도 같은 옵션을 알아볼 수 있거든요.
빌딩 블록을 채택하려면 컨트롤러는 지원하고 싶은 블록을 자신의 타입에 필드로 추가해요. 예를 들어 Job API는 네 가지 모두를 spec.scheduling에서 접근 가능한 단일 타입으로 그룹화해요.
type JobSchedulingConfiguration struct {
SchedulingPolicy *schedulingv1alpha3.WorkloadPodGroupSchedulingPolicy
SchedulingConstraints *schedulingv1alpha3.WorkloadPodGroupSchedulingConstraints
DisruptionMode *schedulingv1alpha3.WorkloadPodGroupDisruptionMode
ResourceClaims []schedulingv1alpha3.WorkloadPodGroupResourceClaim
}
다른 컨트롤러는 다중 파트 워크로드의 구성 요소별로 같은 블록을 중첩하거나, 블록의 부분 집합만 지원할 수도 있어요.
스케줄링 정책 (Scheduling policy)
스케줄링 정책 블록은 PodGroup의 spec.schedulingPolicy와 같은 기본 및 갱 정책을 담아요. 각 정책의 의미와 스케줄러가 어떻게 적용하는지는 PodGroup 스케줄링 정책 문서를 참고하세요.
한 가지 차이는 그 블록의 갱 최소 개수(minimum count)가 선택 사항이라는 점이에요. 사용자는 minCount를 설정하지 않을 수 있으며, 이 경우 컨트롤러는 자신의 영역에 맞는 기본값을 제공해요. 예를 들어 Job 컨트롤러는 Job의 parallelism을 사용해요.
스케줄링 제약 (Scheduling constraints)
스케줄링 제약 블록은 '토폴로지 인식 워크로드 스케줄링'에 문서화된 토폴로지 제약을 담아요. 그룹의 모든 파드가 공유해야 하는 도메인(랙이나 존 같은)을 명명하는 노드 라벨 키로, 그룹당 최대 하나의 토폴로지 제약이 있어요. 컨트롤러는 생성 후 필드를 동결(freeze)해야 해요. 컴파일된 Workload에서 제약이 불변이기 때문이에요.
중단 모드 (Disruption mode)
중단 모드 블록은 그룹의 파드가 개별적으로(single) 또는 단위로만(all) 중단될 수 있는지 선택해요. 이는 '파드 그룹 중단과 우선순위' 문서에 문서화된 Pod 및 PodGroup 중단 모드에 해당해요.
라이브러리는 의미가 없는 조합을 거부해요. 예를 들어 BasicSchedulingPolicy가 있는 PodGroup에 대한 '모든 중단 금지(all)' 중단 모드를 방지해요. 선점 단위가 스케줄링 단위보다 클 수 없기 때문이에요. 파드 단위로 스케줄링된 그룹은 선점하거나 중단할 그룹 수준 단위가 없죠.
리소스 클레임 (Resource claims)
리소스 클레임 블록은 어떤 동적 리소스 할당 클레임이 파드마다 할당되는 대신 그룹의 모든 파드가 공유하는지 표현해요. 각 항목은 그룹 안의 클레임을 명명하고, 기존 ResourceClaim 또는 하나가 생성되는 ResourceClaimTemplate을 가리켜요. 그룹은 최대 네 개의 클레임을 선언할 수 있어요.
파드는 같은 이름을 사용하고 같은 객체를 참조해 자신의 spec에서 일치하는 클레임을 선언함으로써 그룹에 할당된 장치를 소비해요.
복합 빌딩 블록 (Composite building blocks)
다른 컨트롤러를 오케스트레이션하는 다단계 컨트롤러(예: Job을 만드는 JobSet)는 '그룹들의 그룹'(group of groups)을 조정해요. 그 계층을 위해 API는 WorkloadCompositePodGroup... 접두사(예: WorkloadCompositePodGroupSchedulingPolicy)가 붙은 유사한 프리미티브 집합을 제공해요. 이들은 리프 수준 블록과 같은 형태를 따르지만, 복합 갱 정책은 리프의 minCount 대신 minGroupCount(함께 스케줄링 가능해야 하는 자식 그룹의 최소 수)를 사용해요. 리프와 복합 타입을 구분하면 각 계층 수준이 독립적으로 진화할 수 있어요.
예시: Job 통합
Job 컨트롤러는 이러한 블록이 사용되는 내장 예시예요. 사용자는 Job의 spec.scheduling을 채우고, 컨트롤러는 그것을 Workload와 PodGroup으로 컴파일해요. spec.scheduling이 생략될 때 적용되는 기본값, 그리고 생성 후 변경할 수 있는 필드를 포함한 완전한 Job 매니페스트는 'Workload API와 통합' 문서를 참고하세요.
workloadbuilder 라이브러리
workloadbuilder는 컨트롤러의 스케줄링 의도를 스케줄러 직면의 Workload와 그 런타임 PodGroup/CompositePodGroup 객체로 바꾸는 공유 Go 라이브러리예요. 그래서 각 컨트롤러가 기본값 설정, 검증, 템플릿 컴파일을 다시 구현하지 않아도 돼요. 이는 트리 내(in-tree) 컨트롤러(예: Job 컨트롤러)와 트리 외(out-of-tree) 컨트롤러(예: JobSet 또는 Kubeflow TrainJob) 모두를 위해 설계됐으며, 이들은 다른 Go 의존성처럼 vendor해요. k8s.io/component-helpers/scheduling/schedulingv1/workloadbuilder에서 제공돼요.
라이브러리는 scheduling.k8s.io/v1alpha3 빌딩 블록을 소비해 scheduling.k8s.io/v1beta1 Workload와 PodGroup 객체로 컴파일하며, CompositePodGroup 객체는 scheduling.k8s.io/v1alpha3로 남아 있어요.
컨트롤러가 그것을 사용하는 방법
컨트롤러는 자신의 워크로드를 논리 구성 요소당 하나씩 WorkloadItem 노드의 트리로 설명해요. 자식이 없는 노드는 단일 PodGroupTemplate이 되고, 자식이 있는 노드는 그들 위의 CompositePodGroupTemplate이 되며, 이것이 다단계 컨트롤러가 '그룹들의 그룹'을 나타내는 방법이에요. 각 노드는 다음을 담아요.
- 기본 구성(default config): 사용자가 설정하지 않은 것에 대한 컨트롤러 자신의 기본값. 여기서 컨트롤러는 예를 들어 구성되지 않은 Job이 기본 스케줄링에 남도록 결정해요.
- 입력(input): 컨트롤러의 API에서 가져온 사용자의 의도. 컨트롤러는 각 빌딩 블록을 그것이 있는 필드 경로와 함께 기록해, 검증 오류가 사용자가 설정한 정확한 필드를 가리키게 해요.
- 선택적 콜백(callbacks): 병합된 구성을 조정. 이것이 컨트롤러가 컨텍스트별 기본값을 제공하는 방법이에요. 예를 들어 설정되지 않은 갱 최소 개수를 Job의
parallelism으로 채우는 것처럼요.
그런 다음 컨트롤러는 그 트리를 Builder에 넘기고 네 번의 호출로 작업해요.
NewBuilder는 트리에서, 그리고 생산될 객체의 이름, 네임스페이스, 소유자 참조와 함께 빌더를 구성해요. 소유자는 Workload의 컨트롤러 참조가 되며, 발견과 가비지 컬렉션에 사용돼요.Validate는 트리를 해석하고 어떤 문제든 필드 오류 목록으로 보고해요. 컨트롤러는 이를 자신의 API 검증에서 반환해요.BuildWorkload는 트리를 Workload로 컴파일해요. 결과는 캐시되므로 여러 PodGroup이 하나의 컴파일 결과에서 생성될 수 있어요.NewPodGroup은 컴파일된 템플릿 중 하나에서 런타임 PodGroup을 만들어, 빌드할 템플릿을 명명해요.
builder := workloadbuilder.NewBuilder(item, opts)
if errs := builder.Validate(ctx, workloadbuilder.ValidationInput{}); len(errs) > 0 {
// 요청을 거부합니다
}
workload, err := builder.BuildWorkload()
podGroup, err := builder.NewPodGroup("trainer-pg", item.Name)
이 흐름의 완전하고 실행 가능한 버전(Job 컨트롤러가 어떻게 연결하는지 포함)은 패키지 참조의 예시를 참고하세요.
스케줄링 구성 검증
Validate는 구성을 두 계층으로 확인해요.
- 빌딩 블록 자체의 구조적 검증: 필수 필드, 값 범위, 유니언의 정확히 한 멤버가 설정된다는 규칙, 그리고 불변성. 이러한 검사는 API 타입에서 생성된 선언적 검증 규칙에서 나와요.
- 선언적 검증이 표현할 수 없는 컨트롤러 정책 검사: 아래 설명된 허용 목록, 그리고 기본 정책과 함께 '모든' 중단 모드를 거부하는 것 같은 교차 필드 규칙.
또한 검증은 객체를 만들 때와 업데이트할 때가 다를 수 있어요. 업데이트 시 라이브러리는 추가로 생성 후 동결된 필드를 강제하므로, 컨트롤러는 새 구성과 함께 이전에 저장된 구성을 제공해야 해요. 만들 때는 비교할 것이 없으므로 그 검사는 적용되지 않아요.
선언적 검증에서 탈퇴하기
첫 번째 계층을 원하는지 여부는 컨트롤러가 실행되는 위치에 달려 있으며, 하나의 옵션이 그것을 제어해요.
- 트리 외 컨트롤러는 선언적 검증을 활성화된 채로(기본값) 둬요. 다른 무엇도 커스텀 리소스에 그 구조적 규칙을 적용하지 않으므로, 단일 Validate 호출이 두 계층을 모두 덮어요.
- 트리 내 컨트롤러는
DisableDeclarativeValidation을 설정해요. API 서버가 부모 객체를 검증하면서 임베드된 블록에 이미 선언적 검증을 실행하기 때문이에요. 첫 번째 계층을 건너뛰면 같은 필드를 두 번 확인하는 것을 피하고, Validate는 컨트롤러 정책 검사만 실행하게 돼요.
스케줄링 옵션 탈퇴/참여
빌딩 블록 타입은 컨트롤러 간에 공유되므로, 향후 릴리스에서 모든 컨트롤러에 맞지 않는 스케줄링 옵션을 추가할 수 있어요. 새 옵션이 조용히 새어 들어오는 것을 막기 위해 workloadbuilder는 허용 목록(allow-list) 모델을 사용해요. 컨트롤러는 자신이 지원하는 정책과 중단 모드를 선언하고, Validate는 그 집합 밖의 모든 것을 거부해, 잘못된 블록의 필드 경로에서 오류를 보고해요.
따라서 옵션은 기본적으로 거부돼요. 새 정책이 도입되면 기존 컨트롤러는 유지 관리자가 허용 목록을 확장할 때까지 계속 거부하는데, 트리 외 컨트롤러의 경우 vendor한 라이브러리 사본도 업데이트해야 한다는 뜻이에요.
builder := workloadbuilder.NewBuilder(item, workloadbuilder.BuildOptions{
Owner: owner,
AllowedPolicies: []workloadbuilder.SchedulingPolicyOption{workloadbuilder.BasicPolicy, workloadbuilder.GangPolicy},
AllowedDisruptionModes: []workloadbuilder.DisruptionModeOption{workloadbuilder.SingleMode, workloadbuilder.AllMode},
})
allErrs := builder.Validate(ctx, workloadbuilder.ValidationInput{})
기존 Workload에서 PodGroup 생성하기
Workload가 이미 존재할 때, 부모 컨트롤러가 컴파일했거나 수동으로 만든 경우, 런타임 PodGroup만 관리하는 자식 컨트롤러는 NewBuilderFromExistingWorkload를 대신 사용해요. 그 빌더는 자신의 소유자 참조를 사용해 제공된 Workload에서 PodGroup 객체를 만들어요. 그것은 아무것도 검증하거나 컴파일하지 않으므로, 기존 Workload는 다시 컴파일되지 않아요.
builder := workloadbuilder.NewBuilderFromExistingWorkload(parentWorkload, workloadbuilder.BuildOptions{Owner: owner})
podGroup, err := builder.NewPodGroup("trainer-pg", "trainer-pgt-0")
더 알아보기 (Learn more)
- PodGroup 스케줄링 정책에 대해 배워 보세요.
- 파드 그룹 중단과 우선순위에 대해 배워 보세요.
- 토폴로지 인식 워크로드 스케줄링에 대해 배워 보세요.
- Workload API 개요를 참고하세요.
- Job 컨트롤러가 Workload API와 통합하는 방법을 참고하세요.
- workloadbuilder 패키지 참조를 읽어 보세요.