Skip to content

Commit cac2d60

Browse files
authored
Merge pull request #4417 from Fedosin/allocation-strategies
Add new on-demand and spot allocation strategies
2 parents 823c581 + 176d4de commit cac2d60

File tree

5 files changed

+122
-8
lines changed

5 files changed

+122
-8
lines changed

config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinepools.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ spec:
731731
instance types to fulfill On-Demand capacity.
732732
enum:
733733
- prioritized
734+
- lowest-price
734735
type: string
735736
onDemandBaseCapacity:
736737
default: 0
@@ -747,6 +748,8 @@ spec:
747748
enum:
748749
- lowest-price
749750
- capacity-optimized
751+
- capacity-optimized-prioritized
752+
- price-capacity-optimized
750753
type: string
751754
type: object
752755
overrides:

exp/api/v1beta2/types.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ var (
137137
// OnDemandAllocationStrategyPrioritized uses the order of instance type overrides
138138
// for the LaunchTemplate to define the launch priority of each instance type.
139139
OnDemandAllocationStrategyPrioritized = OnDemandAllocationStrategy("prioritized")
140+
141+
// OnDemandAllocationStrategyLowestPrice will make the Auto Scaling group launch
142+
// instances using the On-Demand pools with the lowest price, and evenly allocates
143+
// your instances across the On-Demand pools that you specify.
144+
OnDemandAllocationStrategyLowestPrice = OnDemandAllocationStrategy("lowest-price")
140145
)
141146

142147
// SpotAllocationStrategy indicates how to allocate instances across Spot Instance pools.
@@ -151,15 +156,25 @@ var (
151156
// SpotAllocationStrategyCapacityOptimized will make the Auto Scaling group launch
152157
// instances using Spot pools that are optimally chosen based on the available Spot capacity.
153158
SpotAllocationStrategyCapacityOptimized = SpotAllocationStrategy("capacity-optimized")
159+
160+
// SpotAllocationStrategyCapacityOptimizedPrioritized will make the Auto Scaling group launch
161+
// instances using Spot pools that are optimally chosen based on the available Spot capacity
162+
// while also taking into account the priority order specified by the user for Instance Types.
163+
SpotAllocationStrategyCapacityOptimizedPrioritized = SpotAllocationStrategy("capacity-optimized-prioritized")
164+
165+
// SpotAllocationStrategyPriceCapacityOptimized will make the Auto Scaling group launch
166+
// instances using Spot pools that consider both price and available Spot capacity to
167+
// provide a balance between cost savings and allocation reliability.
168+
SpotAllocationStrategyPriceCapacityOptimized = SpotAllocationStrategy("price-capacity-optimized")
154169
)
155170

156171
// InstancesDistribution to configure distribution of On-Demand Instances and Spot Instances.
157172
type InstancesDistribution struct {
158-
// +kubebuilder:validation:Enum=prioritized
173+
// +kubebuilder:validation:Enum=prioritized;lowest-price
159174
// +kubebuilder:default=prioritized
160175
OnDemandAllocationStrategy OnDemandAllocationStrategy `json:"onDemandAllocationStrategy,omitempty"`
161176

162-
// +kubebuilder:validation:Enum=lowest-price;capacity-optimized
177+
// +kubebuilder:validation:Enum=lowest-price;capacity-optimized;capacity-optimized-prioritized;price-capacity-optimized
163178
// +kubebuilder:default=lowest-price
164179
SpotAllocationStrategy SpotAllocationStrategy `json:"spotAllocationStrategy,omitempty"`
165180

exp/controllers/awsmachinepool_controller_test.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,20 @@ func TestAWSMachinePoolReconciler(t *testing.T) {
8282
},
8383
Spec: expinfrav1.AWSMachinePoolSpec{
8484
MinSize: int32(0),
85-
MaxSize: int32(1),
85+
MaxSize: int32(100),
86+
MixedInstancesPolicy: &expinfrav1.MixedInstancesPolicy{
87+
InstancesDistribution: &expinfrav1.InstancesDistribution{
88+
OnDemandAllocationStrategy: expinfrav1.OnDemandAllocationStrategyPrioritized,
89+
SpotAllocationStrategy: expinfrav1.SpotAllocationStrategyCapacityOptimized,
90+
OnDemandBaseCapacity: aws.Int64(0),
91+
OnDemandPercentageAboveBaseCapacity: aws.Int64(100),
92+
},
93+
Overrides: []expinfrav1.Overrides{
94+
{
95+
InstanceType: "m6a.32xlarge",
96+
},
97+
},
98+
},
8699
},
87100
}
88101

@@ -383,7 +396,20 @@ func TestAWSMachinePoolReconciler(t *testing.T) {
383396

384397
asg := expinfrav1.AutoScalingGroup{
385398
MinSize: int32(0),
386-
MaxSize: int32(1),
399+
MaxSize: int32(100),
400+
MixedInstancesPolicy: &expinfrav1.MixedInstancesPolicy{
401+
InstancesDistribution: &expinfrav1.InstancesDistribution{
402+
OnDemandAllocationStrategy: expinfrav1.OnDemandAllocationStrategyPrioritized,
403+
SpotAllocationStrategy: expinfrav1.SpotAllocationStrategyCapacityOptimized,
404+
OnDemandBaseCapacity: aws.Int64(0),
405+
OnDemandPercentageAboveBaseCapacity: aws.Int64(100),
406+
},
407+
Overrides: []expinfrav1.Overrides{
408+
{
409+
InstanceType: "m6a.32xlarge",
410+
},
411+
},
412+
},
387413
Subnets: []string{"subnet1", "subnet2"}}
388414
ec2Svc.EXPECT().ReconcileLaunchTemplate(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
389415
ec2Svc.EXPECT().ReconcileTags(gomock.Any(), gomock.Any()).Return(nil)
@@ -401,7 +427,7 @@ func TestAWSMachinePoolReconciler(t *testing.T) {
401427

402428
asg := expinfrav1.AutoScalingGroup{
403429
MinSize: int32(0),
404-
MaxSize: int32(1),
430+
MaxSize: int32(100),
405431
Subnets: []string{"subnet1", "subnet2"}}
406432
ec2Svc.EXPECT().ReconcileLaunchTemplate(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
407433
ec2Svc.EXPECT().ReconcileTags(gomock.Any(), gomock.Any()).Return(nil)

pkg/cloud/services/autoscaling/autoscalinggroup.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,27 @@ func (s *Service) SDKToAutoScalingGroup(v *autoscaling.Group) (*expinfrav1.AutoS
6565
}
6666

6767
onDemandAllocationStrategy := aws.StringValue(v.MixedInstancesPolicy.InstancesDistribution.OnDemandAllocationStrategy)
68-
if onDemandAllocationStrategy == string(expinfrav1.OnDemandAllocationStrategyPrioritized) {
68+
switch onDemandAllocationStrategy {
69+
case string(expinfrav1.OnDemandAllocationStrategyPrioritized):
6970
i.MixedInstancesPolicy.InstancesDistribution.OnDemandAllocationStrategy = expinfrav1.OnDemandAllocationStrategyPrioritized
71+
case string(expinfrav1.OnDemandAllocationStrategyLowestPrice):
72+
i.MixedInstancesPolicy.InstancesDistribution.OnDemandAllocationStrategy = expinfrav1.OnDemandAllocationStrategyLowestPrice
73+
default:
74+
return nil, fmt.Errorf("unsupported on-demand allocation strategy: %s", onDemandAllocationStrategy)
7075
}
7176

7277
spotAllocationStrategy := aws.StringValue(v.MixedInstancesPolicy.InstancesDistribution.SpotAllocationStrategy)
73-
if spotAllocationStrategy == string(expinfrav1.SpotAllocationStrategyLowestPrice) {
78+
switch spotAllocationStrategy {
79+
case string(expinfrav1.SpotAllocationStrategyLowestPrice):
7480
i.MixedInstancesPolicy.InstancesDistribution.SpotAllocationStrategy = expinfrav1.SpotAllocationStrategyLowestPrice
75-
} else {
81+
case string(expinfrav1.SpotAllocationStrategyCapacityOptimized):
7682
i.MixedInstancesPolicy.InstancesDistribution.SpotAllocationStrategy = expinfrav1.SpotAllocationStrategyCapacityOptimized
83+
case string(expinfrav1.SpotAllocationStrategyCapacityOptimizedPrioritized):
84+
i.MixedInstancesPolicy.InstancesDistribution.SpotAllocationStrategy = expinfrav1.SpotAllocationStrategyCapacityOptimizedPrioritized
85+
case string(expinfrav1.SpotAllocationStrategyPriceCapacityOptimized):
86+
i.MixedInstancesPolicy.InstancesDistribution.SpotAllocationStrategy = expinfrav1.SpotAllocationStrategyPriceCapacityOptimized
87+
default:
88+
return nil, fmt.Errorf("unsupported spot allocation strategy: %s", spotAllocationStrategy)
7789
}
7890
}
7991

pkg/cloud/services/autoscaling/autoscalinggroup_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func TestServiceGetASGByName(t *testing.T) {
9797
MixedInstancesPolicy: &autoscaling.MixedInstancesPolicy{
9898
InstancesDistribution: &autoscaling.InstancesDistribution{
9999
OnDemandAllocationStrategy: aws.String("prioritized"),
100+
SpotAllocationStrategy: aws.String("price-capacity-optimized"),
100101
},
101102
LaunchTemplate: &autoscaling.LaunchTemplate{},
102103
},
@@ -301,6 +302,62 @@ func TestServiceSDKToAutoScalingGroup(t *testing.T) {
301302
},
302303
wantErr: false,
303304
},
305+
{
306+
name: "invalid input - incorrect on-demand allocation strategy",
307+
input: &autoscaling.Group{
308+
AutoScalingGroupARN: aws.String("test-id"),
309+
AutoScalingGroupName: aws.String("test-name"),
310+
DesiredCapacity: aws.Int64(1234),
311+
MaxSize: aws.Int64(1234),
312+
MinSize: aws.Int64(1234),
313+
CapacityRebalance: aws.Bool(true),
314+
MixedInstancesPolicy: &autoscaling.MixedInstancesPolicy{
315+
InstancesDistribution: &autoscaling.InstancesDistribution{
316+
OnDemandAllocationStrategy: aws.String("prioritized"),
317+
OnDemandBaseCapacity: aws.Int64(1234),
318+
OnDemandPercentageAboveBaseCapacity: aws.Int64(1234),
319+
SpotAllocationStrategy: aws.String("INVALIDONDEMANDALLOCATIONSTRATEGY"),
320+
},
321+
LaunchTemplate: &autoscaling.LaunchTemplate{
322+
Overrides: []*autoscaling.LaunchTemplateOverrides{
323+
{
324+
InstanceType: aws.String("t2.medium"),
325+
WeightedCapacity: aws.String("test-weighted-cap"),
326+
},
327+
},
328+
},
329+
},
330+
},
331+
wantErr: true,
332+
},
333+
{
334+
name: "invalid input - incorrect spot allocation strategy",
335+
input: &autoscaling.Group{
336+
AutoScalingGroupARN: aws.String("test-id"),
337+
AutoScalingGroupName: aws.String("test-name"),
338+
DesiredCapacity: aws.Int64(1234),
339+
MaxSize: aws.Int64(1234),
340+
MinSize: aws.Int64(1234),
341+
CapacityRebalance: aws.Bool(true),
342+
MixedInstancesPolicy: &autoscaling.MixedInstancesPolicy{
343+
InstancesDistribution: &autoscaling.InstancesDistribution{
344+
OnDemandAllocationStrategy: aws.String("prioritized"),
345+
OnDemandBaseCapacity: aws.Int64(1234),
346+
OnDemandPercentageAboveBaseCapacity: aws.Int64(1234),
347+
SpotAllocationStrategy: aws.String("INVALIDSPOTALLOCATIONSTRATEGY"),
348+
},
349+
LaunchTemplate: &autoscaling.LaunchTemplate{
350+
Overrides: []*autoscaling.LaunchTemplateOverrides{
351+
{
352+
InstanceType: aws.String("t2.medium"),
353+
WeightedCapacity: aws.String("test-weighted-cap"),
354+
},
355+
},
356+
},
357+
},
358+
},
359+
wantErr: true,
360+
},
304361
}
305362
for _, tt := range tests {
306363
t.Run(tt.name, func(t *testing.T) {
@@ -381,6 +438,7 @@ func TestServiceASGIfExists(t *testing.T) {
381438
MixedInstancesPolicy: &autoscaling.MixedInstancesPolicy{
382439
InstancesDistribution: &autoscaling.InstancesDistribution{
383440
OnDemandAllocationStrategy: aws.String("prioritized"),
441+
SpotAllocationStrategy: aws.String("price-capacity-optimized"),
384442
},
385443
LaunchTemplate: &autoscaling.LaunchTemplate{},
386444
},

0 commit comments

Comments
 (0)