Skip to content

Commit a776487

Browse files
Merge pull request llm-d-incubation#190 from waltforme/use-cr
Begin to use custom resources
2 parents a894a95 + a8ac230 commit a776487

5 files changed

Lines changed: 104 additions & 8 deletions

File tree

charts/dpctlr/templates/role.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ metadata:
66
app.kubernetes.io/component: controller
77
name: dual-pods-controller
88
rules:
9+
- apiGroups: [ "fma.llm-d.ai" ]
10+
resources: [ inferenceserverconfigs, launcherconfigs ]
11+
verbs: [ get, list, watch ]
912
- apiGroups: [ "" ]
1013
resources: [ configmaps ]
1114
verbs: [ get, list, watch ]

cmd/dual-pods-controller/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232
"k8s.io/klog/v2"
3333

3434
dpctlr "github.com/llm-d-incubation/llm-d-fast-model-actuation/pkg/controller/dual-pods"
35+
fmaclient "github.com/llm-d-incubation/llm-d-fast-model-actuation/pkg/generated/clientset/versioned"
36+
fmainformers "github.com/llm-d-incubation/llm-d-fast-model-actuation/pkg/generated/informers/externalversions"
3537
)
3638

3739
func main() {
@@ -83,17 +85,20 @@ func main() {
8385

8486
kubeClient := kubernetes.NewForConfigOrDie(restConfig)
8587
kubePreInformers := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(overrides.Context.Namespace))
86-
88+
fmaClient := fmaclient.NewForConfigOrDie(restConfig)
89+
fmaPreInformers := fmainformers.NewSharedInformerFactoryWithOptions(fmaClient, 0, fmainformers.WithNamespace(overrides.Context.Namespace))
8790
ctlr, err := config.NewController(
8891
logger,
8992
kubeClient.CoreV1(),
9093
overrides.Context.Namespace,
9194
kubePreInformers.Core().V1(),
95+
fmaPreInformers,
9296
)
9397
if err != nil {
9498
klog.Fatal(err)
9599
}
96100
kubePreInformers.Start(ctx.Done())
101+
fmaPreInformers.Start(ctx.Done())
97102
err = ctlr.Start(ctx)
98103
if err != nil {
99104
klog.Fatal(err)

pkg/api/interface.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,17 @@ package api
4141
// to define the server-providing Pod.
4242
// 1. Remove all annotations;
4343
// 2. Apply the patch
44-
44+
// This annotation is the single source of truth to signal that the server-requesting Pod
45+
// is using the server patch to define its server-providing Pod.
46+
// This annotation is mutually exclusive with the 'InferenceServerConfigAnnotationName' annotation.
4547
const ServerPatchAnnotationName = "dual-pods.llm-d.ai/server-patch"
4648

49+
// InferenceServerConfigAnnotationName is the name of an annotation on the
50+
// server-requesting Pod. The value of the annotation is the name of the
51+
// InferenceServerConfig object that the server-providing Pod uses.
52+
// This annotation is mutually exclusive with the 'ServerPatchAnnotationName' annotation.
53+
const InferenceServerConfigAnnotationName = "dual-pods.llm-d.ai/inference-server-config"
54+
4755
// StatusAnnotationName is the name of an annotation that the dual-pods controller
4856
// maintains reporting the ServerRequestingPodStatus. The value of this annotation is the
4957
// JSON rendering of the status.

pkg/controller/dual-pods/controller.go

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ import (
4040
"k8s.io/client-go/tools/cache"
4141
"k8s.io/klog/v2"
4242

43+
fmav1alpha1 "github.com/llm-d-incubation/llm-d-fast-model-actuation/api/fma/v1alpha1"
4344
"github.com/llm-d-incubation/llm-d-fast-model-actuation/pkg/api"
4445
genctlr "github.com/llm-d-incubation/llm-d-fast-model-actuation/pkg/controller/generic"
46+
fmainformers "github.com/llm-d-incubation/llm-d-fast-model-actuation/pkg/generated/informers/externalversions"
47+
fmalisters "github.com/llm-d-incubation/llm-d-fast-model-actuation/pkg/generated/listers/fma/v1alpha1"
4548
)
4649

4750
// This package implements the dual-pods controller.
@@ -147,6 +150,7 @@ func (config ControllerConfig) NewController(
147150
coreClient coreclient.CoreV1Interface,
148151
namespace string,
149152
corev1PreInformers corev1preinformers.Interface,
153+
fmaInformerFactory fmainformers.SharedInformerFactory,
150154
) (*controller, error) {
151155
ctl := &controller{
152156
enqueueLogger: logger.WithName(ControllerName),
@@ -158,16 +162,21 @@ func (config ControllerConfig) NewController(
158162
cmLister: corev1PreInformers.ConfigMaps().Lister(),
159163
nodeInformer: corev1PreInformers.Nodes().Informer(),
160164
nodeLister: corev1PreInformers.Nodes().Lister(),
165+
iscInformer: fmaInformerFactory.Fma().V1alpha1().InferenceServerConfigs().Informer(),
166+
iscLister: fmaInformerFactory.Fma().V1alpha1().InferenceServerConfigs().Lister(),
167+
lcInformer: fmaInformerFactory.Fma().V1alpha1().LauncherConfigs().Informer(),
168+
lcLister: fmaInformerFactory.Fma().V1alpha1().LauncherConfigs().Lister(),
161169
sleeperLimit: config.SleeperLimit,
162170
debugAccelMemory: config.AcceleratorSleepingMemoryLimitMiB < math.MaxInt32,
163171
accelMemoryLimitMiB: config.AcceleratorSleepingMemoryLimitMiB,
164172
nodeNameToData: map[string]*nodeData{},
165173
}
166174
ctl.gpuMap.Store(&map[string]GpuLocation{})
167175
err := ctl.podInformer.AddIndexers(cache.Indexers{
168-
requesterIndexName: requesterIndexFunc,
169-
nominalHashIndexName: nominalHashIndexFunc,
170-
GPUIndexName: GPUIndexFunc})
176+
inferenceServerConfigIndexName: inferenceServerConfigIndexFunc,
177+
requesterIndexName: requesterIndexFunc,
178+
nominalHashIndexName: nominalHashIndexFunc,
179+
GPUIndexName: GPUIndexFunc})
171180
if err != nil { //impossible
172181
return nil, err
173182
}
@@ -194,6 +203,14 @@ func (config ControllerConfig) NewController(
194203
if err != nil {
195204
panic(err)
196205
}
206+
_, err = ctl.iscInformer.AddEventHandler(ctl)
207+
if err != nil {
208+
panic(err)
209+
}
210+
_, err = ctl.lcInformer.AddEventHandler(ctl)
211+
if err != nil {
212+
panic(err)
213+
}
197214
return ctl, nil
198215
}
199216

@@ -207,6 +224,10 @@ type controller struct {
207224
cmLister corev1listers.ConfigMapLister
208225
nodeInformer cache.SharedIndexInformer
209226
nodeLister corev1listers.NodeLister
227+
iscInformer cache.SharedIndexInformer
228+
iscLister fmalisters.InferenceServerConfigLister
229+
lcInformer cache.SharedIndexInformer
230+
lcLister fmalisters.LauncherConfigLister
210231
genctlr.KnowsProcessedSync[queueItem]
211232

212233
sleeperLimit int
@@ -327,7 +348,7 @@ const (
327348
// The controller cares about server-requesting Pods, bound direct server-providing Pods, and launcher-based server-providing Pods.
328349
// The controller doesn't care about unbound direct providers and other Pods.
329350
func careAbout(pod *corev1.Pod) (item infSvrItem, it infSvrItemType) {
330-
if len(pod.Annotations[api.ServerPatchAnnotationName]) > 0 {
351+
if len(pod.Annotations[api.ServerPatchAnnotationName]) > 0 || len(pod.Annotations[api.InferenceServerConfigAnnotationName]) > 0 {
331352
return infSvrItem{pod.UID, pod.Name}, infSvrItemRequester
332353
}
333354
requesterStr := pod.Annotations[requesterAnnotationKey]
@@ -338,6 +359,17 @@ func careAbout(pod *corev1.Pod) (item infSvrItem, it infSvrItemType) {
338359
return infSvrItem{apitypes.UID(requesterParts[0]), requesterParts[1]}, infSvrItemBoundDirectProvider
339360
}
340361

362+
const inferenceServerConfigIndexName = "inferenceserverconfig"
363+
364+
func inferenceServerConfigIndexFunc(obj any) ([]string, error) {
365+
pod := obj.(*corev1.Pod)
366+
inferenceServerConfigName := pod.Annotations[api.InferenceServerConfigAnnotationName]
367+
if len(inferenceServerConfigName) == 0 {
368+
return []string{}, nil
369+
}
370+
return []string{inferenceServerConfigName}, nil
371+
}
372+
341373
const requesterIndexName = "requester"
342374

343375
func requesterIndexFunc(obj any) ([]string, error) {
@@ -373,6 +405,8 @@ func (ctl *controller) OnAdd(obj any, isInInitialList bool) {
373405
nd.add(item)
374406
ctl.Queue.Add(nodeItem{nodeName})
375407
}
408+
case *fmav1alpha1.InferenceServerConfig:
409+
ctl.enqueueRequestersByInferenceServerConfig(typed, isInInitialList)
376410
case *corev1.ConfigMap:
377411
if typed.Name != GPUMapName {
378412
ctl.enqueueLogger.V(5).Info("Ignoring ConfigMap that is not the GPU map", "ref", cache.MetaObjectToName(typed))
@@ -412,6 +446,8 @@ func (ctl *controller) OnUpdate(prev, obj any) {
412446
nd.add(item)
413447
ctl.Queue.Add(nodeItem{nodeName})
414448
}
449+
case *fmav1alpha1.InferenceServerConfig:
450+
ctl.enqueueRequestersByInferenceServerConfig(typed, false)
415451
case *corev1.ConfigMap:
416452
if typed.Name != GPUMapName {
417453
ctl.enqueueLogger.V(5).Info("Ignoring ConfigMap that is not the GPU map", "ref", cache.MetaObjectToName(typed))
@@ -454,6 +490,8 @@ func (ctl *controller) OnDelete(obj any) {
454490
nd.add(item)
455491
ctl.Queue.Add(nodeItem{nodeName})
456492
}
493+
case *fmav1alpha1.InferenceServerConfig:
494+
ctl.enqueueRequestersByInferenceServerConfig(typed, false)
457495
case *corev1.ConfigMap:
458496
if typed.Name != GPUMapName {
459497
ctl.enqueueLogger.V(5).Info("Ignoring ConfigMap that is not the GPU map", "ref", cache.MetaObjectToName(typed))
@@ -478,7 +516,7 @@ func getProviderNodeName(pod *corev1.Pod) (string, error) {
478516
}
479517

480518
func (ctl *controller) Start(ctx context.Context) error {
481-
if !cache.WaitForNamedCacheSync(ControllerName, ctx.Done(), ctl.cmInformer.HasSynced, ctl.podInformer.HasSynced, ctl.nodeInformer.HasSynced) {
519+
if !cache.WaitForNamedCacheSync(ControllerName, ctx.Done(), ctl.cmInformer.HasSynced, ctl.podInformer.HasSynced, ctl.nodeInformer.HasSynced, ctl.iscInformer.HasSynced, ctl.lcInformer.HasSynced) {
482520
return fmt.Errorf("caches not synced before end of Start context")
483521
}
484522
err := ctl.StartWorkers(ctx)
@@ -550,6 +588,37 @@ func (ctl *controller) enqueueRequesters(ctx context.Context) {
550588
}
551589
}
552590

591+
func (ctl *controller) enqueueRequestersByInferenceServerConfig(isc *fmav1alpha1.InferenceServerConfig, isInInitialList bool) {
592+
inferenceServerConfigName := isc.Name
593+
requesters, err := ctl.podInformer.GetIndexer().ByIndex(inferenceServerConfigIndexName, inferenceServerConfigName)
594+
if err != nil {
595+
ctl.enqueueLogger.Error(err, "Failed to get server requesting pods that use InferenceServerConfig", "ref", cache.MetaObjectToName(isc))
596+
return
597+
}
598+
nodeNames := sets.New[string]()
599+
for _, podObj := range requesters {
600+
pod := podObj.(*corev1.Pod)
601+
item, it := careAbout(pod)
602+
if it != infSvrItemRequester {
603+
// should not happen because of the nature of inferenceServerConfigIndexFunc
604+
ctl.enqueueLogger.V(5).Info("Ignoring Pod that is not a server-requesting Pod", "pod", pod.Name)
605+
continue
606+
}
607+
nodeName := pod.Spec.NodeName
608+
if nodeName == "" {
609+
ctl.enqueueLogger.V(5).Info("Ignoring non-scheduled server-requesting Pod that uses InferenceServerConfig", "pod", pod.Name, "inferenceServerConfigName", inferenceServerConfigName)
610+
continue
611+
}
612+
nd := ctl.getNodeData(nodeName)
613+
ctl.enqueueLogger.V(5).Info("Enqueuing inference server reference due to notification of InferenceServerConfig", "nodeName", nodeName, "item", item, "infSvrItemType", it, "inferenceServerConfigName", inferenceServerConfigName, "isInInitialList", isInInitialList, "resourceVersion", isc.ResourceVersion)
614+
nd.add(item)
615+
nodeNames.Insert(nodeName)
616+
}
617+
for nodeName := range nodeNames {
618+
ctl.Queue.Add(nodeItem{nodeName})
619+
}
620+
}
621+
553622
func (ctl *controller) getNodeData(nodeName string) *nodeData {
554623
ctl.mutex.Lock()
555624
defer ctl.mutex.Unlock()

test/e2e/run.sh

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# Usage: $0
44
# Current working directory must be the root of the Git repository.
5-
# The only reason for this is the `make` commands.
5+
# The reasons for this are the `make` commands and the location of CRDs.
66

77
set -euo pipefail
88

@@ -67,12 +67,23 @@ kubectl get pods -A -o wide
6767

6868
kubectl create clusterrole node-viewer --verb=get,list,watch --resource=nodes
6969

70+
kubectl create -f ./config/crd/
71+
7072
kubectl apply -f - <<EOF
7173
apiVersion: rbac.authorization.k8s.io/v1
7274
kind: Role
7375
metadata:
7476
name: testreq
7577
rules:
78+
- apiGroups:
79+
- "fma.llm-d.ai"
80+
resources:
81+
- inferenceserverconfigs
82+
- launcherconfigs
83+
verbs:
84+
- get
85+
- list
86+
- watch
7687
- apiGroups:
7788
- ""
7889
resourceNames:

0 commit comments

Comments
 (0)