Skip to content
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

feat(lambda): autoscaling for lambda aliases #8883

Merged
merged 47 commits into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
06ad798
preliminary API
Jul 2, 2020
2aeebb4
fixed awslint issues
Jul 2, 2020
2ba8685
Merge branch 'master' into autoscale-lambda
kaizencc Jul 2, 2020
721f9b3
added dependency
Jul 2, 2020
67ce00a
Merge branch 'autoscale-lambda' of https://github.com/kaizen3031593/a…
Jul 2, 2020
9d50cd3
removed autoscaling from lambda-version
Jul 6, 2020
595b408
modified things based off of code review
Jul 6, 2020
7a2c60f
added unit tests
Jul 6, 2020
b3c5c11
added integ test
Jul 7, 2020
e78c0f4
fixed linting issues
Jul 7, 2020
883c377
Merge branch 'master' into autoscale-lambda
kaizencc Jul 7, 2020
02e1c86
added readme comments
Jul 7, 2020
614f109
updated alias with implicit dependency
Jul 7, 2020
41935b2
Merge branch 'master' into autoscale-lambda
kaizencc Jul 7, 2020
4c64b6c
typo
Jul 7, 2020
710a623
updated readme
Jul 7, 2020
66b4ee9
made minor edits
Jul 7, 2020
0b53cf8
typo
Jul 7, 2020
77884af
made requested changes
Jul 16, 2020
627390c
Merge branch 'master' into autoscale-lambda
kaizencc Jul 16, 2020
73bdf11
typo
Jul 16, 2020
35df97a
last minute doc changes
Jul 16, 2020
6a5e976
updated docs and readme
Jul 20, 2020
34ae9d5
changed targetUtilizationPercent to targetUtilizationValue
Jul 21, 2020
e999fa2
Merge branch 'master' into autoscale-lambda
kaizencc Jul 21, 2020
3e162e4
readme modification
Jul 21, 2020
133c161
missed a few changes
Jul 21, 2020
7244d4a
yet another
Jul 21, 2020
b513569
update integ test
Jul 21, 2020
ba7d067
incorporated elads comments
Jul 22, 2020
54d10dc
minor edit
Jul 22, 2020
83b5f10
Merge branch 'master' into autoscale-lambda
kaizencc Jul 22, 2020
ed87489
fixed test case
Jul 22, 2020
eb907d8
readme change
Jul 22, 2020
e73ae72
satisfied aws-lint
Aug 3, 2020
21a3e46
Merge branch 'master' into autoscale-lambda
kaizencc Aug 3, 2020
35044db
Merge branch 'master' into autoscale-lambda
NetaNir Aug 3, 2020
7f3e403
cr changes
NetaNir Aug 6, 2020
281a871
spaces everywhere
NetaNir Aug 6, 2020
6c02e22
temp
NetaNir Aug 10, 2020
57d51a6
split files to allow not exporting private types
NetaNir Aug 11, 2020
86be93f
Merge branch 'master' into autoscale-lambda
NetaNir Aug 11, 2020
741b5a5
fix docs
NetaNir Aug 11, 2020
74ea805
fix;
NetaNir Aug 11, 2020
9ab7fee
final changes
Aug 11, 2020
3519d02
Merge branch 'master' into autoscale-lambda
NetaNir Aug 11, 2020
90138af
Merge branch 'master' into autoscale-lambda
NetaNir Aug 11, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion packages/@aws-cdk/aws-lambda/lib/alias.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import * as appscaling from '@aws-cdk/aws-applicationautoscaling';
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import { Construct } from '@aws-cdk/core';
import * as iam from '@aws-cdk/aws-iam';
import { Construct, Stack} from '@aws-cdk/core';
import { EventInvokeConfigOptions } from './event-invoke-config';
import { IFunction, QualifiedFunctionBase } from './function-base';
import { extractQualifierFromArn, IVersion } from './lambda-version';
import { CfnAlias } from './lambda.generated';
import { EnableScalingProps, IScalableVersionAttribute, ScalableVersionAttribute } from './scalable-version-attribute';

export interface IAlias extends IFunction {
/**
Expand Down Expand Up @@ -129,6 +132,9 @@ export class Alias extends QualifiedFunctionBase implements IAlias {

protected readonly canCreatePermissions: boolean = true;

private provisionedConcurrency: boolean;
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
private scalableAlias?: ScalableVersionAttribute;

constructor(scope: Construct, id: string, props: AliasProps) {
super(scope, id, {
physicalName: props.aliasName,
Expand All @@ -147,6 +153,8 @@ export class Alias extends QualifiedFunctionBase implements IAlias {
provisionedConcurrencyConfig: this.determineProvisionedConcurrency(props),
});

this.provisionedConcurrency = props.provisionedConcurrentExecutions ? true : false;
kaizencc marked this conversation as resolved.
Show resolved Hide resolved

this.functionArn = this.getResourceArnAttribute(alias.ref, {
service: 'lambda',
resource: 'function',
Expand Down Expand Up @@ -193,6 +201,39 @@ export class Alias extends QualifiedFunctionBase implements IAlias {
});
}

/**
* Enable autoscaling for the given lambda version. Requires that lambda version has provisioned concurrency.
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
* @param props The properties for autoscaling
*/
public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute {
if (!this.provisionedConcurrency) {
throw new Error('Autoscaling is available for aliases with provisioned concurrency only');
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
}
if (this.scalableAlias) {
throw new Error('Autoscaling already enabled for this alias');
}
// Use a Service Linked Role
// https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html
const role = iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({
NetaNir marked this conversation as resolved.
Show resolved Hide resolved
service: 'iam',
region: '',
resource: 'role/aws-service-role/lambda.application-autoscaling.amazonaws.com',
resourceName: 'AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency',
}));

this.scalableAlias = new ScalableVersionAttribute(this, 'VersionScaling', {
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
serviceNamespace: appscaling.ServiceNamespace.LAMBDA,
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
dimension: 'lambda:function:ProvisionedConcurrency',
minCapacity: props.minCapacity,
maxCapacity: props.maxCapacity,
resourceId: `function:${this.lambda.functionName}:${this.aliasName}`,
role,
});

// Make sure that alias is created before the autoscale resources
this.scalableAlias.node.addDependency(this);
}
kaizencc marked this conversation as resolved.
Show resolved Hide resolved

/**
* Calculate the routingConfig parameter from the input props
*/
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-lambda/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from './event-source';
export * from './event-source-mapping';
export * from './destination';
export * from './event-invoke-config';
export * from './scalable-version-attribute';

export * from './log-retention';

Expand Down
55 changes: 54 additions & 1 deletion packages/@aws-cdk/aws-lambda/lib/lambda-version.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import * as appscaling from '@aws-cdk/aws-applicationautoscaling';
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import { Construct, Fn, RemovalPolicy } from '@aws-cdk/core';
import * as iam from '@aws-cdk/aws-iam';
import { Construct, Fn, RemovalPolicy, Stack } from '@aws-cdk/core';
import { Alias, AliasOptions } from './alias';
import { EventInvokeConfigOptions } from './event-invoke-config';
import { Function } from './function';
import { IFunction, QualifiedFunctionBase } from './function-base';
import { CfnVersion } from './lambda.generated';
import { EnableScalingProps, IScalableVersionAttribute, ScalableVersionAttribute } from './scalable-version-attribute';
import { addAlias } from './util';

export interface IVersion extends IFunction {
Expand All @@ -25,6 +28,12 @@ export interface IVersion extends IFunction {
* @param options Alias options
*/
addAlias(aliasName: string, options?: AliasOptions): Alias;

// /**
// * Enables autoscaling on a version with provisioned concurrency
// * @param props The properties for autoscaling
// */
// autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute;
}

/**
Expand Down Expand Up @@ -128,6 +137,10 @@ export class Version extends QualifiedFunctionBase implements IVersion {
public addAlias(name: string, opts: AliasOptions = { }): Alias {
return addAlias(this, this, name, opts);
}

public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute {
return this.autoScaleProvisionedConcurrency(props);
}
}
return new Import(scope, id);
}
Expand All @@ -147,6 +160,11 @@ export class Version extends QualifiedFunctionBase implements IVersion {
public addAlias(name: string, opts: AliasOptions = { }): Alias {
return addAlias(this, this, name, opts);
}

public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute {
return this.autoScaleProvisionedConcurrency(props);
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
}

}
return new Import(scope, id);
}
Expand All @@ -159,6 +177,9 @@ export class Version extends QualifiedFunctionBase implements IVersion {
protected readonly qualifier: string;
protected readonly canCreatePermissions = true;

private provisionedConcurrency: boolean;
private scalableVersion?: ScalableVersionAttribute;

constructor(scope: Construct, id: string, props: VersionProps) {
super(scope, id);

Expand All @@ -177,6 +198,8 @@ export class Version extends QualifiedFunctionBase implements IVersion {
});
}

this.provisionedConcurrency = props.provisionedConcurrentExecutions ? true : false;

this.version = version.attrVersion;
this.functionArn = version.ref;
this.functionName = `${this.lambda.functionName}:${this.version}`;
Expand Down Expand Up @@ -223,6 +246,36 @@ export class Version extends QualifiedFunctionBase implements IVersion {
return addAlias(this, this, aliasName, options);
}

/**
* Enable autoscaling for the given lambda version. Requires that lambda version has provisioned concurrency.
* @param props The properties for autoscaling
*/
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute {
if (!this.provisionedConcurrency) {
throw new Error('Autoscaling is available for versions with provisioned concurrency only');
}
if (this.scalableVersion) {
throw new Error('Autoscaling already enabled for this version');
}
// Use a Service Linked Role
// https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html
const role = iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({
service: 'iam',
region: '',
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
resource: 'role/aws-service-role/lambda.application-autoscaling.amazonaws.com',
resourceName: 'AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency',
}));

return this.scalableVersion = new ScalableVersionAttribute(this, 'VersionScaling', {
serviceNamespace: appscaling.ServiceNamespace.LAMBDA,
dimension: 'lambda:function:ProvisionedConcurrency',
minCapacity: props.minCapacity,
maxCapacity: props.maxCapacity,
resourceId: `function:${this.lambda.functionName}:${this.version}`,
role,
});
}

/**
* Validate that the provisionedConcurrentExecutions makes sense
*
Expand Down
65 changes: 65 additions & 0 deletions packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as appscaling from '@aws-cdk/aws-applicationautoscaling';

/**
* Interface for scalable attributes
*/
export interface IScalableVersionAttribute {
/**
* Scale out or in to keep utilization at a given level
*/
scaleOnUtilization(props: UtilizationScalingProps): void;
/**
* Add scheduled scaling for this scaling attribute
*/
scaleOnSchedule(id: string, actions: appscaling.ScalingSchedule): void;
}

/**
* Properties for enabling Lambda utilization tracking
*/
export interface UtilizationScalingProps extends appscaling.BaseTargetTrackingProps {
/**
* Target utilization percentage for the attribute
*/
readonly targetUtilizationPercent: number;
}

/**
* A scalable lambda version attribute
*/
export class ScalableVersionAttribute extends appscaling.BaseScalableAttribute {
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
/**
* Scale out or in to keep utilization at a given level
*/
public scaleOnUtilization(props: UtilizationScalingProps) {
if (props.targetUtilizationPercent < 0 || props.targetUtilizationPercent > 1) {
throw new Error('target utilization is a percentage and must be between 0 and 1.');
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
}
super.doScaleToTrackMetric('Tracking', {
targetValue: props.targetUtilizationPercent,
predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION,
...props,
});
}

/**
* Scale out or in based on time
*/
public scaleOnSchedule(id: string, action: appscaling.ScalingSchedule) {
super.doScaleOnSchedule(id, action);
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
}
}

/**
* Properties for enabling Lambda capacity scaling
*/
export interface EnableScalingProps {
/**
* Minimum capacity to scale to
*/
readonly minCapacity: number;
/**
* Maximum capacity to scale to
*/
readonly maxCapacity: number;
}
7 changes: 6 additions & 1 deletion packages/@aws-cdk/aws-lambda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"sinon": "^9.0.2"
},
"dependencies": {
"@aws-cdk/aws-applicationautoscaling": "0.0.0",
"@aws-cdk/aws-cloudwatch": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-events": "0.0.0",
Expand All @@ -97,6 +98,7 @@
},
"homepage": "https://github.com/aws/aws-cdk",
"peerDependencies": {
"@aws-cdk/aws-applicationautoscaling": "0.0.0",
"@aws-cdk/aws-cloudwatch": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-events": "0.0.0",
Expand Down Expand Up @@ -165,7 +167,10 @@
"props-default-doc:@aws-cdk/aws-lambda.Permission.sourceArn",
"docs-public-apis:@aws-cdk/aws-lambda.ResourceBindOptions",
"docs-public-apis:@aws-cdk/aws-lambda.VersionAttributes",
"props-physical-name:@aws-cdk/aws-lambda.EventInvokeConfigProps"
"props-physical-name:@aws-cdk/aws-lambda.EventInvokeConfigProps",
"props-struct-name:@aws-cdk/aws-lambda.IScalableVersionAttribute",
"construct-ctor-props-type:@aws-cdk/aws-lambda.ScalableVersionAttribute",
"construct-interface-extends-iconstruct:@aws-cdk/aws-lambda.IScalableVersionAttribute"
]
},
"stability": "stable",
Expand Down