Skip to content

Commit 8a73877

Browse files
BDQSam Goodwin
authored and
Sam Goodwin
committed
feat(aws-ecs): ECS service scaling on ALB RequestCount (#1574)
1 parent fd1f3a6 commit 8a73877

File tree

3 files changed

+100
-5
lines changed

3 files changed

+100
-5
lines changed

Diff for: packages/@aws-cdk/aws-ecs/README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ const service = new ecs.FargateService(this, 'Service', { /* ... */ });
205205

206206
const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
207207
const listener = lb.addListener('Listener', { port: 80 });
208-
listener.addTargets('ECS', {
208+
const target = listener.addTargets('ECS', {
209209
port: 80,
210210
targets: [service]
211211
});
@@ -226,6 +226,11 @@ const scaling = service.autoScaleTaskCount({ maxCapacity: 10 });
226226
scaling.scaleOnCpuUtilization('CpuScaling', {
227227
targetUtilizationPercent: 50
228228
});
229+
230+
scaling.scaleOnRequestCount('RequestScaling', {
231+
requestsPerTarget: 10000,
232+
targetGroup: target
233+
})
229234
```
230235

231236
Task AutoScaling is powered by *Application AutoScaling*. Refer to that for

Diff for: packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts

+38-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import appscaling = require('@aws-cdk/aws-applicationautoscaling');
22
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
3+
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
34

45
/**
56
* Scalable attribute representing task count
@@ -29,21 +30,39 @@ export class ScalableTaskCount extends appscaling.BaseScalableAttribute {
2930
disableScaleIn: props.disableScaleIn,
3031
targetValue: props.targetUtilizationPercent,
3132
scaleInCooldownSec: props.scaleInCooldownSec,
32-
scaleOutCooldownSec: props.scaleOutCooldownSec,
33+
scaleOutCooldownSec: props.scaleOutCooldownSec
3334
});
3435
}
3536

3637
/**
37-
* Scale out or in to achieve a target memory utilization utilization
38+
* Scale out or in to achieve a target memory utilization
3839
*/
39-
public scaleOnMemoryUtilization(id: string, props: CpuUtilizationScalingProps) {
40+
public scaleOnMemoryUtilization(id: string, props: MemoryUtilizationScalingProps) {
4041
return super.doScaleToTrackMetric(id, {
4142
predefinedMetric: appscaling.PredefinedMetric.ECSServiceAverageMemoryUtilization,
4243
targetValue: props.targetUtilizationPercent,
4344
policyName: props.policyName,
4445
disableScaleIn: props.disableScaleIn,
4546
scaleInCooldownSec: props.scaleInCooldownSec,
46-
scaleOutCooldownSec: props.scaleOutCooldownSec,
47+
scaleOutCooldownSec: props.scaleOutCooldownSec
48+
});
49+
}
50+
51+
/**
52+
* Scale out or in to achieve a target ALB request count per target
53+
*/
54+
public scaleOnRequestCount(id: string, props: RequestCountScalingProps) {
55+
const resourceLabel = props.targetGroup.firstLoadBalancerFullName +
56+
'/' + props.targetGroup.targetGroupFullName;
57+
58+
return super.doScaleToTrackMetric(id, {
59+
predefinedMetric: appscaling.PredefinedMetric.ALBRequestCountPerTarget,
60+
resourceLabel,
61+
targetValue: props.requestsPerTarget,
62+
policyName: props.policyName,
63+
disableScaleIn: props.disableScaleIn,
64+
scaleInCooldownSec: props.scaleInCooldownSec,
65+
scaleOutCooldownSec: props.scaleOutCooldownSec
4766
});
4867
}
4968

@@ -82,6 +101,21 @@ export interface MemoryUtilizationScalingProps extends appscaling.BaseTargetTrac
82101
targetUtilizationPercent: number;
83102
}
84103

104+
/**
105+
* Properties for enabling scaling based on ALB request counts
106+
*/
107+
export interface RequestCountScalingProps extends appscaling.BaseTargetTrackingProps {
108+
/**
109+
* ALB requests per target
110+
*/
111+
requestsPerTarget: number;
112+
113+
/**
114+
* ALB Target Group
115+
*/
116+
targetGroup: elbv2.ApplicationTargetGroup;
117+
}
118+
85119
/**
86120
* Properties to target track a custom metric
87121
*/

Diff for: packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts

+56
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert';
22
import ec2 = require('@aws-cdk/aws-ec2');
3+
import elbv2 = require("@aws-cdk/aws-elasticloadbalancingv2");
34
import cdk = require('@aws-cdk/cdk');
45
import { Test } from 'nodeunit';
56
import ecs = require('../../lib');
@@ -156,4 +157,59 @@ export = {
156157
test.done();
157158
},
158159
},
160+
161+
"When adding an app load balancer": {
162+
'allows auto scaling by ALB request per target'(test: Test) {
163+
// GIVEN
164+
const stack = new cdk.Stack();
165+
const vpc = new ec2.VpcNetwork(stack, 'MyVpc', {});
166+
const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc });
167+
const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef');
168+
const container = taskDefinition.addContainer('MainContainer', {
169+
image: ContainerImage.fromDockerHub('hello'),
170+
});
171+
container.addPortMappings({ containerPort: 8000 });
172+
const service = new ecs.FargateService(stack, 'Service', { cluster, taskDefinition });
173+
174+
const lb = new elbv2.ApplicationLoadBalancer(stack, "lb", { vpc });
175+
const listener = lb.addListener("listener", { port: 80 });
176+
const targetGroup = listener.addTargets("target", {
177+
port: 80,
178+
targets: [service]
179+
});
180+
181+
// WHEN
182+
const capacity = service.autoScaleTaskCount({ maxCapacity: 10, minCapacity: 1 });
183+
capacity.scaleOnRequestCount("ScaleOnRequests", {
184+
requestsPerTarget: 1000,
185+
targetGroup
186+
});
187+
188+
// THEN
189+
expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', {
190+
MaxCapacity: 10,
191+
MinCapacity: 1
192+
}));
193+
194+
expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalingPolicy', {
195+
TargetTrackingScalingPolicyConfiguration: {
196+
PredefinedMetricSpecification: {
197+
PredefinedMetricType: "ALBRequestCountPerTarget",
198+
ResourceLabel: {
199+
"Fn::Join": ["", [
200+
{ "Fn::Select": [1, { "Fn::Split": ["/", { Ref: "lblistener657ADDEC" }] }] }, "/",
201+
{ "Fn::Select": [2, { "Fn::Split": ["/", { Ref: "lblistener657ADDEC" }] }] }, "/",
202+
{ "Fn::Select": [3, { "Fn::Split": ["/", { Ref: "lblistener657ADDEC" }] }] }, "/",
203+
{ "Fn::GetAtt": ["lblistenertargetGroupC7489D1E", "TargetGroupFullName"] }
204+
]]
205+
}
206+
},
207+
TargetValue: 1000
208+
}
209+
}));
210+
211+
test.done();
212+
}
213+
214+
}
159215
};

0 commit comments

Comments
 (0)