-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add construct library for Application AutoScaling (#933)
Adds a construct library for Application AutoScaling. The DynamoDB construct library has been updated to use the new AutoScaling mechanism, which allows more configuration and uses a Service Linked Role instead of a role per table. BREAKING CHANGE: instead of `addReadAutoScaling()`, call `autoScaleReadCapacity()`, and similar for write scaling. Fixes #856, #861, #640, #644.
- Loading branch information
Showing
41 changed files
with
2,541 additions
and
1,147 deletions.
There are no files selected for viewing
This file contains 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 |
---|---|---|
@@ -1,2 +1,149 @@ | ||
## The CDK Construct Library for AWS Application Auto-Scaling | ||
This module is part of the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) project. | ||
## AWS Application AutoScaling Construct Library | ||
|
||
**Application AutoScaling** is used to configure autoscaling for all | ||
services other than scaling EC2 instances. For example, you will use this to | ||
scale ECS tasks, DynamoDB capacity, Spot Fleet sizes and more. | ||
|
||
As a CDK user, you will probably not have to interact with this library | ||
directly; instead, it will be used by other construct libraries to | ||
offer AutoScaling features for their own constructs. | ||
|
||
This document will describe the general autoscaling features and concepts; | ||
your particular service may offer only a subset of these. | ||
|
||
### AutoScaling basics | ||
|
||
Resources can offer one or more **attributes** to autoscale, typically | ||
representing some capacity dimension of the underlying service. For example, | ||
a DynamoDB Table offers autoscaling of the read and write capacity of the | ||
table proper and its Global Secondary Indexes, an ECS Service offers | ||
autoscaling of its task count, an RDS Aurora cluster offers scaling of its | ||
replica count, and so on. | ||
|
||
When you enable autoscaling for an attribute, you specify a minimum and a | ||
maximum value for the capacity. AutoScaling policies that respond to metrics | ||
will never go higher or lower than the indicated capacity (but scheduled | ||
scaling actions might, see below). | ||
|
||
There are three ways to scale your capacity: | ||
|
||
* **In response to a metric**; for example, you might want to scale out | ||
if the CPU usage across your cluster starts to rise, and scale in | ||
when it drops again. | ||
* **By trying to keep a certain metric around a given value**; you might | ||
want to automatically scale out an in to keep your CPU usage around 50%. | ||
* **On a schedule**; you might want to organize your scaling around traffic | ||
flows you expect, by scaling out in the morning and scaling in in the | ||
evening. | ||
|
||
The general pattern of autoscaling will look like this: | ||
|
||
```ts | ||
const capacity = resource.autoScaleCapacity({ | ||
minCapacity: 5, | ||
maxCapacity: 100 | ||
}); | ||
|
||
// Enable a type of metric scaling and/or schedule scaling | ||
capacity.scaleOnMetric(...); | ||
capacity.scaleToTrackMetric(...); | ||
capacity.scaleOnSchedule(...); | ||
``` | ||
|
||
### AutoScaling in response to a metric | ||
|
||
This type of scaling scales in and out in deterministics steps that you | ||
configure, in response to metric values. For example, your scaling strategy | ||
to scale in response to CPU usage might look like this: | ||
|
||
``` | ||
Scaling -1 (no change) +1 +3 | ||
│ │ │ │ │ | ||
├────────┼───────────────────────┼────────┼────────┤ | ||
│ │ │ │ │ | ||
CPU usage 0% 10% 50% 70% 100% | ||
``` | ||
|
||
(Note that this is not necessarily a recommended scaling strategy, but it's | ||
a possible one. You will have to determine what thresholds are right for you). | ||
|
||
You would configure it like this: | ||
|
||
```ts | ||
capacity.scaleOnMetric('ScaleToCPU', { | ||
metric: service.metricCpuUtilization(), | ||
scalingSteps: [ | ||
{ upper: 10, change: -1 }, | ||
{ lower: 50, change: +1 }, | ||
{ lower: 70, change: +3 }, | ||
], | ||
|
||
// Change this to AdjustmentType.PercentChangeInCapacity to interpret the | ||
// 'change' numbers before as percentages instead of capacity counts. | ||
adjustmentType: autoscaling.AdjustmentType.ChangeInCapacity, | ||
}); | ||
``` | ||
|
||
The AutoScaling construct library will create the required CloudWatch alarms and | ||
AutoScaling policies for you. | ||
|
||
### AutoScaling by tracking a metric value | ||
|
||
This type of scaling scales in and out in order to keep a metric (typically | ||
representing utilization) around a value you prefer. This type of scaling is | ||
typically heavily service-dependent in what metric you can use, and so | ||
different services will have different methods here to set up target tracking | ||
scaling. | ||
|
||
The following example configures the read capacity of a DynamoDB table | ||
to be around 60% utilization: | ||
|
||
```ts | ||
const readCapacity = table.autosScaleReadCapacity({ | ||
minCapacity: 10, | ||
maxCapacity: 1000 | ||
}); | ||
readCapacity.scaleOnUtilization({ | ||
targetUtilizationPercent: 60 | ||
}); | ||
``` | ||
|
||
### AutoScaling on a schedule | ||
|
||
This type of scaling is used to change capacities based on time. It works | ||
by changing the `minCapacity` and `maxCapacity` of the attribute, and so | ||
can be used for two purposes: | ||
|
||
* Scale in and out on a schedule by setting the `minCapacity` high or | ||
the `maxCapacity` low. | ||
* Still allow the regular scaling actions to do their job, but restrict | ||
the range they can scale over (by setting both `minCapacity` and | ||
`maxCapacity` but changing their range over time). | ||
|
||
The following schedule expressions can be used: | ||
|
||
* `at(yyyy-mm-ddThh:mm:ss)` -- scale at a particular moment in time | ||
* `rate(value unit)` -- scale every minute/hour/day | ||
* `cron(mm hh dd mm dow)` -- scale on arbitrary schedules | ||
|
||
Of these, the cron expression is the most useful but also the most | ||
complicated. There is a `Cron` helper class to help build cron expressions. | ||
|
||
The following example scales the fleet out in the morning, and lets natural | ||
scaling take over at night: | ||
|
||
```ts | ||
const capacity = resource.autoScaleCapacity({ | ||
minCapacity: 1, | ||
maxCapacity: 50, | ||
}); | ||
|
||
capacity.scaleOnSchedule('PrescaleInTheMorning', { | ||
schedule: autoscaling.Cron.dailyUtc(8), | ||
minCapacity: 20, | ||
}); | ||
|
||
capacity.scaleOnSchedule('AllowDownscalingAtNight', { | ||
schedule: autoscaling.Cron.dailyUtc(20), | ||
minCapacity: 1 | ||
}); |
99 changes: 99 additions & 0 deletions
99
packages/@aws-cdk/aws-applicationautoscaling/lib/base-scalable-attribute.ts
This file contains 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,99 @@ | ||
import iam = require('@aws-cdk/aws-iam'); | ||
import cdk = require('@aws-cdk/cdk'); | ||
import { ScalableTarget, ScalingSchedule, ServiceNamespace } from './scalable-target'; | ||
import { BasicStepScalingPolicyProps } from './step-scaling-policy'; | ||
import { BasicTargetTrackingScalingPolicyProps } from './target-tracking-scaling-policy'; | ||
|
||
/** | ||
* Properties for a ScalableTableAttribute | ||
*/ | ||
export interface BaseScalableAttributeProps extends EnableScalingProps { | ||
/** | ||
* Service namespace of the scalable attribute | ||
*/ | ||
serviceNamespace: ServiceNamespace; | ||
|
||
/** | ||
* Resource ID of the attribute | ||
*/ | ||
resourceId: string; | ||
|
||
/** | ||
* Scalable dimension of the attribute | ||
*/ | ||
dimension: string; | ||
|
||
/** | ||
* Role to use for scaling | ||
*/ | ||
role: iam.IRole; | ||
} | ||
|
||
/** | ||
* Represent an attribute for which autoscaling can be configured | ||
* | ||
* This class is basically a light wrapper around ScalableTarget, but with | ||
* all methods protected instead of public so they can be selectively | ||
* exposed and/or more specific versions of them can be exposed by derived | ||
* classes for individual services support autoscaling. | ||
* | ||
* Typical use cases: | ||
* | ||
* - Hide away the PredefinedMetric enum for target tracking policies. | ||
* - Don't expose all scaling methods (for example Dynamo tables don't support | ||
* Step Scaling, so the Dynamo subclass won't expose this method). | ||
*/ | ||
export abstract class BaseScalableAttribute extends cdk.Construct { | ||
private target: ScalableTarget; | ||
|
||
public constructor(parent: cdk.Construct, id: string, protected readonly props: BaseScalableAttributeProps) { | ||
super(parent, id); | ||
|
||
this.target = new ScalableTarget(this, 'Target', { | ||
serviceNamespace: this.props.serviceNamespace, | ||
scalableDimension: this.props.dimension, | ||
resourceId: this.props.resourceId, | ||
role: this.props.role, | ||
minCapacity: props.minCapacity !== undefined ? props.minCapacity : 1, | ||
maxCapacity: props.maxCapacity | ||
}); | ||
} | ||
|
||
/** | ||
* Scale out or in based on time | ||
*/ | ||
protected scaleOnSchedule(id: string, props: ScalingSchedule) { | ||
this.target.scaleOnSchedule(id, props); | ||
} | ||
|
||
/** | ||
* Scale out or in based on a metric value | ||
*/ | ||
protected scaleOnMetric(id: string, props: BasicStepScalingPolicyProps) { | ||
this.target.scaleOnMetric(id, props); | ||
} | ||
|
||
/** | ||
* Scale out or in in order to keep a metric around a target value | ||
*/ | ||
protected scaleToTrackMetric(id: string, props: BasicTargetTrackingScalingPolicyProps) { | ||
this.target.scaleToTrackMetric(id, props); | ||
} | ||
} | ||
|
||
/** | ||
* Properties for enabling DynamoDB capacity scaling | ||
*/ | ||
export interface EnableScalingProps { | ||
/** | ||
* Minimum capacity to scale to | ||
* | ||
* @default 1 | ||
*/ | ||
minCapacity?: number; | ||
|
||
/** | ||
* Maximum capacity to scale to | ||
*/ | ||
maxCapacity: number; | ||
} |
This file contains 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,19 @@ | ||
/** | ||
* Helper class to generate Cron expressions | ||
*/ | ||
export class Cron { | ||
|
||
/** | ||
* Return a cron expression to run every day at a particular time | ||
* | ||
* The time is specified in UTC. | ||
* | ||
* @param hour The hour in UTC to schedule this action | ||
* @param minute The minute in the our to schedule this action (defaults to 0) | ||
*/ | ||
public static dailyUtc(hour: number, minute?: number) { | ||
minute = minute || 0; | ||
// 3rd and 5th expression are mutually exclusive, one of them should be ? | ||
return `cron(${minute} ${hour} * * ?)`; | ||
} | ||
} |
This file contains 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 |
---|---|---|
@@ -1,2 +1,9 @@ | ||
// AWS::ApplicationAutoScaling CloudFormation Resources: | ||
export * from './applicationautoscaling.generated'; | ||
|
||
export * from './base-scalable-attribute'; | ||
export * from './cron'; | ||
export * from './scalable-target'; | ||
export * from './step-scaling-policy'; | ||
export * from './step-scaling-action'; | ||
export * from './target-tracking-scaling-policy'; |
Oops, something went wrong.