Skip to content

Commit e1edb4c

Browse files
authored
Add batch-scheduler option, deprecate enable-batch-scheduler option (#2300)
1 parent 13eb7b2 commit e1edb4c

File tree

10 files changed

+377
-81
lines changed

10 files changed

+377
-81
lines changed

helm-chart/kuberay-operator/templates/deployment.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,14 @@ spec:
5757
args:
5858
{{- $argList := list -}}
5959
{{- $argList = append $argList (include "kuberay.featureGates" . | trim) -}}
60+
{{- if .Values.batchScheduler -}}
6061
{{- if .Values.batchScheduler.enabled -}}
6162
{{- $argList = append $argList "--enable-batch-scheduler" -}}
6263
{{- end -}}
64+
{{- if .Values.batchScheduler.name -}}
65+
{{- $argList = append $argList (printf "--batch-scheduler=%s" .Values.batchScheduler.name) -}}
66+
{{- end -}}
67+
{{- end -}}
6368
{{- $watchNamespace := "" -}}
6469
{{- if and .Values.singleNamespaceInstall (not .Values.watchNamespace) -}}
6570
{{- $watchNamespace = .Release.Namespace -}}

helm-chart/kuberay-operator/values.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,33 @@ readinessProbe:
5555
periodSeconds: 5
5656
failureThreshold: 5
5757

58+
# Enable customized Kubernetes scheduler integration. If enabled, Ray workloads will be scheduled
59+
# by the customized scheduler.
60+
# * "enabled" is the legacy option and will be deprecated soon.
61+
# * "name" is the standard option, expecting a scheduler name, supported values are
62+
# "default", "volcano", and "yunikorn".
63+
#
64+
# Examples:
65+
# 1. Use volcano (deprecated)
66+
# batchScheduler:
67+
# enabled: true
68+
#
69+
# 2. Use volcano
70+
# batchScheduler:
71+
# name: volcano
72+
#
73+
# 3. Use yunikorn
74+
# batchScheduler:
75+
# name: yunikorn
76+
#
5877
batchScheduler:
78+
# Deprecated. This option will be removed in the future.
79+
# Note, for backwards compatibility. When it sets to true, it enables volcano scheduler integration.
5980
enabled: false
81+
# Name of the scheduler, currently supported "default", "volcano" and "yunikorn",
82+
# set the customized scheduler name, e.g "volcano" or "yunikorn", do not set
83+
# "batchScheduler.enabled=true" at the same time as it will override this option.
84+
name: default
6085

6186
featureGates:
6287
- name: RayClusterStatusConditions
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package v1alpha1
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/go-logr/logr"
7+
8+
"github.com/ray-project/kuberay/ray-operator/controllers/ray/batchscheduler/volcano"
9+
"github.com/ray-project/kuberay/ray-operator/controllers/ray/batchscheduler/yunikorn"
10+
)
11+
12+
func ValidateBatchSchedulerConfig(logger logr.Logger, config Configuration) error {
13+
if config.EnableBatchScheduler {
14+
logger.Info("Feature flag enable-batch-scheduler is deprecated and will not be supported soon. " +
15+
"Use batch-scheduler instead. ")
16+
return nil
17+
}
18+
19+
if len(config.BatchScheduler) > 0 {
20+
// default option, no-opt.
21+
if config.BatchScheduler == "default" {
22+
return nil
23+
}
24+
25+
// if a customized scheduler is configured, check it is supported
26+
if config.BatchScheduler == volcano.GetPluginName() || config.BatchScheduler == yunikorn.GetPluginName() {
27+
logger.Info("Feature flag batch-scheduler is enabled",
28+
"scheduler name", config.BatchScheduler)
29+
} else {
30+
return fmt.Errorf("scheduler is not supported, name=%s", config.BatchScheduler)
31+
}
32+
}
33+
return nil
34+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package v1alpha1
2+
3+
import (
4+
"testing"
5+
6+
"github.com/go-logr/logr"
7+
"github.com/go-logr/logr/testr"
8+
9+
"github.com/ray-project/kuberay/ray-operator/controllers/ray/batchscheduler/volcano"
10+
"github.com/ray-project/kuberay/ray-operator/controllers/ray/batchscheduler/yunikorn"
11+
)
12+
13+
func TestValidateBatchSchedulerConfig(t *testing.T) {
14+
type args struct {
15+
logger logr.Logger
16+
config Configuration
17+
}
18+
tests := []struct {
19+
name string
20+
args args
21+
wantErr bool
22+
}{
23+
{
24+
name: "legacy option, enable-batch-scheduler=false",
25+
args: args{
26+
logger: testr.New(t),
27+
config: Configuration{
28+
EnableBatchScheduler: false,
29+
},
30+
},
31+
wantErr: false,
32+
},
33+
{
34+
name: "legacy option, enable-batch-scheduler=true",
35+
args: args{
36+
logger: testr.New(t),
37+
config: Configuration{
38+
EnableBatchScheduler: true,
39+
},
40+
},
41+
wantErr: false,
42+
},
43+
{
44+
name: "valid option, batch-scheduler=yunikorn",
45+
args: args{
46+
logger: testr.New(t),
47+
config: Configuration{
48+
BatchScheduler: yunikorn.GetPluginName(),
49+
},
50+
},
51+
wantErr: false,
52+
},
53+
{
54+
name: "valid option, batch-scheduler=volcano",
55+
args: args{
56+
logger: testr.New(t),
57+
config: Configuration{
58+
BatchScheduler: volcano.GetPluginName(),
59+
},
60+
},
61+
wantErr: false,
62+
},
63+
{
64+
name: "invalid option, invalid scheduler name",
65+
args: args{
66+
logger: testr.New(t),
67+
config: Configuration{
68+
EnableBatchScheduler: false,
69+
BatchScheduler: "unknown-scheduler-name",
70+
},
71+
},
72+
wantErr: true,
73+
},
74+
}
75+
76+
for _, tt := range tests {
77+
t.Run(tt.name, func(t *testing.T) {
78+
t.Logf(tt.name)
79+
if err := ValidateBatchSchedulerConfig(tt.args.logger, tt.args.config); (err != nil) != tt.wantErr {
80+
t.Errorf("ValidateBatchSchedulerConfig() error = %v, wantErr %v", err, tt.wantErr)
81+
}
82+
})
83+
}
84+
}

ray-operator/apis/config/v1alpha1/configuration_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ type Configuration struct {
4343
// Defaults to `json` if empty.
4444
LogStdoutEncoder string `json:"logStdoutEncoder,omitempty"`
4545

46+
// BatchScheduler enables the batch scheduler integration with a specific scheduler
47+
// based on the given name, currently, supported values are volcano and yunikorn.
48+
BatchScheduler string `json:"batchScheduler,omitempty"`
49+
4650
// HeadSidecarContainers includes specification for a sidecar container
4751
// to inject into every Head pod.
4852
HeadSidecarContainers []corev1.Container `json:"headSidecarContainers,omitempty"`
Lines changed: 60 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,92 @@
11
package batchscheduler
22

33
import (
4-
"fmt"
54
"sync"
65

76
"k8s.io/apimachinery/pkg/runtime"
8-
"k8s.io/client-go/rest"
97
"sigs.k8s.io/controller-runtime/pkg/builder"
108

11-
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
12-
schedulerinterface "github.com/ray-project/kuberay/ray-operator/controllers/ray/batchscheduler/interface"
9+
configapi "github.com/ray-project/kuberay/ray-operator/apis/config/v1alpha1"
1310
"github.com/ray-project/kuberay/ray-operator/controllers/ray/batchscheduler/volcano"
1411
"github.com/ray-project/kuberay/ray-operator/controllers/ray/batchscheduler/yunikorn"
12+
13+
"k8s.io/client-go/rest"
14+
15+
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
16+
schedulerinterface "github.com/ray-project/kuberay/ray-operator/controllers/ray/batchscheduler/interface"
1517
"github.com/ray-project/kuberay/ray-operator/controllers/ray/utils"
1618
)
1719

18-
var schedulerContainers = map[string]schedulerinterface.BatchSchedulerFactory{
19-
schedulerinterface.GetDefaultPluginName(): &schedulerinterface.DefaultBatchSchedulerFactory{},
20-
volcano.GetPluginName(): &volcano.VolcanoBatchSchedulerFactory{},
21-
yunikorn.GetPluginName(): &yunikorn.YuniKornSchedulerFactory{},
22-
}
23-
24-
func GetRegisteredNames() []string {
25-
var pluginNames []string
26-
for key := range schedulerContainers {
27-
pluginNames = append(pluginNames, key)
28-
}
29-
return pluginNames
20+
type SchedulerManager struct {
21+
config *rest.Config
22+
factory schedulerinterface.BatchSchedulerFactory
23+
scheduler schedulerinterface.BatchScheduler
24+
rayConfigs configapi.Configuration
25+
sync.Mutex
3026
}
3127

32-
func ConfigureReconciler(b *builder.Builder) *builder.Builder {
33-
for _, factory := range schedulerContainers {
34-
b = factory.ConfigureReconciler(b)
28+
// NewSchedulerManager maintains a specific scheduler plugin based on config
29+
func NewSchedulerManager(rayConfigs configapi.Configuration, config *rest.Config) (*SchedulerManager, error) {
30+
// init the scheduler factory from config
31+
factory := getSchedulerFactory(rayConfigs)
32+
scheduler, err := factory.New(config)
33+
if err != nil {
34+
return nil, err
3535
}
36-
return b
37-
}
3836

39-
func AddToScheme(scheme *runtime.Scheme) {
40-
for _, factory := range schedulerContainers {
41-
factory.AddToScheme(scheme)
37+
manager := SchedulerManager{
38+
rayConfigs: rayConfigs,
39+
config: config,
40+
factory: factory,
41+
scheduler: scheduler,
4242
}
43-
}
4443

45-
type SchedulerManager struct {
46-
config *rest.Config
47-
plugins map[string]schedulerinterface.BatchScheduler
48-
sync.Mutex
44+
return &manager, nil
4945
}
5046

51-
func NewSchedulerManager(config *rest.Config) *SchedulerManager {
52-
manager := SchedulerManager{
53-
config: config,
54-
plugins: make(map[string]schedulerinterface.BatchScheduler),
47+
func getSchedulerFactory(rayConfigs configapi.Configuration) schedulerinterface.BatchSchedulerFactory {
48+
var factory schedulerinterface.BatchSchedulerFactory
49+
// init with the default factory
50+
factory = &schedulerinterface.DefaultBatchSchedulerFactory{}
51+
// when a batch scheduler name is provided
52+
if len(rayConfigs.BatchScheduler) > 0 {
53+
switch rayConfigs.BatchScheduler {
54+
case volcano.GetPluginName():
55+
factory = &volcano.VolcanoBatchSchedulerFactory{}
56+
case yunikorn.GetPluginName():
57+
factory = &yunikorn.YuniKornSchedulerFactory{}
58+
default:
59+
factory = &schedulerinterface.DefaultBatchSchedulerFactory{}
60+
}
5561
}
56-
return &manager
57-
}
5862

59-
func (batch *SchedulerManager) GetSchedulerForCluster(app *rayv1.RayCluster) (schedulerinterface.BatchScheduler, error) {
60-
if schedulerName, ok := app.ObjectMeta.Labels[utils.RaySchedulerName]; ok {
61-
return batch.GetScheduler(schedulerName)
63+
// legacy option, if this is enabled, register volcano
64+
// this is for backwards compatibility
65+
if rayConfigs.EnableBatchScheduler {
66+
factory = &volcano.VolcanoBatchSchedulerFactory{}
6267
}
6368

64-
// no scheduler provided
65-
return &schedulerinterface.DefaultBatchScheduler{}, nil
69+
return factory
6670
}
6771

68-
func (batch *SchedulerManager) GetScheduler(schedulerName string) (schedulerinterface.BatchScheduler, error) {
69-
factory, registered := schedulerContainers[schedulerName]
70-
if !registered {
71-
return nil, fmt.Errorf("unregistered scheduler plugin %s", schedulerName)
72+
func (batch *SchedulerManager) GetSchedulerForCluster(app *rayv1.RayCluster) (schedulerinterface.BatchScheduler, error) {
73+
// for backwards compatibility
74+
if batch.rayConfigs.EnableBatchScheduler {
75+
if schedulerName, ok := app.ObjectMeta.Labels[utils.RaySchedulerName]; ok {
76+
if schedulerName == volcano.GetPluginName() {
77+
return batch.scheduler, nil
78+
}
79+
}
7280
}
7381

74-
batch.Lock()
75-
defer batch.Unlock()
82+
return batch.scheduler, nil
83+
}
7684

77-
plugin, existed := batch.plugins[schedulerName]
85+
func (batch *SchedulerManager) ConfigureReconciler(b *builder.Builder) *builder.Builder {
86+
batch.factory.ConfigureReconciler(b)
87+
return b
88+
}
7889

79-
if existed && plugin != nil {
80-
return plugin, nil
81-
}
82-
if existed && plugin == nil {
83-
return nil, fmt.Errorf(
84-
"failed to get scheduler plugin %s, previous initialization has failed", schedulerName)
85-
}
86-
plugin, err := factory.New(batch.config)
87-
if err != nil {
88-
batch.plugins[schedulerName] = nil
89-
return nil, err
90-
}
91-
batch.plugins[schedulerName] = plugin
92-
return plugin, nil
90+
func (batch *SchedulerManager) AddToScheme(scheme *runtime.Scheme) {
91+
batch.factory.AddToScheme(scheme)
9392
}

0 commit comments

Comments
 (0)