Skip to content

Commit 58a3cb7

Browse files
committed
feat(binder): persist fractional GPU compute mode
Signed-off-by: davidLif <davidshani12@gmail.com>
1 parent 4478f8a commit 58a3cb7

6 files changed

Lines changed: 102 additions & 67 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kind: Added
2+
body: |-
3+
Persist fractional GPU compute mode

pkg/binder/binding/binder.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,22 @@ func (b *Binder) Rollback(ctx context.Context, pod *v1.Pod, node *v1.Node, bindR
119119
}
120120

121121
func (b *Binder) reserveGPUs(ctx context.Context, pod *v1.Pod, bindRequest *v1alpha2.BindRequest) ([]string, error) {
122-
if len(bindRequest.Spec.SelectedGPUGroups) == 0 {
122+
fractionalGpuGroups := bindRequest.Spec.SelectedFractionalGpuGroupsOrDefault()
123+
if len(fractionalGpuGroups) == 0 {
123124
// Old bindingRequest bad conversion. delete the binding request.
124-
return nil, fmt.Errorf("no SelectedGPUGroups for fractional pod: %w", InvalidCrdWarning)
125+
return nil, fmt.Errorf("no selected GPU groups for fractional pod: %w", InvalidCrdWarning)
125126
}
126127

127128
var gpuIndexes []string
128-
for _, gpuGroup := range bindRequest.Spec.SelectedGPUGroups {
129-
gpuIndex, err := b.resourceReservationService.ReserveGpuDevice(ctx, pod, bindRequest.Spec.SelectedNode, gpuGroup)
129+
for _, fractionalGpuGroup := range fractionalGpuGroups {
130+
gpuIndex, err := b.resourceReservationService.ReserveGpuDevice(
131+
ctx, pod, bindRequest.Spec.SelectedNode, fractionalGpuGroup)
130132
if err != nil {
131133
// Cleanup will be handled by the rollback function
132134

133-
return nil, fmt.Errorf("failed to reserve GPUs for pod <%s/%s> in gpu group <%s>: %w", pod.Namespace, pod.Name, gpuGroup, err)
135+
return nil, fmt.Errorf(
136+
"failed to reserve GPUs for pod <%s/%s> in gpu group <%s>: %w",
137+
pod.Namespace, pod.Name, fractionalGpuGroup.ID, err)
134138
}
135139
gpuIndexes = append(gpuIndexes, gpuIndex)
136140
}

pkg/binder/binding/resourcereservation/mock/resource_reservation_mock.go

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/binder/binding/resourcereservation/resource_reservation.go

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,18 @@ type Interface interface {
3434
Sync(ctx context.Context) error
3535
SyncForNode(ctx context.Context, nodeName string) error
3636
SyncForGpuGroup(ctx context.Context, gpuGroup string) error
37-
ReserveGpuDevice(ctx context.Context, pod *v1.Pod, nodeName string, gpuGroup string) (string, error)
37+
ReserveGpuDevice(
38+
ctx context.Context, pod *v1.Pod, nodeName string,
39+
fractionalGpuGroup schedulingv1alpha2.FractionalGpuGroup,
40+
) (string, error)
3841
RemovePodGpuGroupsConnection(ctx context.Context, pod *v1.Pod) error
3942
}
4043

4144
const (
42-
resourceReservation = "resource-reservation"
43-
gpuReservationPodPrefix = "gpu-reservation"
44-
gpuIndexAnnotationName = "run.ai/reserve_for_gpu_index"
45-
numberOfGPUsToReserve = 1
46-
unknownGpuIndicator = "-1"
45+
resourceReservation = "resource-reservation"
46+
gpuIndexAnnotationName = "run.ai/reserve_for_gpu_index"
47+
numberOfGPUsToReserve = 1
48+
unknownGpuIndicator = "-1"
4749
)
4850

4951
type service struct {
@@ -240,19 +242,23 @@ func (rsc *service) hasActiveBindRequestsForGpuGroup(ctx context.Context, gpuGro
240242
br.Status.Phase == schedulingv1alpha2.BindRequestPhaseFailed {
241243
continue
242244
}
243-
if slices.Contains(br.Spec.SelectedGPUGroups, gpuGroup) {
245+
if slices.Contains(br.Spec.SelectedFractionalGpuGroupIDs(), gpuGroup) {
244246
return true, nil
245247
}
246248
}
247249
return false, nil
248250
}
249-
func (rsc *service) ReserveGpuDevice(ctx context.Context, pod *v1.Pod, nodeName string, gpuGroup string) (string, error) {
251+
func (rsc *service) ReserveGpuDevice(
252+
ctx context.Context, pod *v1.Pod, nodeName string, fractionalGpuGroup schedulingv1alpha2.FractionalGpuGroup,
253+
) (string, error) {
250254
logger := log.FromContext(ctx)
255+
fractionalGpuGroup = fractionalGpuGroup.WithDefaults()
256+
gpuGroup := fractionalGpuGroup.ID
251257

252258
rsc.gpuGroupMutex.LockMutexForGroup(gpuGroup)
253259
defer rsc.gpuGroupMutex.ReleaseMutex(gpuGroup)
254260

255-
gpuIndex, err := rsc.acquireGPUIndexByGroup(ctx, pod, nodeName, gpuGroup)
261+
gpuIndex, err := rsc.acquireGPUIndexByGroup(ctx, nodeName, fractionalGpuGroup)
256262
if err != nil {
257263
return unknownGpuIndicator, err
258264
}
@@ -333,16 +339,16 @@ func escapeJSONPointer(s string) string {
333339
}
334340

335341
func (rsc *service) acquireGPUIndexByGroup(
336-
ctx context.Context, sourcePod *v1.Pod, nodeName, gpuGroup string,
342+
ctx context.Context, nodeName string, fractionalGpuGroup schedulingv1alpha2.FractionalGpuGroup,
337343
) (string, error) {
338-
gpuIndex, err := rsc.findGPUIndexByGroup(gpuGroup)
344+
gpuIndex, err := rsc.findGPUIndexByGroup(fractionalGpuGroup.ID)
339345
if err != nil {
340346
return "", err
341347
}
342348
if gpuIndex != "" {
343349
return gpuIndex, err
344350
}
345-
return rsc.createGPUReservationPodAndGetIndex(ctx, sourcePod, nodeName, gpuGroup)
351+
return rsc.createGPUReservationPodAndGetIndex(ctx, nodeName, fractionalGpuGroup)
346352
}
347353

348354
func (rsc *service) findGPUIndexByGroup(gpuGroup string) (
@@ -369,11 +375,11 @@ func (rsc *service) findGPUIndexByGroup(gpuGroup string) (
369375
}
370376

371377
func (rsc *service) createGPUReservationPodAndGetIndex(
372-
ctx context.Context, sourcePod *v1.Pod, nodeName, gpuGroup string,
378+
ctx context.Context, nodeName string, fractionalGpuGroup schedulingv1alpha2.FractionalGpuGroup,
373379
) (
374380
gpuIndex string, err error) {
375381
logger := log.FromContext(ctx)
376-
pod, err := rsc.createGPUReservationPod(ctx, sourcePod, nodeName, gpuGroup)
382+
pod, err := rsc.createGPUReservationPod(ctx, nodeName, fractionalGpuGroup)
377383
if err != nil {
378384
return unknownGpuIndicator, err
379385
}
@@ -426,14 +432,14 @@ func (rsc *service) deleteReservationPod(ctx context.Context, pod *v1.Pod) error
426432
}
427433

428434
func (rsc *service) createGPUReservationPod(
429-
ctx context.Context, sourcePod *v1.Pod, nodeName, gpuGroup string,
435+
ctx context.Context, nodeName string, fractionalGpuGroup schedulingv1alpha2.FractionalGpuGroup,
430436
) (*v1.Pod, error) {
431437
logger := log.FromContext(ctx)
432438
if rsc.isScalingUp(ctx) {
433439
return nil, fmt.Errorf("cluster is scaling up, could not create reservation pod")
434440
}
435441

436-
podName := reservationPodName(nodeName, gpuGroup)
442+
podName := reservationPodName(nodeName, fractionalGpuGroup.ID)
437443

438444
// Build resource requirements starting with GPU resources
439445
resources := v1.ResourceRequirements{
@@ -456,15 +462,15 @@ func (rsc *service) createGPUReservationPod(
456462
}
457463
}
458464

459-
pod, err := rsc.createResourceReservationPod(sourcePod, nodeName, gpuGroup, podName, resources)
465+
pod, err := rsc.createResourceReservationPod(nodeName, fractionalGpuGroup, podName, resources)
460466
if err != nil {
461467
// The reservation pod name is deterministic per (node, gpu-group). AlreadyExists
462468
// means another actor (a concurrent bind, a retry, or another binder replica)
463469
// already created the reservation pod for this gpu-group, so reuse it rather than
464470
// creating a duplicate on a different physical GPU.
465471
if apierrors.IsAlreadyExists(err) {
466472
logger.Info("GPU reservation pod already exists for gpu group, reusing",
467-
"nodeName", nodeName, "namespace", rsc.namespace, "name", podName, "gpuGroup", gpuGroup)
473+
"nodeName", nodeName, "namespace", rsc.namespace, "name", podName, "gpuGroup", fractionalGpuGroup.ID)
468474
return pod, nil
469475
}
470476
logger.Error(err, "Failed to create GPU reservation pod on node",
@@ -531,29 +537,25 @@ func (rsc *service) waitForGPUReservationPodAllocation(
531537
}
532538

533539
func (rsc *service) createResourceReservationPod(
534-
sourcePod *v1.Pod, nodeName, gpuGroup, podName string, resources v1.ResourceRequirements,
540+
nodeName string, fractionalGpuGroup schedulingv1alpha2.FractionalGpuGroup,
541+
podName string, resources v1.ResourceRequirements,
535542
) (*v1.Pod, error) {
536-
var tolerations []v1.Toleration
537-
if sourcePod != nil {
538-
sourcePodSpec := sourcePod.Spec.DeepCopy()
539-
tolerations = sourcePodSpec.Tolerations
540-
}
541-
543+
fractionalGpuGroup = fractionalGpuGroup.WithDefaults()
542544
podSpec := &v1.Pod{
543545
ObjectMeta: metav1.ObjectMeta{
544546
Name: podName,
545547
Namespace: rsc.namespace,
546548
Labels: map[string]string{
547549
constants.AppLabelName: rsc.appLabelValue,
548-
constants.GPUGroup: gpuGroup,
550+
constants.GPUGroup: fractionalGpuGroup.ID,
549551
},
550552
Annotations: map[string]string{
551553
karpenterv1.DoNotDisruptAnnotationKey: "true",
554+
constants.GpuComputeSharingMode: string(fractionalGpuGroup.ComputeSharingMode),
552555
},
553556
},
554557
Spec: v1.PodSpec{
555-
NodeName: nodeName,
556-
Tolerations: tolerations,
558+
NodeName: nodeName,
557559
RuntimeClassName: func() *string {
558560
if len(rsc.runtimeClassName) == 0 {
559561
return nil
@@ -633,7 +635,7 @@ func (rsc *service) isScalingUp(ctx context.Context) bool {
633635
}
634636

635637
func IsGPUReservationPod(pod *v1.Pod) bool {
636-
return strings.HasPrefix(pod.Name, gpuReservationPodPrefix)
638+
return strings.HasPrefix(pod.Name, constants.GPUReservationPodPrefix)
637639
}
638640

639641
// reservationPodName derives a deterministic reservation pod name from the node and
@@ -642,5 +644,5 @@ func IsGPUReservationPod(pod *v1.Pod) bool {
642644
// pods on different physical GPUs.
643645
func reservationPodName(nodeName, gpuGroup string) string {
644646
hash := sha256.Sum256([]byte(nodeName + "/" + gpuGroup))
645-
return fmt.Sprintf("%s-%s", gpuReservationPodPrefix, hex.EncodeToString(hash[:8]))
647+
return fmt.Sprintf("%s-%s", constants.GPUReservationPodPrefix, hex.EncodeToString(hash[:8]))
646648
}

0 commit comments

Comments
 (0)