-
Notifications
You must be signed in to change notification settings - Fork 91
feat(poolautoscaler): add PoolAutoscaler CRD with capacity-based and cron scaling #625
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
Open
chrisliu1995
wants to merge
1
commit into
openkruise:master
Choose a base branch
from
chrisliu1995:feature/pool-autoscaler
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,227 @@ | ||
| /* | ||
| Copyright 2026. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| */ | ||
|
|
||
| package v1alpha1 | ||
|
|
||
| import ( | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/util/intstr" | ||
| ) | ||
|
|
||
| var PoolAutoscalerControllerKind = GroupVersion.WithKind("PoolAutoscaler") | ||
|
|
||
| // CrossVersionObjectReference contains enough information to let you identify the referred resource. | ||
| type CrossVersionObjectReference struct { | ||
| // Kind of the referent; More info: https://git.k8s.io/community/contributors/dede/sig-architecture/api-conventions.md#types-kinds | ||
| // +required | ||
| Kind string `json:"kind"` | ||
| // Name of the referent; More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names | ||
| // +required | ||
| Name string `json:"name"` | ||
| // API version of the referent | ||
| // +optional | ||
| APIVersion string `json:"apiVersion,omitempty"` | ||
| } | ||
|
|
||
| // PoolAutoscalerSpec describes the desired functionality of the PoolAutoscaler. | ||
| type PoolAutoscalerSpec struct { | ||
| // ScaleTargetRef points to the target warming pool to scale, and is used to select the pods for which instance status | ||
| // should be collected, as well as to actually change the replica count. | ||
| // +required | ||
| ScaleTargetRef CrossVersionObjectReference `json:"scaleTargetRef"` | ||
|
|
||
| // MaxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. | ||
| // It cannot be less than minReplicas. | ||
| // +required | ||
| MaxReplicas int32 `json:"maxReplicas"` | ||
|
|
||
| // MinReplicas is the lower limit for the number of replicas to which the autoscaler | ||
| // can scale down. It defaults to 0 pods. | ||
| // +optional | ||
| MinReplicas int32 `json:"minReplicas"` | ||
|
|
||
| // CronPolicies is a list of potential cron scaling policies which can be used during scaling. | ||
| // When both CronPolicies and CapacityPolicy are set, CronPolicies takes higher priority. | ||
| // +optional | ||
| CronPolicies []CronScalingPolicy `json:"cronPolicies,omitempty"` | ||
|
|
||
| // CapacityPolicy defines the capacity configuration of the target resource pool. | ||
| // +optional | ||
| CapacityPolicy *CapacityPolicy `json:"capacityPolicy,omitempty"` | ||
|
|
||
| // Suspend tells the controller to suspend subsequent executions. | ||
| // Defaults to false. | ||
| // +optional | ||
| Suspend *bool `json:"suspend,omitempty"` | ||
| } | ||
|
|
||
| // CronScalingPolicy defines the cron-based scaling configuration for the resource pool. | ||
| type CronScalingPolicy struct { | ||
| // Name is used to specify the scaling policy. | ||
| // +required | ||
| Name string `json:"name"` | ||
|
|
||
| // TimeZone is the time zone name for the given schedule, e.g. "Asia/Shanghai", "UTC". | ||
| // If not specified, this will default to the time zone of the autoscaler controller manager process. | ||
| // +optional | ||
| TimeZone *string `json:"timeZone,omitempty"` | ||
|
|
||
| // Schedule is a cron expression that defines when this policy should be executed. | ||
| // Supports standard cron format with 5 fields (minute hour day month weekday). | ||
| // +required | ||
| Schedule string `json:"schedule"` | ||
|
|
||
| // TargetReplicas is the desired replicas when this policy executes. | ||
| // +required | ||
| TargetReplicas int32 `json:"targetReplicas"` | ||
| } | ||
|
|
||
| // CapacityPolicy defines the capacity configuration of the target resource pool. | ||
| type CapacityPolicy struct { | ||
| // TargetAvailable is the desired available replicas. | ||
| // Can be an absolute number (ex: 5) or a percentage of current replicas (ex: 70%). | ||
| // +required | ||
| TargetAvailable intstr.IntOrString `json:"targetAvailable"` | ||
|
|
||
| // Tolerance is the tolerance between the watermark and desired value under which | ||
| // no updates are made to the desired number of replicas. | ||
| // Can be an absolute number (ex: 5) or a percentage (ex: 10%). | ||
| // If not set, defaults to 10%. | ||
| // +optional | ||
| Tolerance *intstr.IntOrString `json:"tolerance,omitempty"` | ||
|
|
||
| // ScaleUp is the scaling rule for scaling up. | ||
| // +optional | ||
| ScaleUp *CapacityScalingRules `json:"scaleUp,omitempty"` | ||
|
|
||
| // ScaleDown is the scaling rule for scaling down. | ||
| // +optional | ||
| ScaleDown *CapacityScalingRules `json:"scaleDown,omitempty"` | ||
| } | ||
|
|
||
| // CapacityScalingRules configures the scaling behavior for one direction. | ||
| type CapacityScalingRules struct { | ||
| // StabilizationWindowSeconds is the number of seconds for which past recommendations should be | ||
| // considered while scaling up or scaling down. | ||
| // Must be >= 0 and <= 3600 (one hour). | ||
| // Defaults: scale up: 0, scale down: 300. | ||
| // +optional | ||
| StabilizationWindowSeconds *int32 `json:"stabilizationWindowSeconds,omitempty"` | ||
| } | ||
|
|
||
| // PoolAutoscalerStatus describes the current status of a pool autoscaler. | ||
| type PoolAutoscalerStatus struct { | ||
| // ObservedGeneration is the most recent generation observed by this autoscaler. | ||
| // +optional | ||
| ObservedGeneration *int64 `json:"observedGeneration,omitempty"` | ||
|
|
||
| // LastScaleTime is the last time the PoolAutoscaler scaled the number of pods. | ||
| // +optional | ||
| LastScaleTime *metav1.Time `json:"lastScaleTime,omitempty"` | ||
|
|
||
| // CurrentReplicas is current number of replicas of pods managed by this autoscaler. | ||
| CurrentReplicas int32 `json:"currentReplicas"` | ||
|
|
||
| // DesiredReplicas is the desired number of replicas of pods managed by this autoscaler. | ||
| DesiredReplicas int32 `json:"desiredReplicas"` | ||
|
|
||
| // Suspended indicates whether the autoscaler is currently suspended. | ||
| // +optional | ||
| Suspended bool `json:"suspended,omitempty"` | ||
|
|
||
| // AppliedCronPolicies is the execution status of cron policies. | ||
| // +optional | ||
| AppliedCronPolicies []CronScalingPolicyStatus `json:"appliedCronPolicies,omitempty"` | ||
|
|
||
| // CurrentCapacity is the last read state of the capacity used by this autoscaler. | ||
| // +optional | ||
| CurrentCapacity CapacityStatus `json:"currentCapacity,omitempty"` | ||
|
|
||
| // Conditions is the set of conditions required for this autoscaler to scale its target. | ||
| // +optional | ||
| Conditions []metav1.Condition `json:"conditions,omitempty"` | ||
| } | ||
|
|
||
| // CronScalingPolicyStatus records the last schedule time for a cron policy. | ||
| type CronScalingPolicyStatus struct { | ||
| // Name is the cron policy name. | ||
| Name string `json:"name"` | ||
|
|
||
| // LastScheduleTime is the last time the policy was successfully scheduled. | ||
| // +optional | ||
| LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"` | ||
| } | ||
|
|
||
| // CapacityStatus records the current capacity observed by the autoscaler. | ||
| type CapacityStatus struct { | ||
| // Available is current number of available pods managed by this autoscaler. | ||
| Available int32 `json:"available"` | ||
| } | ||
|
|
||
| // PoolAutoscalerConditionType are the valid conditions of a PoolAutoscaler. | ||
| type PoolAutoscalerConditionType string | ||
|
|
||
| const ( | ||
| // ScalingActive indicates that the autoscaler is able to scale. | ||
| ScalingActive PoolAutoscalerConditionType = "ScalingActive" | ||
| // AbleToScale indicates that the autoscaler is able to calculate and set scale. | ||
| AbleToScale PoolAutoscalerConditionType = "AbleToScale" | ||
| // ScalingLimited indicates that the autoscaler is constrained by min/max bounds. | ||
| ScalingLimited PoolAutoscalerConditionType = "ScalingLimited" | ||
| ) | ||
|
|
||
| // +genclient | ||
| // +kubebuilder:object:root=true | ||
| // +kubebuilder:subresource:status | ||
| // +kubebuilder:resource:path=poolautoscalers,shortName={pa},singular=poolautoscaler | ||
| // +kubebuilder:storageversion | ||
| // +kubebuilder:printcolumn:name="Reference",type="string",JSONPath=".spec.scaleTargetRef.name" | ||
| // +kubebuilder:printcolumn:name="MinReplicas",type="integer",JSONPath=".spec.minReplicas" | ||
| // +kubebuilder:printcolumn:name="MaxReplicas",type="integer",JSONPath=".spec.maxReplicas" | ||
| // +kubebuilder:printcolumn:name="CurrentReplicas",type="integer",JSONPath=".status.currentReplicas" | ||
| // +kubebuilder:printcolumn:name="DesiredReplicas",type="integer",JSONPath=".status.desiredReplicas" | ||
| // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" | ||
|
|
||
| // PoolAutoscaler is the configuration for a warming pool autoscaler, | ||
| // which automatically manages the replica count of the warming pool | ||
| // based on the policies specified. | ||
| type PoolAutoscaler struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
|
|
||
| // +optional | ||
| metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` | ||
|
|
||
| // Spec defines the desired behavior of the autoscaler. | ||
| // +optional | ||
| Spec PoolAutoscalerSpec `json:"spec,omitempty"` | ||
|
|
||
| // Status is the current information about the autoscaler. | ||
| // +optional | ||
| Status PoolAutoscalerStatus `json:"status,omitempty"` | ||
| } | ||
|
|
||
| // +kubebuilder:object:root=true | ||
|
|
||
| // PoolAutoscalerList contains a list of PoolAutoscaler. | ||
| type PoolAutoscalerList struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
| metav1.ListMeta `json:"metadata,omitempty"` | ||
| Items []PoolAutoscaler `json:"items"` | ||
| } | ||
|
|
||
| func init() { | ||
| SchemeBuilder.Register(&PoolAutoscaler{}, &PoolAutoscalerList{}) | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Are CronPolicies and CapacityPolicy mutually exclusive? If both are set simultaneously, would the implementation be complex and the logic unclear?
If not well thought through, I suggest not supporting both at the same time for now — this can be enforced via webhook validation. Additionally, if the controller detects both are set, it should log a warning. CronPolicies should take higher priority.
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.
The current implementation supports both CronPolicies and CapacityPolicy coexisting, with CronPolicies taking strict priority:
This design serves a common production scenario: users configure cron policies to pre-warm the pool ahead of known traffic peaks (e.g., scale to 200 at 9:00 AM on weekdays), while capacity policy handles the unpredictable demand during off-peak hours. There is no ambiguity or conflict — at any given reconcile, exactly one path produces the desired replicas.Additionally, the stabilization window now checks max(lastScaleUpAt, lastScaleDownAt) — meaning a cron-triggered scale-up will not be immediately reverted by capacity scale-down until the scaleDown.stabilizationWindowSeconds has elapsed. This prevents thrashing between the two policies.If you still prefer mutual exclusivity for simplicity in the initial version, I can add a webhook rejection. Let me know your preference.