From cc99717c67b598e0a96badd08722fccbd75c3a90 Mon Sep 17 00:00:00 2001 From: Nithin Chandran Rajashankar Date: Sun, 26 Apr 2026 18:55:24 +0000 Subject: [PATCH] feat(lambda): Add Lambda SnapStart with Bedrock CDK pattern Python 3.13 Lambda function with SnapStart enabled that invokes Amazon Bedrock Claude Sonnet for text generation. SnapStart reduces cold start latency by up to 10x via execution environment snapshots. - SnapStartConf.ON_PUBLISHED_VERSIONS with version + live alias - boto3 client initialized at module level for snapshot capture - Inference profile model ID with scoped IAM permissions - JSON structured logging with 2-week retention --- lambda-snapstart-bedrock-cdk/.gitignore | 6 ++ lambda-snapstart-bedrock-cdk/.npmignore | 3 + lambda-snapstart-bedrock-cdk/README.md | 87 +++++++++++++++++++ lambda-snapstart-bedrock-cdk/bin/app.ts | 7 ++ lambda-snapstart-bedrock-cdk/cdk.context.json | 7 ++ lambda-snapstart-bedrock-cdk/cdk.json | 3 + .../example-pattern.json | 52 +++++++++++ .../lib/lambda-snapstart-bedrock-stack.ts | 63 ++++++++++++++ lambda-snapstart-bedrock-cdk/package.json | 15 ++++ lambda-snapstart-bedrock-cdk/src/index.py | 33 +++++++ lambda-snapstart-bedrock-cdk/tsconfig.json | 22 +++++ 11 files changed, 298 insertions(+) create mode 100644 lambda-snapstart-bedrock-cdk/.gitignore create mode 100644 lambda-snapstart-bedrock-cdk/.npmignore create mode 100644 lambda-snapstart-bedrock-cdk/README.md create mode 100644 lambda-snapstart-bedrock-cdk/bin/app.ts create mode 100644 lambda-snapstart-bedrock-cdk/cdk.context.json create mode 100644 lambda-snapstart-bedrock-cdk/cdk.json create mode 100644 lambda-snapstart-bedrock-cdk/example-pattern.json create mode 100644 lambda-snapstart-bedrock-cdk/lib/lambda-snapstart-bedrock-stack.ts create mode 100644 lambda-snapstart-bedrock-cdk/package.json create mode 100644 lambda-snapstart-bedrock-cdk/src/index.py create mode 100644 lambda-snapstart-bedrock-cdk/tsconfig.json diff --git a/lambda-snapstart-bedrock-cdk/.gitignore b/lambda-snapstart-bedrock-cdk/.gitignore new file mode 100644 index 000000000..7bddfb4c1 --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/.gitignore @@ -0,0 +1,6 @@ +*.js +!src/*.js +*.d.ts +node_modules +cdk.out +package-lock.json diff --git a/lambda-snapstart-bedrock-cdk/.npmignore b/lambda-snapstart-bedrock-cdk/.npmignore new file mode 100644 index 000000000..72b28b5da --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/.npmignore @@ -0,0 +1,3 @@ +*.ts +!*.d.ts +cdk.out diff --git a/lambda-snapstart-bedrock-cdk/README.md b/lambda-snapstart-bedrock-cdk/README.md new file mode 100644 index 000000000..5ce1e974f --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/README.md @@ -0,0 +1,87 @@ +# Lambda SnapStart with Amazon Bedrock + +This pattern deploys a Python Lambda function with **SnapStart** enabled that invokes Amazon Bedrock (Claude Sonnet) for text generation, using AWS CDK. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/lambda-snapstart-bedrock-cdk + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +- [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +- [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) installed +- [Node.js](https://nodejs.org/) 18.x or later +- [Amazon Bedrock model access](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html) enabled for Anthropic Claude Sonnet in your AWS account + +## How it works + +Lambda SnapStart reduces cold start latency by up to 10x by pre-initializing the execution environment and caching a snapshot of the initialized state. When a new execution environment is needed, Lambda restores from the cached snapshot instead of running full initialization. + +**Key points:** +- SnapStart is enabled via `snapStart: lambda.SnapStartConf.ON_PUBLISHED_VERSIONS` in CDK +- SnapStart only applies to **published versions**, not `$LATEST` — the pattern creates a version and alias automatically +- Supported runtimes: Java 11/17/21, Python 3.12/3.13, .NET 8 +- The Bedrock client is initialized at module level (outside the handler) so it gets captured in the SnapStart snapshot + +``` +┌──────────────────────┐ ┌──────────────────────┐ +│ │ │ │ +│ Lambda (SnapStart) │────────▶│ Amazon Bedrock │ +│ Python 3.13 │ │ Claude Sonnet │ +│ │◀────────│ │ +└──────────────────────┘ └──────────────────────┘ +``` + +## Deployment + +1. Install dependencies: + ```bash + cd lambda-snapstart-bedrock-cdk + npm install + ``` + +2. Deploy the stack: + ```bash + cdk deploy + ``` + +## Testing + +Invoke the function via the **alias** (SnapStart only applies to published versions): + +```bash +aws lambda invoke \ + --function-name snapstart-bedrock-cdk:live \ + --payload '{"prompt": "Explain Lambda SnapStart in one paragraph"}' \ + --cli-binary-format raw-in-base64-out \ + output.json + +cat output.json | python3 -m json.tool +``` + +To compare cold start times, invoke `$LATEST` (no SnapStart) vs the alias (with SnapStart): + +```bash +# Without SnapStart ($LATEST) +aws lambda invoke --function-name snapstart-bedrock-cdk \ + --payload '{"prompt": "Hello"}' --cli-binary-format raw-in-base64-out /dev/null + +# With SnapStart (alias) +aws lambda invoke --function-name snapstart-bedrock-cdk:live \ + --payload '{"prompt": "Hello"}' --cli-binary-format raw-in-base64-out /dev/null +``` + +Check the `Init Duration` in CloudWatch Logs — SnapStart-optimized invocations show `Restore Duration` instead, which is significantly lower. + +## Cleanup + +```bash +cdk destroy +``` + +--- + +Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/lambda-snapstart-bedrock-cdk/bin/app.ts b/lambda-snapstart-bedrock-cdk/bin/app.ts new file mode 100644 index 000000000..e562c1928 --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/bin/app.ts @@ -0,0 +1,7 @@ +#!/usr/bin/env node +import "source-map-support/register"; +import * as cdk from "aws-cdk-lib"; +import { LambdaSnapstartBedrockStack } from "../lib/lambda-snapstart-bedrock-stack"; + +const app = new cdk.App(); +new LambdaSnapstartBedrockStack(app, "LambdaSnapstartBedrockStack"); diff --git a/lambda-snapstart-bedrock-cdk/cdk.context.json b/lambda-snapstart-bedrock-cdk/cdk.context.json new file mode 100644 index 000000000..a4f31f660 --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/cdk.context.json @@ -0,0 +1,7 @@ +{ + "acknowledged-issue-numbers": [ + 33623, + 34635, + 34892 + ] +} diff --git a/lambda-snapstart-bedrock-cdk/cdk.json b/lambda-snapstart-bedrock-cdk/cdk.json new file mode 100644 index 000000000..27fe6d2ec --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "npx ts-node bin/app.ts" +} diff --git a/lambda-snapstart-bedrock-cdk/example-pattern.json b/lambda-snapstart-bedrock-cdk/example-pattern.json new file mode 100644 index 000000000..7e842d550 --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/example-pattern.json @@ -0,0 +1,52 @@ +{ + "title": "Lambda SnapStart with Amazon Bedrock", + "description": "Invoke Amazon Bedrock from a Lambda function with SnapStart enabled for dramatically reduced cold start latency, deployed with AWS CDK.", + "language": "Python", + "level": "200", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern deploys a Python Lambda function with SnapStart enabled that invokes Amazon Bedrock (Claude Sonnet) for text generation.", + "Lambda SnapStart reduces cold start latency by up to 10x by pre-initializing the execution environment and caching a snapshot of the initialized state.", + "When a new execution environment is needed, Lambda restores from the cached snapshot instead of running full initialization, providing near-instant cold starts.", + "A published version and alias are created to activate SnapStart optimization — SnapStart only applies to published versions, not $LATEST." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-snapstart-bedrock-cdk", + "templateURL": "serverless-patterns/lambda-snapstart-bedrock-cdk", + "projectFolder": "lambda-snapstart-bedrock-cdk", + "templateFile": "lib/lambda-snapstart-bedrock-stack.ts" + } + }, + "resources": { + "bullets": [ + { "text": "Lambda SnapStart documentation", "link": "https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html" }, + { "text": "Lambda SnapStart for Python and .NET", "link": "https://aws.amazon.com/blogs/aws/reduce-cold-starts-for-your-lambda-functions-with-snapstart-for-python-and-net/" }, + { "text": "Amazon Bedrock documentation", "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/" } + ] + }, + "deploy": { + "text": ["cdk deploy"] + }, + "testing": { + "text": ["See the README for testing instructions."] + }, + "cleanup": { + "text": ["cdk destroy"] + }, + "authors": [ + { + "name": "Nithin Chandran R", + "bio": "Technical Account Manager at AWS", + "linkedin": "nithin-chandran-r" + } + ], + "patternArch": { + "icon1": { "x": 20, "y": 50, "service": "lambda", "label": "Lambda (SnapStart)" }, + "icon2": { "x": 80, "y": 50, "service": "bedrock", "label": "Amazon Bedrock" }, + "line1": { "from": "icon1", "to": "icon2" } + } +} diff --git a/lambda-snapstart-bedrock-cdk/lib/lambda-snapstart-bedrock-stack.ts b/lambda-snapstart-bedrock-cdk/lib/lambda-snapstart-bedrock-stack.ts new file mode 100644 index 000000000..8b1c53230 --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/lib/lambda-snapstart-bedrock-stack.ts @@ -0,0 +1,63 @@ +import * as cdk from "aws-cdk-lib"; +import * as lambda from "aws-cdk-lib/aws-lambda"; +import * as iam from "aws-cdk-lib/aws-iam"; +import * as logs from "aws-cdk-lib/aws-logs"; +import { Construct } from "constructs"; + +export class LambdaSnapstartBedrockStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const modelId = new cdk.CfnParameter(this, "BedrockModelId", { + type: "String", + default: "us.anthropic.claude-sonnet-4-20250514-v1:0", + description: "Bedrock inference profile model ID", + }); + + const functionName = "snapstart-bedrock-cdk"; + + const logGroup = new logs.LogGroup(this, "LogGroup", { + logGroupName: `/aws/lambda/${functionName}`, + retention: logs.RetentionDays.TWO_WEEKS, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + const fn = new lambda.Function(this, "BedrockFn", { + runtime: lambda.Runtime.PYTHON_3_13, + handler: "index.handler", + code: lambda.Code.fromAsset("src"), + timeout: cdk.Duration.seconds(30), + memorySize: 512, + functionName, + description: "Bedrock invocation with SnapStart for reduced cold starts", + loggingFormat: lambda.LoggingFormat.JSON, + logGroup, + snapStart: lambda.SnapStartConf.ON_PUBLISHED_VERSIONS, + environment: { MODEL_ID: modelId.valueAsString }, + }); + + fn.addToRolePolicy( + new iam.PolicyStatement({ + actions: ["bedrock:InvokeModel"], + resources: [ + `arn:aws:bedrock:${this.region}:${this.account}:inference-profile/${modelId.valueAsString}`, + "arn:aws:bedrock:*::foundation-model/*", + ], + }) + ); + + // Publish a version to activate SnapStart + const version = fn.currentVersion; + + // Alias pointing to the SnapStart-optimized version + const alias = new lambda.Alias(this, "LiveAlias", { + aliasName: "live", + version, + }); + + new cdk.CfnOutput(this, "FunctionName", { value: fn.functionName }); + new cdk.CfnOutput(this, "FunctionArn", { value: fn.functionArn }); + new cdk.CfnOutput(this, "AliasArn", { value: alias.functionArn }); + new cdk.CfnOutput(this, "LogGroupName", { value: logGroup.logGroupName }); + } +} diff --git a/lambda-snapstart-bedrock-cdk/package.json b/lambda-snapstart-bedrock-cdk/package.json new file mode 100644 index 000000000..6b9edec19 --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/package.json @@ -0,0 +1,15 @@ +{ + "name": "lambda-snapstart-bedrock-cdk", + "version": "1.0.0", + "bin": { "app": "bin/app.ts" }, + "scripts": { "build": "tsc", "cdk": "cdk" }, + "dependencies": { + "aws-cdk-lib": "2.236.0", + "constructs": "10.4.2" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "ts-node": "^10.9.0", + "typescript": "~5.7.0" + } +} diff --git a/lambda-snapstart-bedrock-cdk/src/index.py b/lambda-snapstart-bedrock-cdk/src/index.py new file mode 100644 index 000000000..7805141fc --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/src/index.py @@ -0,0 +1,33 @@ +import json +import os +import boto3 + +bedrock = boto3.client("bedrock-runtime") +MODEL_ID = os.environ["MODEL_ID"] + + +def handler(event, context): + prompt = event.get("prompt", "What are the benefits of Lambda SnapStart?") + + response = bedrock.invoke_model( + modelId=MODEL_ID, + contentType="application/json", + accept="application/json", + body=json.dumps({ + "anthropic_version": "bedrock-2023-05-31", + "max_tokens": 1024, + "messages": [{"role": "user", "content": prompt}], + }), + ) + + body = json.loads(response["body"].read()) + + return { + "statusCode": 200, + "body": json.dumps({ + "prompt": prompt, + "response": body["content"][0]["text"], + "model": MODEL_ID, + "usage": body["usage"], + }), + } diff --git a/lambda-snapstart-bedrock-cdk/tsconfig.json b/lambda-snapstart-bedrock-cdk/tsconfig.json new file mode 100644 index 000000000..5a9a7d6dd --- /dev/null +++ b/lambda-snapstart-bedrock-cdk/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["es2020"], + "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 + }, + "exclude": ["cdk.out"] +}