Skip to content

Commit

Permalink
feat(aws-ecs): ECS service scaling on ALB RequestCount (#1574)
Browse files Browse the repository at this point in the history
  • Loading branch information
BDQ authored and Sam Goodwin committed Jan 23, 2019
1 parent fd1f3a6 commit 8a73877
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 5 deletions.
7 changes: 6 additions & 1 deletion packages/@aws-cdk/aws-ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ const service = new ecs.FargateService(this, 'Service', { /* ... */ });

const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
const listener = lb.addListener('Listener', { port: 80 });
listener.addTargets('ECS', {
const target = listener.addTargets('ECS', {
port: 80,
targets: [service]
});
Expand All @@ -226,6 +226,11 @@ const scaling = service.autoScaleTaskCount({ maxCapacity: 10 });
scaling.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 50
});

scaling.scaleOnRequestCount('RequestScaling', {
requestsPerTarget: 10000,
targetGroup: target
})
```

Task AutoScaling is powered by *Application AutoScaling*. Refer to that for
Expand Down
42 changes: 38 additions & 4 deletions packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import appscaling = require('@aws-cdk/aws-applicationautoscaling');
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');

/**
* Scalable attribute representing task count
Expand Down Expand Up @@ -29,21 +30,39 @@ export class ScalableTaskCount extends appscaling.BaseScalableAttribute {
disableScaleIn: props.disableScaleIn,
targetValue: props.targetUtilizationPercent,
scaleInCooldownSec: props.scaleInCooldownSec,
scaleOutCooldownSec: props.scaleOutCooldownSec,
scaleOutCooldownSec: props.scaleOutCooldownSec
});
}

/**
* Scale out or in to achieve a target memory utilization utilization
* Scale out or in to achieve a target memory utilization
*/
public scaleOnMemoryUtilization(id: string, props: CpuUtilizationScalingProps) {
public scaleOnMemoryUtilization(id: string, props: MemoryUtilizationScalingProps) {
return super.doScaleToTrackMetric(id, {
predefinedMetric: appscaling.PredefinedMetric.ECSServiceAverageMemoryUtilization,
targetValue: props.targetUtilizationPercent,
policyName: props.policyName,
disableScaleIn: props.disableScaleIn,
scaleInCooldownSec: props.scaleInCooldownSec,
scaleOutCooldownSec: props.scaleOutCooldownSec,
scaleOutCooldownSec: props.scaleOutCooldownSec
});
}

/**
* Scale out or in to achieve a target ALB request count per target
*/
public scaleOnRequestCount(id: string, props: RequestCountScalingProps) {
const resourceLabel = props.targetGroup.firstLoadBalancerFullName +
'/' + props.targetGroup.targetGroupFullName;

return super.doScaleToTrackMetric(id, {
predefinedMetric: appscaling.PredefinedMetric.ALBRequestCountPerTarget,
resourceLabel,
targetValue: props.requestsPerTarget,
policyName: props.policyName,
disableScaleIn: props.disableScaleIn,
scaleInCooldownSec: props.scaleInCooldownSec,
scaleOutCooldownSec: props.scaleOutCooldownSec
});
}

Expand Down Expand Up @@ -82,6 +101,21 @@ export interface MemoryUtilizationScalingProps extends appscaling.BaseTargetTrac
targetUtilizationPercent: number;
}

/**
* Properties for enabling scaling based on ALB request counts
*/
export interface RequestCountScalingProps extends appscaling.BaseTargetTrackingProps {
/**
* ALB requests per target
*/
requestsPerTarget: number;

/**
* ALB Target Group
*/
targetGroup: elbv2.ApplicationTargetGroup;
}

/**
* Properties to target track a custom metric
*/
Expand Down
56 changes: 56 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert';
import ec2 = require('@aws-cdk/aws-ec2');
import elbv2 = require("@aws-cdk/aws-elasticloadbalancingv2");
import cdk = require('@aws-cdk/cdk');
import { Test } from 'nodeunit';
import ecs = require('../../lib');
Expand Down Expand Up @@ -156,4 +157,59 @@ export = {
test.done();
},
},

"When adding an app load balancer": {
'allows auto scaling by ALB request per target'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.VpcNetwork(stack, 'MyVpc', {});
const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc });
const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef');
const container = taskDefinition.addContainer('MainContainer', {
image: ContainerImage.fromDockerHub('hello'),
});
container.addPortMappings({ containerPort: 8000 });
const service = new ecs.FargateService(stack, 'Service', { cluster, taskDefinition });

const lb = new elbv2.ApplicationLoadBalancer(stack, "lb", { vpc });
const listener = lb.addListener("listener", { port: 80 });
const targetGroup = listener.addTargets("target", {
port: 80,
targets: [service]
});

// WHEN
const capacity = service.autoScaleTaskCount({ maxCapacity: 10, minCapacity: 1 });
capacity.scaleOnRequestCount("ScaleOnRequests", {
requestsPerTarget: 1000,
targetGroup
});

// THEN
expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', {
MaxCapacity: 10,
MinCapacity: 1
}));

expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalingPolicy', {
TargetTrackingScalingPolicyConfiguration: {
PredefinedMetricSpecification: {
PredefinedMetricType: "ALBRequestCountPerTarget",
ResourceLabel: {
"Fn::Join": ["", [
{ "Fn::Select": [1, { "Fn::Split": ["/", { Ref: "lblistener657ADDEC" }] }] }, "/",
{ "Fn::Select": [2, { "Fn::Split": ["/", { Ref: "lblistener657ADDEC" }] }] }, "/",
{ "Fn::Select": [3, { "Fn::Split": ["/", { Ref: "lblistener657ADDEC" }] }] }, "/",
{ "Fn::GetAtt": ["lblistenertargetGroupC7489D1E", "TargetGroupFullName"] }
]]
}
},
TargetValue: 1000
}
}));

test.done();
}

}
};

0 comments on commit 8a73877

Please sign in to comment.