diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/.gitignore b/typescript/lambda-provisioned-concurrency-autoscaling/.gitignore new file mode 100644 index 000000000..f60797b6a --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/.npmignore b/typescript/lambda-provisioned-concurrency-autoscaling/.npmignore new file mode 100644 index 000000000..c1d6d45dc --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/README.md b/typescript/lambda-provisioned-concurrency-autoscaling/README.md new file mode 100644 index 000000000..834213668 --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/README.md @@ -0,0 +1,68 @@ + +# Provisioned Concurrency AutoScaling for Lambda Function + +This example creates Provisioned Concurrency on lambda function and enable autoscaling(target and schedule) configuration with Provisioned Concurrency. + + +--- + +## Setup + +Clone this repository: +```bash +git clone https://github.com/aws-samples/aws-cdk-examples.git +``` + +Change directory: +```bash +cd aws-cdk-examples/typescript/lambda-provisioned-concurrency-autoscaling +``` + +To build this app, you need to be in this example's root folder. Then run the following: +```bash +npm install -g aws-cdk +npm install +npm run build +``` + +This will install the necessary CDK, then this example's dependencies, and then build your TypeScript files and your CloudFormation template. + + +## Deployment + +This stack uses assets, so the toolkit stack must be deployed to the environment. This can be done by running the following command: +```bash +cdk bootstrap aws://account-id/aws-region +``` + +At this point you can now synthesize the CloudFormation template for this code: +```bash +cdk synth +``` +then check the output file in the "cdk.out" directory. + +Run `cdk deploy`. This will deploy / redeploy your Stack to your AWS Account. + + + +## Test + +```bash +npm run test +``` + +## Clean Up +To clean up, issue this command: +``` +cdk destroy +``` + +## Useful commands + + * `cdk ls` list all stacks in the app + * `cdk synth` emits the synthesized CloudFormation template + * `cdk deploy` deploy this stack to your default AWS account/region + * `cdk diff` compare deployed stack with current state + * `cdk docs` open CDK documentation + +Enjoy! \ No newline at end of file diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/bin/lambda-pc-autoscaling.ts b/typescript/lambda-provisioned-concurrency-autoscaling/bin/lambda-pc-autoscaling.ts new file mode 100644 index 000000000..a3c777057 --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/bin/lambda-pc-autoscaling.ts @@ -0,0 +1,15 @@ +#!/usr/bin/env node +import * as cdk from 'aws-cdk-lib'; +import { LambdaPCScalingTargetStack } from '../lib/lambda-pc-autoscaling-target'; +import { LambdaPCScalingScheduleStack } from '../lib/lambda-pc-autoscaling-schedule'; + +export const lambdaFunctionName = "LambdaPCAutoScalingExample" + +const app = new cdk.App(); +new LambdaPCScalingTargetStack(app, 'LambdaProvisionedConcurrencyScalingTarget', { + functionName: `${lambdaFunctionName}Target`, +}); + +new LambdaPCScalingScheduleStack(app, 'LambdaProvisionedConcurrencyScalingSchedule', { + functionName: `${lambdaFunctionName}Schedule`, +}); diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/cdk.json b/typescript/lambda-provisioned-concurrency-autoscaling/cdk.json new file mode 100644 index 000000000..9523f92e5 --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/lambda-pc-autoscaling.ts" +} diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/jest.config.js b/typescript/lambda-provisioned-concurrency-autoscaling/jest.config.js new file mode 100644 index 000000000..2d329fb14 --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/jest.config.js @@ -0,0 +1,5 @@ +const config = { + moduleFileExtensions: ["js"] +}; + +module.exports = config; \ No newline at end of file diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/lambda/lambda-handler.ts b/typescript/lambda-provisioned-concurrency-autoscaling/lambda/lambda-handler.ts new file mode 100644 index 000000000..77d7b2ce4 --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/lambda/lambda-handler.ts @@ -0,0 +1,6 @@ +exports.handler = async () => { + return { + statusCode: 200, + body: JSON.stringify({ message: 'Hello, World!' }), + }; + }; \ No newline at end of file diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/lib/lambda-pc-autoscaling-schedule.ts b/typescript/lambda-provisioned-concurrency-autoscaling/lib/lambda-pc-autoscaling-schedule.ts new file mode 100644 index 000000000..0a4ea4422 --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/lib/lambda-pc-autoscaling-schedule.ts @@ -0,0 +1,59 @@ +import { Duration, Stack, StackProps} from "aws-cdk-lib"; +import { Function, Runtime } from "aws-cdk-lib/aws-lambda"; +import { Construct } from 'constructs'; + +import { ScalableTarget, Schedule, ServiceNamespace } from 'aws-cdk-lib/aws-applicationautoscaling'; +import { Metric }from 'aws-cdk-lib/aws-cloudwatch'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Alias } from 'aws-cdk-lib/aws-lambda'; + + + +interface LambdaPCScalingScheduleStackProps extends StackProps { + functionName: string +} + +export class LambdaPCScalingScheduleStack extends Stack { + private lambdaFunction: Function + private lambdaFunctionName: string + + constructor(scope: Construct, id: string, props: LambdaPCScalingScheduleStackProps) { + super(scope, id, props); + this.lambdaFunctionName = props.functionName + + // Create Sample Lambda Function which will create metrics + this.lambdaFunction = new NodejsFunction(this, 'LambdaFunction', { + functionName: this.lambdaFunctionName, + entry: `./lambda/lambda-handler.ts`, + runtime: Runtime.NODEJS_18_X, + memorySize: 512, + timeout: Duration.seconds(6), + }); + // Enable Provisioned Concurrency + new Alias(this, `LambdaFunctionAlias`, { + aliasName: 'provisioned', + version: this.lambdaFunction.currentVersion, + provisionedConcurrentExecutions: 1, + }); + // Enable AutoScaling Scalable Schedule + const asg = new ScalableTarget(this, `${this.lambdaFunctionName}ScalableSchedule`, { + serviceNamespace: ServiceNamespace.LAMBDA, + maxCapacity: 1, + minCapacity: 0, + resourceId: `function:${this.lambdaFunctionName}:provisioned`, + scalableDimension: 'lambda:function:ProvisionedConcurrency', + }) + // Scaling out every weekday (Monday through Friday) at 11:00 AM(UTC+0), + asg.scaleOnSchedule(`${this.lambdaFunctionName}ScheduleScaleOut`, { + schedule: Schedule.expression('cron(0 11 ? * MON-FRI *))'), + minCapacity: 1, + maxCapacity: 1 + }) + // Scaling in every weekday (Monday through Friday) at 12:00 AM(UTC+0), + asg.scaleOnSchedule(`${this.lambdaFunctionName}ScheduleScaleIn`, { + schedule: Schedule.expression('cron(0 12 ? * MON-FRI *))'), + minCapacity: 0, + maxCapacity: 0 + }) + }; +} diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/lib/lambda-pc-autoscaling-target.ts b/typescript/lambda-provisioned-concurrency-autoscaling/lib/lambda-pc-autoscaling-target.ts new file mode 100644 index 000000000..ffe01507c --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/lib/lambda-pc-autoscaling-target.ts @@ -0,0 +1,62 @@ +import { Duration, Stack, StackProps } from "aws-cdk-lib"; +import { Function, Runtime } from "aws-cdk-lib/aws-lambda"; +import { Construct } from 'constructs'; + +import { ScalableTarget, ServiceNamespace } from 'aws-cdk-lib/aws-applicationautoscaling'; +import { Metric }from 'aws-cdk-lib/aws-cloudwatch'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Alias } from 'aws-cdk-lib/aws-lambda'; + + + + +interface LambdaPCScalingTargetStackProps extends StackProps { + functionName: string +} + +export class LambdaPCScalingTargetStack extends Stack { + private lambdaFunction: Function + private lambdaFunctionName: string + + constructor(scope: Construct, id: string, props: LambdaPCScalingTargetStackProps) { + super(scope, id, props); + this.lambdaFunctionName = props.functionName + + // Create Sample Lambda Function which will create metrics + this.lambdaFunction = new NodejsFunction(this, 'LambdaFunction', { + functionName: this.lambdaFunctionName, + entry: `./lambda/lambda-handler.ts`, + runtime: Runtime.NODEJS_18_X, + memorySize: 512, + timeout: Duration.seconds(6), + }); + // Enable Provisioned Concurrency + new Alias(this, `LambdaFunctionAlias`, { + aliasName: 'provisioned', + version: this.lambdaFunction.currentVersion, + provisionedConcurrentExecutions: 1, + }); + // Create Metric of Lambda Provisioned Concurrency + const metrics = new Metric({ + metricName: 'ProvisionedConcurrencyUtilization', + namespace: 'AWS/Lambda', + dimensionsMap: { + FunctionName: this.lambdaFunction.functionName, + Resource: `${this.lambdaFunction.functionName}:'provisioned`, + }, + statistic: 'Maximum', + period: Duration.minutes(5), + }); + // Enable AutoScaling Scalable Target when over target threshold(0.8) + new ScalableTarget(this, `${this.lambdaFunctionName}ScalableTarget`, { + serviceNamespace: ServiceNamespace.LAMBDA, + maxCapacity: 2, + minCapacity: 1, + resourceId: `function:${this.lambdaFunctionName}:provisioned`, + scalableDimension: 'lambda:function:ProvisionedConcurrency', + }).scaleToTrackMetric(`${this.lambdaFunctionName}PCScaleTracking`, { + targetValue: 0.8, + customMetric: metrics, + }); + }; +} diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/package.json b/typescript/lambda-provisioned-concurrency-autoscaling/package.json new file mode 100644 index 000000000..0d17261ca --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/package.json @@ -0,0 +1,32 @@ +{ + "name": "lambda-provision-concurrenced-autoscaling", + "version": "0.1.0", + "bin": { + "lambda-pc-autoscaling": "bin/lambda-pc-autoscaling.js" + }, + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "cdk": "cdk", + "test": "jest --config=jest.config.js" + }, + "devDependencies": { + "@aws-cdk/assert": "*", + "@types/node": "10.17.27", + "aws-cdk": "*", + "ts-node": "^10.9.1", + "@types/jest": "^26.0.24", + "jest": "^26.6.3", + "typescript": "~5.1.6" + }, + "dependencies": { + "aws-cdk-lib": "^2.0.0", + "constructs": "^10.0.0" + + } +} diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/test/stack.test.ts b/typescript/lambda-provisioned-concurrency-autoscaling/test/stack.test.ts new file mode 100644 index 000000000..42884d499 --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/test/stack.test.ts @@ -0,0 +1,31 @@ +import { App } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { LambdaPCScalingTargetStack } from '../lib/lambda-pc-autoscaling-target'; +import { LambdaPCScalingScheduleStack } from '../lib/lambda-pc-autoscaling-schedule'; + +describe('CDK Stack Testing', () => { + test('Lambda Function PC Scaling Target Testing', () => { + const app = new App(); + // WHEN + const LambdaPCScalingTargetstack = new LambdaPCScalingTargetStack(app, 'LambdaPCScalingTargetStackTest', { + functionName: `LambdaPCScalingTargetStackTesting`, + } + ); + const targetAssert = Template.fromStack(LambdaPCScalingTargetstack); + // THEN + targetAssert.resourceCountIs('AWS::Lambda::Function', 1); + }); + + test('Lambda Function PC Scaling Schedule Testing', () => { + const app = new App(); + // WHEN + const LambdaPCScalingSchedulestack = new LambdaPCScalingScheduleStack(app, 'LambdaPCScalingScheduleStackTest', { + functionName: `LambdaPCScalingScheduleStackTesting`, + } + ); + // THEN + const scheduleAssert = Template.fromStack(LambdaPCScalingSchedulestack); + scheduleAssert.resourceCountIs('AWS::Lambda::Function', 1); + }); +}); + diff --git a/typescript/lambda-provisioned-concurrency-autoscaling/tsconfig.json b/typescript/lambda-provisioned-concurrency-autoscaling/tsconfig.json new file mode 100644 index 000000000..ec75123ce --- /dev/null +++ b/typescript/lambda-provisioned-concurrency-autoscaling/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "lib": ["es2018"], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": ["./node_modules/@types"] + }, + "exclude": ["cdk.out"] +}