如何使用AWS CDK-Typescript创建MySQL数据库及数据表
使用AWS CDK TypeScript创建MySQL数据库及数据表
1. 初始化项目与安装依赖
初始化CDK TypeScript项目,安装所需AWS服务依赖包:
cdk init app --language typescript npm install @aws-cdk/aws-rds @aws-cdk/aws-ec2 @aws-cdk/aws-lambda @aws-cdk/aws-secretsmanager @aws-cdk/custom-resources
2. 创建RDS MySQL实例
在lib/<你的栈名称>.ts中编写代码,完成VPC、RDS实例及数据库凭证的创建:
import * as cdk from 'aws-cdk-lib'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as rds from 'aws-cdk-lib/aws-rds'; import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'; export class MySqlStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // 创建VPC(RDS必须部署在VPC内) const vpc = new ec2.Vpc(this, 'MyVpc', { maxAzs: 2, subnetConfiguration: [ { name: 'private-subnet', subnetType: ec2.SubnetType.PRIVATE_ISOLATED, }, { name: 'public-subnet', subnetType: ec2.SubnetType.PUBLIC, }, ], }); // 创建数据库凭证(存储在Secrets Manager) const dbSecret = new secretsmanager.Secret(this, 'DbSecret', { secretName: 'mysql-db-credentials', generateSecretString: { secretStringTemplate: JSON.stringify({ username: 'admin' }), generateStringKey: 'password', excludePunctuation: true, passwordLength: 16, }, }); // 创建RDS MySQL实例 const dbInstance = new rds.DatabaseInstance(this, 'MySqlInstance', { engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_33, }), instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO), vpc: vpc, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED, }, credentials: rds.Credentials.fromSecret(dbSecret), databaseName: 'mydb', publiclyAccessible: false, enablePerformanceInsights: false, deletionProtection: false, // 测试环境可关闭,生产环境建议开启 }); } }
3. 通过自定义资源创建数据表
CDK本身不直接支持建表操作,需通过自定义资源触发Lambda函数连接数据库执行SQL:
3.1 编写Lambda建表代码
- 创建lambda目录并初始化依赖:
mkdir lambda && cd lambda npm init -y npm install mysql2 @aws-sdk/client-secrets-manager
- 创建
create-table.ts文件,写入以下代码:
import * as mysql from 'mysql2/promise'; import * as secretsmanager from '@aws-sdk/client-secrets-manager'; const secretsClient = new secretsmanager.SecretsManagerClient({}); export const handler = async (event: any) => { // 仅在资源创建/更新时执行 if (event.RequestType !== 'Create' && event.RequestType !== 'Update') { return sendResponse(event, 'SUCCESS', 'No action needed'); } try { // 从Secrets Manager获取数据库凭证 const secretResponse = await secretsClient.send( new secretsmanager.GetSecretValueCommand({ SecretId: event.ResourceProperties.SecretArn, }) ); const secret = JSON.parse(secretResponse.SecretString!); // 连接数据库 const connection = await mysql.createConnection({ host: event.ResourceProperties.DbEndpoint, user: secret.username, password: secret.password, database: event.ResourceProperties.DbName, connectTimeout: 10000, }); // 执行建表SQL(示例创建users表) await connection.execute(` CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `); await connection.end(); return sendResponse(event, 'SUCCESS', 'Table created successfully'); } catch (error) { console.error('Error creating table:', error); return sendResponse(event, 'FAILED', (error as Error).message); } }; // 向CDK发送自定义资源响应 const sendResponse = async (event: any, status: string, message: string) => { const https = require('https'); const url = require('url'); const parsedUrl = url.parse(event.ResponseURL); const options = { hostname: parsedUrl.hostname, port: 443, path: parsedUrl.path, method: 'PUT', headers: { 'Content-Type': '', 'Content-Length': Buffer.byteLength(JSON.stringify({ Status: status, Reason: message, PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId, StackId: event.StackId, RequestId: event.RequestId, LogicalResourceId: event.LogicalResourceId, Data: { Message: message } })), }, }; return new Promise((resolve, reject) => { const req = https.request(options, (res: any) => { resolve('Success'); }); req.on('error', (err: any) => { reject(err); }); req.write(JSON.stringify({ Status: status, Reason: message, PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId, StackId: event.StackId, RequestId: event.RequestId, LogicalResourceId: event.LogicalResourceId, Data: { Message: message } })); req.end(); }); };
3.2 在CDK栈中配置自定义资源
回到lib/<你的栈名称>.ts,添加Lambda和自定义资源的配置代码:
import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as cr from 'aws-cdk-lib/custom-resources'; // ... 保留之前的VPC、RDS实例代码 ... // 创建Lambda函数,用于执行建表SQL const createTableLambda = new lambda.Function(this, 'CreateTableLambda', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'create-table.handler', code: lambda.Code.fromAsset('lambda'), vpc: vpc, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED }, environment: { NODE_ENV: 'production', }, }); // 授予Lambda访问Secrets Manager的权限 dbSecret.grantRead(createTableLambda); // 允许Lambda访问RDS实例的3306端口 dbInstance.connections.allowFrom(createTableLambda, ec2.Port.tcp(3306)); // 创建自定义资源,触发Lambda执行建表 new cr.AwsCustomResource(this, 'CreateTableResource', { onCreate: { service: 'Lambda', action: 'invoke', parameters: { FunctionName: createTableLambda.functionName, Payload: JSON.stringify({ RequestType: 'Create', ResourceProperties: { SecretArn: dbSecret.secretArn, DbEndpoint: dbInstance.dbInstanceEndpointAddress, DbName: 'mydb', }, }), }, physicalResourceId: cr.PhysicalResourceId.of(`CreateTable-${Date.now()}`), }, onUpdate: { service: 'Lambda', action: 'invoke', parameters: { FunctionName: createTableLambda.functionName, Payload: JSON.stringify({ RequestType: 'Update', ResourceProperties: { SecretArn: dbSecret.secretArn, DbEndpoint: dbInstance.dbInstanceEndpointAddress, DbName: 'mydb', }, }), }, }, policy: cr.AwsCustomResourcePolicy.fromSdkCalls({ resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE, }), });
4. 部署与验证
执行部署命令:
cdk deploy
部署完成后,可通过Secrets Manager获取数据库凭证,使用MySQL客户端连接实例,验证users表是否存在。
注意事项
- Lambda需部署在与RDS同VPC的私有子网中,确保网络连通性
- 建表SQL中使用
IF NOT EXISTS避免重复创建报错 - 生产环境建议开启RDS删除保护,调整实例类型和VPC配置
- 可扩展Lambda代码支持多表创建或数据初始化
内容的提问来源于stack exchange,提问作者Ammu Amulya
相关产品推荐
相关产品推荐

