-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Cluster Local Model CR #3839
Cluster Local Model CR #3839
Conversation
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
ca5d830
to
072109d
Compare
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
69eafde
to
6ad65f0
Compare
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
|
||
type CachedModelStatus struct { | ||
OverallStatus OverallStatus `json:"overallStatus,omitempty"` | ||
NodeStatus map[string]NodeStatus `json:"nodeStatus,omitempty"` |
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.
example: nodeX: Downloading
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.
suggest NodeModelStatus
|
||
// How many nodes have the model available | ||
// +optional | ||
ModelCopies *ModelCopies `json:"copies,omitempty"` |
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.
for example: Suceessful: 10 total: 20
// How many nodes have the model available | ||
// +optional | ||
ModelCopies *ModelCopies `json:"copies,omitempty"` | ||
InferenceServices []NamespacedName `json:"infereneceServices,omitempty"` |
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.
ns1: isvc1, ns2: isvc2 uses this cached model
// Container spec for the storage initializer init container | ||
|
||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="StorageUri is immutable" | ||
StorageUri string `json:"storageUri" validate:"required"` |
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.
Full match vs match prefix like "storageUriPrefix"
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="StorageUri is immutable" | ||
StorageUri string `json:"storageUri" validate:"required"` | ||
ModelSize resource.Quantity `json:"modelSize" validate:"required"` | ||
NodeGroups []string `json:"nodeGroups" validate:"required"` |
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.
one model can be cached on multiple nodegroups (gpu-1, gpu-2, gpu-3)
NodeGroups []string `json:"nodeGroups" validate:"required"` | ||
// only local is supported for now | ||
StorageType StorageType `json:"storageType" validate:"required"` | ||
CleanupPolicy CleanupPolicy `json:"cleanupPolicy" validate:"required"` |
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.
Maybe we also need a "download policy" that expects the model is on the node without creating jobs. Just for special maintenances.
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.
Should we remove this field?
|
||
// StorageContainerSpec defines the container spec for the storage initializer init container, and the protocols it supports. | ||
// +k8s:openapi-gen=true | ||
type ClusterCachedModelSpec struct { |
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.
better name?
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 this is cluster scope only.
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 still prefer the name ClusterLocalModel
as the implementation of this CR is based on caching models locally.
// ModelCacheNodeGroupSpec defines a group of nodes for to download the model to. | ||
// +k8s:openapi-gen=true | ||
type ModelCacheNodeGroupSpec struct { | ||
StorageLimit resource.Quantity `json:"storageLimit"` |
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.
We can use the storageLimit on the PersistentVolume instead of this field. However that field is immutable which is less flexible.
// +k8s:openapi-gen=true | ||
type ModelCacheNodeGroupSpec struct { | ||
StorageLimit resource.Quantity `json:"storageLimit"` | ||
NodeSelector map[string]string `json:"nodeSelector"` |
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.
Discussed with @sukumargaonkar. Two approaches here.
Approach 1 - It's up to the user to make sure they use the correct label selector so two node groups never have overlaps.
Approach 2 - Require a specific label set on nodes. e.g. kserve-nodegroup-name: xyz
Otherwise if we allow overlapping in nodegroups. storage size calculation would be tricky.
NodeSelector map[string]string `json:"nodeSelector"` | ||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="persistentVolume is immutable" | ||
// PV spec template | ||
PersistentVolume corev1.PersistentVolume `json:"persistentVolume"` |
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.
We will always create PVs for now. In the future, users might be able to leverage PersistentVolumeClaim with a custom CSI driver which makes sure different PVs map to the same disk location.
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.
In the future we may have a namespace-scoped CR for NFS backed PV which does not share across namespaces. For non-local disks, sharing across namespaces is tricky as you would need different PVs and the volume plugin need to make sure they map to the same storage.
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.
How can we make it namespace scoped? Now the PV/PVC is defined on the node group CR but this probably never should be made namespace scoped.
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.
another namespace-scoped model cache CR that doesn't use nodegroup cr.
// only local is supported for now | ||
StorageType StorageType `json:"storageType" validate:"required"` | ||
CleanupPolicy CleanupPolicy `json:"cleanupPolicy" validate:"required"` | ||
} |
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.
Envvars for auth can be put in a configmap and added to the pod spec.
envFrom:
- configMapRef:
name: <config-file>
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
key: access-key
name: name
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
|
||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="StorageUri is immutable" | ||
// Original StorageUri | ||
StorageUri string `json:"storageUri" validate:"required"` |
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.
suggest renaming to SourceModelUri
// group of nodes to cache the model on. | ||
NodeGroups []string `json:"nodeGroups" validate:"required"` | ||
// only local is supported for now | ||
StorageType StorageType `json:"storageType" validate:"required"` |
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 this CR is very specifically designed for local model cache, for other type of storage like AWS EBS fields like NodeGroup, cleanup policy would not apply I think.
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 was thinking about temporarily disabling deletion jobs for some reason, say we want to delete and then recreate the CR but keeping the files on the disk.
) | ||
|
||
// CleanupPolicy enum | ||
// +kubebuilder:validation:Enum="";DeleteModel;Ignore |
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.
what are the other possible values? this sounds like a flag
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.
yeah, I can't think of other values for now.
@@ -32,6 +32,7 @@ type StorageContainerSpec struct { | |||
|
|||
// List of URI formats that this container supports | |||
SupportedUriFormats []SupportedUriFormat `json:"supportedUriFormats" validate:"required"` | |||
UseCase UseCase `json:"useCase" validate:"required"` |
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.
suggest workloadType
: InitContainer or Job
package v1alpha1 | ||
|
||
type CachedModelStatus struct { | ||
OverallStatus OverallStatus `json:"overallStatus,omitempty"` |
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.
can we just call status?
} | ||
|
||
// +k8s:openapi-gen=true | ||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object |
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.
deepcopy-gen is deprecated
https://kubebuilder.io/migration/legacy/migration_guide_v1tov2#migrate-the-apis
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.
replaced with +kubebuilder:object:root=true
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.
Somehow we still call deepcopy-gen
and removing it would cause an error while generating swagger files.
} | ||
|
||
// +k8s:openapi-gen=true | ||
// +kubebuilder:object:root=true |
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.
We only need this line
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.
same as above
type ClusterLocalModelSpec struct { | ||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="StorageUri is immutable" | ||
// Original StorageUri | ||
SourceModelUri string `json:"sourceModelUri" validate:"required"` |
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.
maybe we need to consider supporting multiple source modelUris
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.
We can make it a list of models here or support multiple local model crs for one inference service.
@@ -0,0 +1,24 @@ | |||
/* | |||
Copyright 2021 The KServe Authors. |
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.
Copyright 2021 The KServe Authors. | |
Copyright 2024 The KServe Authors. |
@@ -0,0 +1,60 @@ | |||
/* | |||
Copyright 2023 The KServe Authors. |
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.
Copyright 2023 The KServe Authors. | |
Copyright 2024 The KServe Authors. |
// +k8s:openapi-gen=true | ||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||
// +genclient |
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.
// +k8s:openapi-gen=true | |
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | |
// +genclient |
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
} | ||
|
||
// +k8s:openapi-gen=true | ||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object |
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.
Somehow we still call deepcopy-gen
and removing it would cause an error while generating swagger files.
type ClusterLocalModelSpec struct { | ||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="StorageUri is immutable" | ||
// Original StorageUri | ||
SourceModelUri string `json:"sourceModelUri" validate:"required"` |
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.
We can make it a list of models here or support multiple local model crs for one inference service.
InferenceServices []NamespacedName `json:"inferenceServices,omitempty"` | ||
} | ||
|
||
type NamespacedName struct { |
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.
Defining a new struct here because I got encountered struct field "Namespace" without JSON tag in type "NamespacedName"
if I use types.NamespacedName
.
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
} | ||
|
||
// +k8s:openapi-gen=true | ||
// +kubebuilder:object:root=true |
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.
same as above
type NodeStatus string | ||
|
||
// NodeStatus Enum values | ||
const ( |
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.
With the daemonset in the future, Node status should be ModelExists
or ModelDoesNotExist
reported by the daemonset. For other status, we can look at individual jobs?
It still makes sense if we do not go down the daemonset approach.
…ntainers.yaml Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
/rerun |
/rerun-all |
4a78624
to
531ae53
Compare
Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
531ae53
to
c6fef73
Compare
|
||
type ClusterLocalModelStatus struct { | ||
// Status of the model on a node, like NodeDownloaded or NodeNotReady | ||
NodeStatus map[string]NodeStatus `json:"nodeStatus,omitempty"` |
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 we can create a LocalModelNodeGroup controller and create the LocalModelNode resource per node, in this way we can spawn the model reconciler daemonset and update the status on each LocalModelNode.
/lgtm |
Discussed with @greenmoon55 to create a follow up PR to add a LocalModelNode custom resource to track the model status for each node in the group. |
* new model cache cr Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * update crd Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Fix genereted python tests Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Fix test failure Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Make nodegroup a list field in model cache cr Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * fix lint Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * minor updates to model cache cr Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Add usecase field to cluster storage container Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Fix test failures Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Change variable name Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Fix lint Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Fix default storage container cr Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * fix defualt.yaml Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Remove storagelimit field from node group Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Fix python code Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Change some fields Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Rename crd Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Fix lint error in python test files Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Rename CR Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Add status to local model node group Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Add missing node status Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Remove files related to ClusterLocalNodeGroup Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Add default value for workload type Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Fix StorageContainerSpec WorkloadType default value Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * nodegroups -> nodegroup Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Add comments Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Add back storageLimit Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> * Update charts/kserve-crd/templates/serving.kserve.io_clusterstoragecontainers.yaml Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com> --------- Signed-off-by: Jin Dong <greenmoon55@users.noreply.github.com>
What this PR does / why we need it:
This PR introduces a cluster model cache CR and a node group CR. With these two PRs, you can define a model that would be downloaded to multiple nodes which are defined by a node group CR (node selectors).
Example Model Cache CR
NodeGroup CR
Which issue(s) this PR fixes (optional, in
fixes #<issue number>(, fixes #<issue_number>, ...)
format, will close the issue(s) when PR gets merged):Fixes #
Type of changes
Please delete options that are not relevant.
Feature/Issue validation/testing:
Please describe the tests that you ran to verify your changes and relevant result summary. Provide instructions so it can be reproduced.
Please also list any relevant details for your test configuration.
Test A
Test B
Logs
Special notes for your reviewer:
Checklist:
Release note:
Re-running failed tests
/rerun-all
- rerun all failed workflows./rerun-workflow <workflow name>
- rerun a specific failed workflow. Only one workflow name can be specified. Multiple /rerun-workflow commands are allowed per comment.