AWS CDK Lambda版本管理问题:保留旧版本保障Step Functions运行
CDK部署Lambda+Step Functions:旧版本调用权限与版本保留问题
需求背景
- 通过AWS CDK部署包含Lambda函数和Step Functions的云栈
- 执行
cdk deploy推送新版本时,正在运行的Step Functions执行流必须能继续调用部署时对应的旧版本Lambda - 部分Lambda需要启用预置并发,因此必须通过
Alias或Version实现版本管理
现有实现代码
PythonFunction类定义
import * as python from "@aws-cdk/aws-lambda-python-alpha"; import * as cdk from "aws-cdk-lib"; import { DockerImage, Duration } from "aws-cdk-lib"; import { PolicyStatement } from "aws-cdk-lib/aws-iam"; import * as lambda from "aws-cdk-lib/aws-lambda"; import { execSync } from "child_process"; import { Construct } from "constructs"; import { artifactoryIndexUrl } from "../../../utils/jfrog"; import { FunctionVersionProps } from "../common/function-version"; export interface PythonFunctionProps extends python.PythonFunctionProps {} export class PythonFunction extends python.PythonFunction { constructor(scope: Construct, id: string, props: PythonFunctionProps) { const stack = cdk.Stack.of(scope); const pipCacheDir = execSync("pip cache dir").toString().trim(); const poetryCacheDir = execSync("poetry config cache-dir").toString().trim(); super(scope, id, { timeout: Duration.seconds(30), tracing: lambda.Tracing.ACTIVE, bundling: { image: DockerImage.fromBuild(__dirname, { buildArgs: { IMAGE: props.runtime.bundlingImage.image, }, }), commandHooks: { beforeBundling(inputDir: string, outputDir: string): string[] { return [`pip config set global.index-url ${artifactoryIndexUrl}`]; }, afterBundling(inputDir: string, outputDir: string): string[] { return []; }, }, assetExcludes: ["**/__pycache__", "**/*.pyc", "**/*.pyo", "**/.pytest_cache", "**/tests", "README.md"], assetHashType: cdk.AssetHashType.SOURCE, volumes: [ { hostPath: pipCacheDir, containerPath: "/tmp/pip-cache", }, { hostPath: poetryCacheDir, containerPath: "/tmp/poetry-cache", }, ], }, ...props, }); this.addToRolePolicy( new PolicyStatement({ actions: ["ssm:GetParameter"], resources: [`arn:${stack.partition}:ssm:${stack.region}:${stack.account}:parameter/vita-*`], }) ); } version(props?: FunctionVersionProps): lambda.IFunction { const id = this.node.id; const version = this.currentVersion; const aliasName = `v${version.version}`; return new lambda.Alias(this, `${id}Alias`, { aliasName, version, ...(props?.provisionedConcurrency && { provisionedConcurrentExecutions: props?.provisionedConcurrency.value }), }); } }
FunctionVersionProps接口
export interface FunctionVersionProps { provisionedConcurrency?: { value: number; }; }
Step Functions中调用Lambda版本
prepareSegmentsFn: prepareSegmentsFn.version({ provisionedConcurrency: { value: 1, }, }),
问题现象
- 使用
.currentVersion时,每次cdk deploy会生成新版本(版本号递增),同时CDK会自动删除旧版本 - 正在运行的Step Functions执行流调用旧版本Lambda时,出现403权限错误:
{
"Error": "Lambda.AWSLambdaException",
"Cause": "User: arn:aws:sts:::assumed-role/Stack--/ is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:eu-central-1:***:function:Stack-SharedDeleteModelFn659FE70B-2IgdOOYDRn78:v34 because no identity-based policy allows the lambda:InvokeFunction action (Service: Lambda, Status Code: 403, Request ID: ***)"
}
解决方案
1. 保留旧Lambda版本
修改version方法,显式创建Lambda版本并设置保留策略,避免CDK删除旧版本:
version(props?: FunctionVersionProps): lambda.IFunction { const id = this.node.id; // 显式创建版本,设置RemovalPolicy.RETAIN防止旧版本被销毁 const version = new lambda.Version(this, `${id}Version`, { lambda: this, removalPolicy: cdk.RemovalPolicy.RETAIN, }); const aliasName = `v${version.version}`; const alias = new lambda.Alias(this, `${id}Alias`, { aliasName, version, ...(props?.provisionedConcurrency && { provisionedConcurrentExecutions: props.provisionedConcurrency.value }), }); return alias; }
2. 扩展Step Functions执行角色的权限
确保Step Functions的执行角色拥有调用目标Lambda所有版本/别名的权限,而不是仅当前版本:
// 假设stateMachineRole是Step Functions的执行角色 stateMachineRole.addToPolicy(new PolicyStatement({ actions: ["lambda:InvokeFunction"], resources: [ // 匹配目标Lambda的所有版本和别名 `${prepareSegmentsFn.functionArn}:*`, // 如有其他Lambda,添加对应的ARN模式 `${anotherLambdaFn.functionArn}:*` ] }));
3. 确保Step Functions任务绑定到版本/别名
在定义Step Functions的Lambda调用任务时,直接使用创建的Alias/Version实例,确保部署时生成对应版本的引用:
import * as tasks from "aws-cdk-lib/aws-stepfunctions-tasks"; // 创建调用Lambda的Step Functions任务 new tasks.LambdaInvoke(this, "PrepareSegmentsTask", { lambdaFunction: prepareSegmentsFn.version({ provisionedConcurrency: { value: 1 } }), // 其他任务配置(如输入输出处理) });
内容的提问来源于stack exchange,提问作者Or Shemtov
相关产品推荐
相关产品推荐

