You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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)。
  • 代码硬编码优化

    • 将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 06:12:45