-
Notifications
You must be signed in to change notification settings - Fork 664
RayJob Volcano Integration #3972
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
6e4184a
973fe82
2ee7e8a
92e7d95
dd38daa
a33a3b7
42479c2
597e57d
a64c011
b8cb224
84d39f9
cf6b48b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| apiVersion: scheduling.volcano.sh/v1beta1 | ||
| kind: Queue | ||
| metadata: | ||
| name: kuberay-test-queue | ||
| spec: | ||
| weight: 1 | ||
| capability: | ||
| cpu: 4 | ||
| memory: 6Gi | ||
| --- | ||
| apiVersion: ray.io/v1 | ||
| kind: RayJob | ||
| metadata: | ||
| name: rayjob-sample-0 | ||
| labels: | ||
| ray.io/scheduler-name: volcano | ||
| volcano.sh/queue-name: kuberay-test-queue | ||
| spec: | ||
| entrypoint: python /home/ray/samples/sample_code.py | ||
| runtimeEnvYAML: | | ||
| pip: | ||
| - requests==2.26.0 | ||
| - pendulum==2.1.2 | ||
| env_vars: | ||
| counter_name: "test_counter" | ||
| rayClusterSpec: | ||
| rayVersion: '2.46.0' | ||
| headGroupSpec: | ||
| rayStartParams: {} | ||
| template: | ||
| spec: | ||
| containers: | ||
| - name: ray-head | ||
| image: rayproject/ray:2.46.0 | ||
| ports: | ||
| - containerPort: 6379 | ||
| name: gcs-server | ||
| - containerPort: 8265 | ||
| name: dashboard | ||
| - containerPort: 10001 | ||
| name: client | ||
| resources: | ||
| limits: | ||
| cpu: "1" | ||
| memory: "2Gi" | ||
| requests: | ||
| cpu: "1" | ||
| memory: "2Gi" | ||
| volumeMounts: | ||
| - mountPath: /home/ray/samples | ||
| name: code-sample | ||
| volumes: | ||
| - name: code-sample | ||
| configMap: | ||
| name: ray-job-code-sample | ||
| items: | ||
| - key: sample_code.py | ||
| path: sample_code.py | ||
| workerGroupSpecs: | ||
| - replicas: 2 | ||
| minReplicas: 2 | ||
| maxReplicas: 2 | ||
| groupName: small-group | ||
| rayStartParams: {} | ||
| template: | ||
| spec: | ||
| containers: | ||
| - name: ray-worker | ||
| image: rayproject/ray:2.46.0 | ||
| resources: | ||
| limits: | ||
| cpu: "1" | ||
| memory: "1Gi" | ||
| requests: | ||
| cpu: "1" | ||
| memory: "1Gi" | ||
| --- | ||
| apiVersion: v1 | ||
| kind: ConfigMap | ||
| metadata: | ||
| name: ray-job-code-sample | ||
| data: | ||
| sample_code.py: | | ||
| import ray | ||
| import os | ||
| import requests | ||
| ray.init() | ||
| @ray.remote | ||
| class Counter: | ||
| def __init__(self): | ||
| # Used to verify runtimeEnv | ||
| self.name = os.getenv("counter_name") | ||
| assert self.name == "test_counter" | ||
| self.counter = 0 | ||
| def inc(self): | ||
| self.counter += 1 | ||
| def get_counter(self): | ||
| return "{} got {}".format(self.name, self.counter) | ||
| counter = Counter.remote() | ||
| for _ in range(5): | ||
| ray.get(counter.inc.remote()) | ||
| print(ray.get(counter.get_counter.remote())) | ||
| # Verify that the correct runtime env was used for the job. | ||
| assert requests.__version__ == "2.26.0" | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,16 +15,18 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ctrl "sigs.k8s.io/controller-runtime" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "sigs.k8s.io/controller-runtime/pkg/builder" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "sigs.k8s.io/controller-runtime/pkg/client" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| volcanov1alpha1 "volcano.sh/apis/pkg/apis/batch/v1alpha1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| volcanov1beta1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| volcanobatchv1alpha1 "volcano.sh/apis/pkg/apis/batch/v1alpha1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| volcanoschedulingv1beta1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| schedulerinterface "github.com/ray-project/kuberay/ray-operator/controllers/ray/batchscheduler/interface" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ray-project/kuberay/ray-operator/controllers/ray/common" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ray-project/kuberay/ray-operator/controllers/ray/utils" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PodGroupName = "podgroups.scheduling.volcano.sh" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This variable is unused and can be removed i think.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can just remove it |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pluginName = "volcano" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| QueueNameLabelKey = "volcano.sh/queue-name" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -34,106 +36,189 @@ type VolcanoBatchScheduler struct { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type VolcanoBatchSchedulerFactory struct{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func GetPluginName() string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "volcano" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func GetPluginName() string { return pluginName } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (v *VolcanoBatchScheduler) Name() string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return GetPluginName() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (v *VolcanoBatchScheduler) DoBatchSchedulingOnSubmission(ctx context.Context, object metav1.Object) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app, ok := object.(*rayv1.RayCluster) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !ok { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("currently only RayCluster is supported, got %T", object) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var minMember int32 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var totalResource corev1.ResourceList | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !utils.IsAutoscalingEnabled(&app.Spec) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minMember = utils.CalculateDesiredReplicas(ctx, app) + 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| totalResource = utils.CalculateDesiredResources(app) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minMember = utils.CalculateMinReplicas(app) + 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| totalResource = utils.CalculateMinResources(app) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch obj := object.(type) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case *rayv1.RayCluster: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return v.handleRayCluster(ctx, obj) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case *rayv1.RayJob: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return v.handleRayJob(ctx, obj) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("unsupported object type %T, only RayCluster and RayJob are supported", object) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // handleRayCluster calculates the PodGroup MinMember and MinResources for a RayCluster | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (v *VolcanoBatchScheduler) handleRayCluster(ctx context.Context, raycluster *rayv1.RayCluster) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check if this RayCluster is created by a RayJob, if so, skip PodGroup creation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if crdType, ok := raycluster.Labels[utils.RayOriginatedFromCRDLabelKey]; ok && crdType == utils.RayOriginatedFromCRDLabelValue(utils.RayJobCRD) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return v.syncPodGroup(ctx, app, minMember, totalResource) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minMember, totalResource := v.calculatePodGroupParams(ctx, &raycluster.Spec) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return v.syncPodGroup(ctx, raycluster, minMember, totalResource) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func getAppPodGroupName(app *rayv1.RayCluster) string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Sprintf("ray-%s-pg", app.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // handleRayJob calculates the PodGroup MinMember and MinResources for a RayJob | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (v *VolcanoBatchScheduler) handleRayJob(ctx context.Context, rayJob *rayv1.RayJob) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if rayJob.Spec.RayClusterSpec == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("gang scheduling does not support RayJob %s/%s referencing an existing RayCluster", rayJob.Namespace, rayJob.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var totalResourceList []corev1.ResourceList | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minMember, totalResource := v.calculatePodGroupParams(ctx, rayJob.Spec.RayClusterSpec) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| totalResourceList = append(totalResourceList, totalResource) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // MinMember intentionally excludes the submitter pod to avoid a startup deadlock | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (submitter waits for cluster; gang would wait for submitter). We still add the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // submitter's resource requests into MinResources so capacity is reserved. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+78
to
+80
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // MinMember intentionally excludes the submitter pod to avoid a startup deadlock | |
| // (submitter waits for cluster; gang would wait for submitter). We still add the | |
| // submitter's resource requests into MinResources so capacity is reserved. | |
| // MinMember intentionally excludes the submitter pod to avoid a startup deadlock. | |
| // If the submitter pod were included in MinMember, the gang scheduler would wait for | |
| // all pods—including the submitter—to be schedulable before scheduling any of them. | |
| // This creates a circular dependency: the submitter pod waits for the cluster to be ready, | |
| // but the cluster cannot be scheduled until the submitter is also schedulable. To avoid this, | |
| // we exclude the submitter from MinMember, but still add its resource requests into MinResources | |
| // so that capacity is reserved for it. |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the result for this code is correct, but the behavior is not.
for k8s mode, we should get submitter's information from submitterTemplate
for sidecar mode, we should get submitter's information from GetDefaultSubmitterContainer, since we use this function currently.
update:
I've discussed offline with @win5923
I am writing a commit to fix this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Younikorn also uses GetSubmitterTemplate for sidecar mode. Would you like to update the Younikorn part as well (newTaskGroupsFromRayJobSpec)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes I can do it now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @rueian
I have tested the Yunikorn integration in both Kubernetes job mode and sidecar mode and can confirm that no code changes are required. The existing logic correctly handles both scenarios.
Kubernetes Job Mode
The function newTaskGroupsFromRayJobSpec is ultimately called by AddMetadataToChildResource. Within the RayJob controller, AddMetadataToChildResource is only invoked when the RayJob is configured for Kubernetes job mode, as seen in these two locations:
1st place:
kuberay/ray-operator/controllers/ray/rayjob_controller.go
Lines 582 to 606 in c6bafa3
| func (r *RayJobReconciler) createK8sJobIfNeed(ctx context.Context, rayJobInstance *rayv1.RayJob, rayClusterInstance *rayv1.RayCluster) error { | |
| logger := ctrl.LoggerFrom(ctx) | |
| job := &batchv1.Job{} | |
| namespacedName := common.RayJobK8sJobNamespacedName(rayJobInstance) | |
| if err := r.Client.Get(ctx, namespacedName, job); err != nil { | |
| if errors.IsNotFound(err) { | |
| submitterTemplate, err := getSubmitterTemplate(rayJobInstance, rayClusterInstance) | |
| if err != nil { | |
| return err | |
| } | |
| if r.options.BatchSchedulerManager != nil { | |
| if scheduler, err := r.options.BatchSchedulerManager.GetScheduler(); err == nil { | |
| scheduler.AddMetadataToChildResource(ctx, rayJobInstance, &submitterTemplate, utils.RayNodeSubmitterGroupLabelValue) | |
| } else { | |
| return err | |
| } | |
| } | |
| return r.createNewK8sJob(ctx, rayJobInstance, submitterTemplate) | |
| } | |
| return err | |
| } | |
| logger.Info("The submitter Kubernetes Job for RayJob already exists", "Kubernetes Job", job.Name) | |
| return nil | |
| } |
2nd place:
kuberay/ray-operator/controllers/ray/rayjob_controller.go
Lines 949 to 956 in c6bafa3
| if r.options.BatchSchedulerManager != nil && rayJobInstance.Spec.SubmissionMode == rayv1.K8sJobMode { | |
| if scheduler, err := r.options.BatchSchedulerManager.GetScheduler(); err == nil { | |
| // Group name is only used for individual pods to specify their task group ("headgroup", "worker-group-1", etc.). | |
| // RayCluster contains multiple groups, so we pass an empty string. | |
| scheduler.AddMetadataToChildResource(ctx, rayJobInstance, rayClusterInstance, "") | |
| } else { | |
| return nil, err | |
| } |
That's why it behaves correctly.
Because of this, the Yunikorn-specific logic is correctly applied only when the RayJob creates a Kubernetes Job, and it behaves as expected.
Sidecar Mode
In sidecar mode, the submitter container is added to the Ray head pod, which is part of the RayCluster specification. When the RayCluster controller reconciles the RayCluster custom resource, it calculates the task groups for the head and worker pods. At that point, the head pod correctly contains both the Ray head container and the submitter sidecar container, ensuring their resources are accounted for in the task group calculation, as handled by the logic here:
kuberay/ray-operator/controllers/ray/rayjob_controller.go
Lines 978 to 1016 in c6bafa3
| func (r *RayJobReconciler) constructRayClusterForRayJob(rayJobInstance *rayv1.RayJob, rayClusterName string) (*rayv1.RayCluster, error) { | |
| labels := make(map[string]string, len(rayJobInstance.Labels)) | |
| for key, value := range rayJobInstance.Labels { | |
| labels[key] = value | |
| } | |
| labels[utils.RayOriginatedFromCRNameLabelKey] = rayJobInstance.Name | |
| labels[utils.RayOriginatedFromCRDLabelKey] = utils.RayOriginatedFromCRDLabelValue(utils.RayJobCRD) | |
| rayCluster := &rayv1.RayCluster{ | |
| ObjectMeta: metav1.ObjectMeta{ | |
| Labels: labels, | |
| Annotations: rayJobInstance.Annotations, | |
| Name: rayClusterName, | |
| Namespace: rayJobInstance.Namespace, | |
| }, | |
| Spec: *rayJobInstance.Spec.RayClusterSpec.DeepCopy(), | |
| } | |
| // Set the ownership in order to do the garbage collection by k8s. | |
| if err := ctrl.SetControllerReference(rayJobInstance, rayCluster, r.Scheme); err != nil { | |
| return nil, err | |
| } | |
| // Inject a submitter container into the head Pod in SidecarMode. | |
| if rayJobInstance.Spec.SubmissionMode == rayv1.SidecarMode { | |
| sidecar, err := getSubmitterContainer(rayJobInstance, rayCluster) | |
| if err != nil { | |
| return nil, err | |
| } | |
| rayCluster.Spec.HeadGroupSpec.Template.Spec.Containers = append( | |
| rayCluster.Spec.HeadGroupSpec.Template.Spec.Containers, sidecar) | |
| // In K8sJobMode, the submitter Job relies on the K8s Job backoffLimit API to restart if it fails. | |
| // This mainly handles WebSocket connection failures caused by transient network issues. | |
| // In SidecarMode, however, the submitter container shares the same network namespace as the Ray dashboard, | |
| // so restarts are no longer needed. | |
| rayCluster.Spec.HeadGroupSpec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever | |
| } | |
| return rayCluster, nil | |
| } |
troychiu marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential nil pointer dereference if owner.GetLabels() returns nil. The code should check if labels exist before accessing the map.
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential nil pointer dereference if owner.GetLabels() returns nil. The code should check if labels exist before accessing the map.
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential nil pointer dereference if owner.GetLabels() returns nil. The code should check if labels exist before accessing the map.



There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @win5923
can we add
ray.io/gang-scheduling-enabled: "true"in the example and test them?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, adding
ray.io/gang-scheduling-enabled: "true"does not have any effect. This only works withYuniKornor theScheduler plugin.kuberay/ray-operator/controllers/ray/batchscheduler/yunikorn/yunikorn_scheduler.go
Lines 120 to 123 in c6bafa3
kuberay/ray-operator/controllers/ray/batchscheduler/scheduler-plugins/scheduler_plugins.go
Lines 109 to 112 in c6bafa3
And I think this is a breaking change if we add this check. We should also update the doc to mention that starting from version 1.5.0, users need to add
ray.io/gang-scheduling-enabled: "true"to enable gang scheduling for Volcano. https://docs.ray.io/en/latest/cluster/kubernetes/k8s-ecosystem/volcano.htmlThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found that volcano's default scheduler configmap is gang scheduling enabled!
so in the future, if the user want to disable it, we might need to tell them to edit the configmap or figure out some way to control it by adding more information in our CR.
thank you!!