2323
2424Today, the current VPA recommends CPU/Mem requests based on one recommender,
2525which recommends the future requests based on the historical usage observed in a
26- rolling time window. As there is no universal recommendation policy that applies to all
27- types of workload , this KEP suggests supporting multiple customized recommenders in VPA.
28- Thus, users can run different recommenders for different workloads, as they may exhibit
29- very distinct resource usage behaviors.
26+ rolling time window. As there is no universal recommendation policy that applies
27+ to all types of workloads , this KEP suggests supporting multiple customized
28+ recommenders in VPA. Thus, users can run different recommenders for different
29+ workloads, as they may exhibit very distinct resource usage behaviors.
3030
3131## Motivation
3232
@@ -48,6 +48,7 @@ behaviors for workloads and applying different algorithms to improve resource ut
4848
4949- Allow the VPA object to specify a customized recommender to use.
5050- Allow the VPA object to use the default recommender when no recommender is specified.
51+ - Don't block future changes that will allow a VPA object to use multiple recommenders at the same time.
5152
5253### Non-Goals
5354
@@ -84,9 +85,12 @@ VPA adopts a reactive approach so a more proactive recommender is needed for the
8485
8586### Implementation Details
8687
87- The following describes the details of implementing a first-citizen approach to support the customized
88- recommender. Namely, a dedicated field ` recommenderName ` is added to the VPA crd definition in
89- ` deploy/vpa-v1.crd.yaml ` .
88+ The following describes the details of implementing a first-citizen approach to
89+ support the customized recommender. Namely, a dedicated field ` recommenders ` is
90+ added to the VPA crd definition in ` deploy/vpa-v1.crd.yaml ` . We identify
91+ recommender to use with a struct containing name of the recommender rather than
92+ using a plain string to identify the recommender. This will allow us to pass
93+ parameters to recommenders if we need to do that in the future.
9094
9195``` yaml
9296validation :
@@ -98,18 +102,32 @@ validation:
98102 type : object
99103 required : []
100104 properties :
101- recommenderName :
102- type : string
105+ recommenders :
106+ type : array
107+ items :
108+ properties :
109+ name :
110+ description : Name of the recommmender
111+ type : string
112+ type : object
103113 targetRef :
104114 type : object
105115 updatePolicy :
106116 type : object
107117` ` `
108118
109119Correspondingly, the ` VerticalPodAutoscalerSpec` in `pkg/apis/autoscaling.k8s.io/v1/types.go`
110- should be updated to include the `recommenderName ` field.
120+ should be updated to include the `recommenders ` field.
111121
112122` ` ` golang
123+
124+ // VerticalPodAutoscalerRecommenderSelector points to a specificic Vertical Pod Autoscaler recommender
125+ // in the future it might pass parameters to the recommender.
126+ type VerticalPodAutoscalerRecommenderSelector struct {
127+ // Name of the recommender responsible for generating recommendation for this object.
128+ Name string ` json:"name" protobuf:"bytes,1,opt,name=name"`
129+ }
130+
113131// VerticalPodAutoscalerSpec is the specification of the behavior of the autoscaler.
114132type VerticalPodAutoscalerSpec struct {
115133 // TargetRef points to the controller managing the set of pods for the
@@ -137,9 +155,11 @@ type VerticalPodAutoscalerSpec struct {
137155 // resources for all containers in the pod, without additional constraints.
138156 // +optional
139157 ResourcePolicy *PodResourcePolicy `json:"resourcePolicy,omitempty" protobuf:"bytes,3,opt,name=resourcePolicy"`
140-
141- // Name of the recommender responsible for generating recommendation for this object.
142- RecommenderName []string `json:"recommenderName,omitempty" protobuf:"bytes,4,opt,name=recommenderName"`
158+ // Recommender responsible for generating recommendation for this object.
159+ // List should be empty (then the default recommender will generate the
160+ // recommendation) or contain exactly one recommender.
161+ // +optional
162+ Recommenders []*VerticalPodAutoscalerRecommenderSelector `json:"recommenders,omitempty" protobuf:"bytes,4,opt,name=recommenders"`
143163}
144164```
145165
@@ -152,7 +172,7 @@ const RecommenderName = "default"
152172recommender := routines.NewRecommender (config, *checkpointsGCInterval, useCheckpoints, RecommenderName , *vpaObjectNamespace)
153173```
154174
155- where the routines.NewRecommender can pass the ` RecommenderName ` to the clusterState object.
175+ where the ` routines.NewRecommender ` can pass the ` RecommenderName ` to the ` clusterState ` object.
156176
157177``` golang
158178// NewRecommender creates a new recommender instance.
@@ -185,9 +205,24 @@ func NewClusterState(recommender_name string) *ClusterState {
185205}
186206```
187207
188- Therefore, when loading VPA objects to the ` clusterStateFeeder ` , it can use the field selector to select VPA CRDs that
189- have ` recommenderName ` equal to the current clusterState’s ` RecommenderName ` .
208+ Therefore, when loading VPA objects to the ` clusterStateFeeder ` , it should
209+ ignore VPA objects that don't have an item in the ` recommenders ` field with
210+ ` name ` equal to the current clusterState’s ` RecommenderName ` .
211+
190212``` golang
213+ func implicitDefaultRecommender (selectors []*VerticalPodAutoscalerRecommenderSelector ) bool {
214+ return len (selectors) == 0
215+ }
216+
217+ func selectsRecommender (selectors []*VerticalPodAutoscalerRecommenderSelector , name *string ) bool {
218+ for _ , s := range (selectors) {
219+ if s.Name == *name {
220+ return true
221+ }
222+ }
223+ return false
224+ }
225+
191226// Fetch VPA objects and load them into the cluster state.
192227func (feeder *clusterStateFeeder ) LoadVPAs () {
193228 // List VPA API objects.
@@ -200,8 +235,8 @@ func (feeder *clusterStateFeeder) LoadVPAs() {
200235 var vpaCRDs []*vpa_types.VerticalPodAutoscaler
201236 for _ , vpaCRD := range allVpaCRDs {
202237 currentRecommenderName := feeder.clusterState .RecommenderName
203- if (vpaCRD.Spec .RecommenderName != currentRecommenderName ) && (vpaCRD.Spec .RecommenderName != " " ) {
204- klog.V (6 ).Infof (" Ignoring the vpaCRD as its name %v is not equal to the current recommender's name %v " , vpaCRD. Spec . RecommenderName , currentRecommenderName)
238+ if (! implicitDefaultRecommender ( vpaCRD.Spec .Recommenders ) && ! selectsRecommender (vpaCRD.Spec .Recommenders , currentRecommenderName) ) {
239+ klog.V (6 ).Infof (" Ignoring the vpaCRD as current recommender's name %v doesn't appear among its recommenders " , currentRecommenderName)
205240 continue
206241 }
207242 vpaCRDs = append (vpaCRDs, vpaCRD)
@@ -216,37 +251,34 @@ func (feeder *clusterStateFeeder) LoadVPAs() {
216251}
217252` ` `
218253
219- Accordingly, the VPA object yaml should include the ` recommenderName` as the default ` RecommenderName` .
220- ` ` ` yaml
221- apiVersion: " autoscaling.k8s.io/v1"
222- kind: VerticalPodAutoscaler
223- metadata:
224- name: hamster-vpa
225- Spec:
226- recommenderName: default
227- targetRef:
228- apiVersion: " apps/v1"
229- ... ...
230- ` ` `
231-
232254### Deployment Details
233- The customized recommender is supposed to be deployed as a separate deployment that is chosen
234- by different sets of VPA objects.Each VPA object is supposed to choose only one recommender at a time.
235- The way how the default recommender and the customized recommender are running and interacting with VPA objects
236- are shown in the following drawing.
255+ The customized recommender is supposed to be deployed as a separate deployment
256+ that is chosen by different sets of VPA objects. Each VPA object is supposed to
257+ choose only one recommender at a time.
258+ The way how the default recommender and the customized recommender are running
259+ and interacting with VPA objects are shown in the following drawing.
237260
238261<img src="images/deployment.png" alt="deployment" width="720" height="360"/>
239262
240- Though we do not support a VPA object to use multiple recommenders in this proposal, we leave the possibility of necessary
241- changes of using multiple recommenders in the future. Namely, we define ` recommenderName` to be an array instead of a string, but we support one element only in this proposal. We modify the admission controller to validate that the array has <= 1 elements.
263+ Though we do not support a VPA object to use multiple recommenders in this
264+ proposal, we leave the possibility of necessary changes of using multiple
265+ recommenders in the future. Namely, we define ` recommenders` to be an array
266+ instead of an object, but we support one element only in this proposal. We
267+ modify the admission controller to validate that the array has <= 1 elements.
242268
243- We will add the following check in the ` func validateVPA (vpa *vpa_types.VerticalPodAutoscaler , isCreate bool )` function.
244- ` ` `
245- if len (vpa.Spec .RecommenderName ) > 1 {
246- return fmt.Errorf (" VPA object shouldn't specify more than one recommenderNames." )
269+ We will add the following check in the
270+ ` func validateVPA (vpa *vpa_types.VerticalPodAutoscaler , isCreate bool )`
271+ function.
272+
273+ ` ` ` golang
274+ if len (vpa.Spec .Recommenders ) > 1 {
275+ return fmt.Errorf (" VPA object shouldn't specify more than one recommender." )
247276 }
248277` ` `
249278
279+ For now we don't allow users to pass any parameters to recommenders but we leave
280+ that as a possibility for the future. We do this by using a struct (which can be
281+ extended if we need to) rather than a string.
250282
251283## Design Details
252284
0 commit comments