使用AWS CDK无法连接Lambda到SQL RDB的问题排查
问题:Lambda调用RDS创建数据表返回502 "Internal server error"
我用AWS CDK创建了一个Stack,其中包含一个需要连接同Stack内RDS实例的Lambda函数,这个函数用来创建数据表,但调用时返回502错误:"message": "Internal server error"。我已经通过PolicyStatement类给Lambda附加了IAM策略:s3ListPolicy允许Lambda列出所有S3存储桶,rdsReadPolicy允许readLambda和initCreateTab函数连接RDS实例,但怀疑权限不足或者还有其他配置问题。
Stack代码
import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as rds from 'aws-cdk-lib/aws-rds'; import * as cdk from 'aws-cdk-lib'; import { Function as LambdaFunction, Runtime } from 'aws-cdk-lib/aws-lambda'; import { join } from 'path'; import { LambdaIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; export class ReserveStack extends cdk.Stack { private api : RestApi = new RestApi(this, 'SpaceApi') constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); const vpc = new ec2.Vpc(this, 'cdk-vpc-rds', { cidr: '10.0.0.0/16', natGateways: 0, maxAzs: 3, subnetConfiguration: [ { name: 'public-subnet-1', subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24, }, { name: 'isolated-subnet-1', subnetType: ec2.SubnetType.PRIVATE_ISOLATED, cidrMask: 28, }, ], }); // create a security group for the EC2 instance const ec2InstanceSG = new ec2.SecurityGroup(this, 'ec2-instance-sg', { vpc, }); ec2InstanceSG.addIngressRule( ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'allow SSH connections from anywhere', ); // create RDS instance const dbInstance = new rds.DatabaseInstance(this, 'db-instance', { vpc, vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC, }, engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_26 }), instanceType: ec2.InstanceType.of( ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MICRO, ), credentials: rds.Credentials.fromGeneratedSecret('admin'), multiAz: false, allocatedStorage: 110, maxAllocatedStorage: 127, allowMajorVersionUpgrade: false, autoMinorVersionUpgrade: true, backupRetention: cdk.Duration.days(0), deleteAutomatedBackups: true, removalPolicy: cdk.RemovalPolicy.DESTROY, deletionProtection: false, databaseName: 'reservationsDb', publiclyAccessible: false, }); const initCreateTab = new NodejsFunction(this, 'ininCreateTab', { vpc, runtime: Runtime.NODEJS_18_X, entry: (join(__dirname, '..', 'services', 'crud-lambda', 'initCreateTab.ts')), handler: 'handler', }) // Lambda integrations const helloLambdaIntegration = new LambdaIntegration(helloLambdaNodeJs); const helloLambdaResource = this.api.root.addResource('hello'); helloLambdaResource.addMethod('GET', helloLambdaIntegration); const readLambdaIntegration = new LambdaIntegration(readLambda); const readLambdaResource = this.api.root.addResource('readLambda'); readLambdaResource.addMethod('GET', readLambdaIntegration); const initCreateTabIntegration = new LambdaIntegration(initCreateTab); const initCreateTabResource = this.api.root.addResource('createTable'); initCreateTabResource.addMethod('GET', initCreateTabIntegration); const s3ListPolicy = new PolicyStatement(); s3ListPolicy.addActions('s3:ListAllMyBuckets'); s3ListPolicy.addResources('*'); helloLambdaNodeJs.addToRolePolicy(s3ListPolicy); readLambda.addToRolePolicy(s3ListPolicy); const rdsReadPolicy = new PolicyStatement(); rdsReadPolicy.addActions('rds-db:connect'); rdsReadPolicy.addResources(dbInstance.secret!.secretArn); readLambda.addToRolePolicy(rdsReadPolicy); initCreateTab.addToRolePolicy(rdsReadPolicy); // dbInstance.connections.allowFrom(ec2Instance, ec2.Port.tcp(5432)); new cdk.CfnOutput(this, 'dbEndpoint', { value: dbInstance.instanceEndpoint.hostname, }); new cdk.CfnOutput(this, 'secretName', { // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain value: dbInstance.secret?.secretName!, }); } }
Lambda代码
import * as AWS from 'aws-sdk'; import { APIGatewayProxyHandler } from 'aws-lambda'; const rdsDataService = new AWS.RDSDataService(); export const handler: APIGatewayProxyHandler = async (event, context) => { const createTableQuery = ` CREATE TABLE Reservations ( id INT NOT NULL AUTO_INCREMENT, firstName VARCHAR(255), lastName VARCHAR(255), email VARCHAR(255), phone VARCHAR(255), PRIMARY KEY (id) ); `; const params = { resourceArn: 'arn:aws:rds:ca-central-1:201682123230:db:reservestack-dbinstance310a317f-gjixavyg2vyd', secretArn: 'arn:aws:secretsmanager:ca-central-1:201682123230:secret:ReserveStackdbinstanceSecre-ODm947ED1NFz-XPwT45', sql: createTableQuery, database: 'reservationsDb' }; try { const result = await rdsDataService.executeStatement(params).promise(); console.log(result); return { statusCode: 200, body: 'Table created successfully' }; } catch (err) { console.error(err); return { statusCode: 500, body: 'Error creating table' }; } };
排查与修复要点
IAM权限修正
rds-db:connect的资源格式错误,应该使用RDS实例的用户资源ARN而非Secrets Manager ARN,正确格式为arn:aws:rds-db:<region>:<account-id>:dbuser:<db-resource-id>/admin。更简单的方式是用CDK内置方法dbInstance.grantConnect(initCreateTab, 'admin'),自动生成正确的权限策略。- 缺少RDS Data API的执行权限,需添加
rds-data:ExecuteStatement动作到Lambda的IAM策略中。
安全组与网络配置
- RDS实例的安全组未允许Lambda的安全组访问MySQL端口(3306),添加规则:
dbInstance.connections.allowFrom(initCreateTab, ec2.Port.tcp(3306))(注意你用的是MySQL,不是PostgreSQL的5432端口)。 - 确认Lambda所在子网与RDS子网在同一VPC内,且路由配置允许私有网络内通信(因为RDS设置了
publiclyAccessible: false)。
- RDS实例的安全组未允许Lambda的安全组访问MySQL端口(3306),添加规则:
代码硬编码优化
- 将
resourceArn、secretArn等配置通过CDK传递环境变量给Lambda,避免硬编码:// CDK中配置Lambda环境变量 const initCreateTab = new NodejsFunction(this, 'ininCreateTab', { // ...其他配置 environment: { DB_RESOURCE_ARN: dbInstance.instanceArn, DB_SECRET_ARN: dbInstance.secret!.secretArn, DB_NAME: 'reservationsDb' } });// Lambda中读取环境变量 const params = { resourceArn: process.env.DB_RESOURCE_ARN!, secretArn: process.env.DB_SECRET_ARN!, sql: createTableQuery, database: process.env.DB_NAME! };
- 将
日志与API启用检查
- 查看Lambda的CloudWatch日志,获取具体错误信息(比如权限拒绝、连接超时等),这是定位问题的核心依据。
- 确认RDS实例已启用Data API,MySQL 8.0.26支持该API,但需确保实例配置中已开启。
内容的提问来源于stack exchange,提问作者track_p54skate
相关产品推荐
相关产品推荐

