英文:
Solved: MySQL PROTOCOL_CONNECTION_LOST Error with AWS CDK CustomResource, Lambda
问题
我正在使用AWS CDK来定义AWS基础架构。
我有一个名为database-stack
的堆栈,定义了RDS(MySQL)实例,并设置了AWSCustomResource,如下所示,以在第一次部署数据库实例时运行table-creation.sql
。
每次启动实例时,都会显示错误,表未在实例中创建:
2023-06-11T14:23:19.813Z 8155569d-cd7e-4fc4-833c-db7da6afbf3d INFO Error: Connection lost: The server closed the connection.
at Protocol.end (/var/task/index.js:9350:17)
at Socket.<anonymous> (/var/task/index.js:9907:32)
at Socket.<anonymous> (/var/task/index.js:10233:14)
at Socket.emit (node:events:525:35)
at Socket.emit (node:domain:489:12)
at endReadableNT (node:internal/streams/readable:1358:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
--------------------
at Protocol._enqueue (/var/task/index.js:9374:52)
at Protocol.handshake (/var/task/index.js:9304:27)
at PoolConnection.connect (/var/task/index.js:9925:22)
at Pool.getConnection (/var/task/index.js:10330:20)
at Pool.query (/var/task/index.js:10447:12)
at /var/task/index.js:10997:22
at new Promise (<anonymous>)
at Runtime.handler (/var/task/index.js:10996:18)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
fatal: true,
code: 'PROTOCOL_CONNECTION_LOST'
}
为了解决这个问题,我尝试查看了这篇帖子,似乎与此有关:https://stackoverflow.com/questions/20210522/nodejs-mysql-error-connection-lost-the-server-closed-the-connection
但解决方案对我没有起作用,其他帖子也是如此。
另外,我注意到解决方案主要是使用Node.js而不是AWS Lambda函数。
我尝试了很多方法,想知道可能出现的问题以及如何解决这个问题。任何建议都将不胜感激。
以下是使用AWS CDK定义的database-stack.ts
,其中包括AWSCustomResource以在创建时运行lambda函数以及rds-init.ts
,这是用于创建表的lambda函数代码。
database-stack.ts
// 创建Lambda函数以初始化具有表格和数据的DB。
const rdsLambdaFunction = new NodejsFunction(this, "rdsLambdaFN", {
entry: "./src/lambda_functions/rds-init.ts",
runtime: Runtime.NODEJS_16_X,
timeout: Duration.minutes(3), // 防止冷启动时间
functionName: "rds-init-function",
environment: {
DB_ENDPOINT_ADDRESS: dbProxy.endpoint,
DB_NAME: "vegafoliodb",
DB_SECRET_ARN: instance.secret?.secretFullArn || "", // 通过SecretARN而不是直接获取密码以增强安全性 :)
},
vpc,
vpcSubnets: vpc.selectSubnets({
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
}),
bundling: {
// 使用命令挂钩将table-creation.sql文件包含在捆绑包中
commandHooks: {
beforeBundling(inputDir: string, outputDir: string) {
return [
`cp ${inputDir}/src/lambda_functions/table-creation.sql ${outputDir}`,
];
},
afterBundling(inputDir: string, outputDir: string) {
return [];
},
beforeInstall(inputDir: string, outputDir: string) {
return [];
},
},
externalModules: [
"aws-sdk", // 不需要包含AWS SDK,因为我们正在使用本机aws-sdk。
],
},
securityGroups: [dbSG],
});
// 在db堆栈创建时创建自定义资源以调用Lambda函数
new AwsCustomResource(this, "rdsInitCustomResource", {
onCreate: {
service: "Lambda",
action: "invoke",
parameters: {
FunctionName: rdsLambdaFunction.functionArn,
InvocationType: "RequestResponse",
},
physicalResourceId: PhysicalResourceId.of(
"rdsInitCustomResource"
),
},
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: [rdsLambdaFunction.functionArn],
}),
role: customResourcerole,
});
rds-init.ts
// 在db堆栈创建时创建自定义资源以调用Lambda函数
new AwsCustomResource(this, "rdsInitCustomResource", {
onCreate: {
service: "Lambda",
action: "invoke",
parameters: {
FunctionName: rdsLambdaFunction.functionArn,
InvocationType: "RequestResponse",
},
physicalResourceId: PhysicalResourceId.of(
"rdsInitCustomResource"
),
},
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: [rdsLambdaFunction.functionArn],
}),
role: customResourcerole,
});
英文:
I am using AWS CDK for defining AWS infrastructure.
I have database-stack
that defines RDS(MySQL) instance and have set up AWSCustomResource like below to run table-creation.sql
when the Database instance is deployed for the first time.
Everytime, the instance is launched, the table is not created in the instance showing error:
2023-06-11T14:23:19.813Z 8155569d-cd7e-4fc4-833c-db7da6afbf3d INFO Error: Connection lost: The server closed the connection.
at Protocol.end (/var/task/index.js:9350:17)
at Socket.<anonymous> (/var/task/index.js:9907:32)
at Socket.<anonymous> (/var/task/index.js:10233:14)
at Socket.emit (node:events:525:35)
at Socket.emit (node:domain:489:12)
at endReadableNT (node:internal/streams/readable:1358:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
--------------------
at Protocol._enqueue (/var/task/index.js:9374:52)
at Protocol.handshake (/var/task/index.js:9304:27)
at PoolConnection.connect (/var/task/index.js:9925:22)
at Pool.getConnection (/var/task/index.js:10330:20)
at Pool.query (/var/task/index.js:10447:12)
at /var/task/index.js:10997:22
at new Promise (<anonymous>)
at Runtime.handler (/var/task/index.js:10996:18)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
fatal: true,
code: 'PROTOCOL_CONNECTION_LOST'
}
To solve this, I tried looking into this post which seemed relevant: https://stackoverflow.com/questions/20210522/nodejs-mysql-error-connection-lost-the-server-closed-the-connection
But the solution didn't work for me and other posts as well.
Also I noticed the solutions were mostly with NodeJS without AWS Lambda functions.
I tried many things and I am wondering what could be the possible problems and how to solve this issue. Any advice would be thankful.
Here are the AWS CDK defined database-stack.ts
with AWSCustomResource to run a lambda function at its creation and rds-init.ts
which is lambda function code for creating tables.
database-stack.ts
// Create Lambda Function to initialize the DB with tables and data.
const rdsLambdaFunction = new NodejsFunction(this, "rdsLambdaFN", {
entry: "./src/lambda_functions/rds-init.ts",
runtime: Runtime.NODEJS_16_X,
timeout: Duration.minutes(3), // Preventing coldstart time
functionName: "rds-init-function",
environment: {
DB_ENDPOINT_ADDRESS: dbProxy.endpoint,
DB_NAME: "vegafoliodb",
DB_SECRET_ARN: instance.secret?.secretFullArn || "", // Not Fetching Password directly but via SecretARN for security :)
},
vpc,
vpcSubnets: vpc.selectSubnets({
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
}),
bundling: {
// Use Command Hooks to include table-creation.sql file in the bundle
commandHooks: {
beforeBundling(inputDir: string, outputDir: string) {
return [
`cp ${inputDir}/src/lambda_functions/table-creation.sql ${outputDir}`,
];
},
afterBundling(inputDir: string, outputDir: string) {
return [];
},
beforeInstall(inputDir: string, outputDir: string) {
return [];
},
},
externalModules: [
"aws-sdk", // No Need to include AWS SDK as we are using native aws-sdk.
],
},
securityGroups: [dbSG],
});
// Custom Resource to invoke Lambda Function on db stack creation creation
new AwsCustomResource(this, "rdsInitCustomResource", {
onCreate: {
service: "Lambda",
action: "invoke",
parameters: {
FunctionName: rdsLambdaFunction.functionArn,
InvocationType: "RequestResponse",
},
physicalResourceId: PhysicalResourceId.of(
"rdsInitCustomResource"
),
},
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: [rdsLambdaFunction.functionArn],
}),
role: customResourcerole,
});
rds-init.ts
// Custom Resource to invoke Lambda Function on db stack creation creation
new AwsCustomResource(this, "rdsInitCustomResource", {
onCreate: {
service: "Lambda",
action: "invoke",
parameters: {
FunctionName: rdsLambdaFunction.functionArn,
InvocationType: "RequestResponse",
},
physicalResourceId: PhysicalResourceId.of(
"rdsInitCustomResource"
),
},
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: [rdsLambdaFunction.functionArn],
}),
role: customResourcerole,
});
答案1
得分: 0
将Lambda函数放置在与数据库相同的安全组中不会自动允许它们之间的连接。要实现这一点,您需要明确允许安全组内的连接。您可以像这样执行:
myDb.connections.allowDefaultPortInternally();
英文:
Placing the Lambda in the same Security Group as the Database does not automatically allow the connection between them. To do so, you need to allow connections within the SG explicitly. You can do so like this:
myDb.connections.allowDefaultPortInternally();
答案2
得分: -1
我注意到这是由于我必须为AwsCustomResource
定义的缺失依赖代码引起的。
代码需要确保在创建数据库实例(如果有dbproxy)之后运行。
// 在创建后运行rdsCustomResource(添加依赖项)
rdsCustomResource.node.addDependency(dbProxy);
rdsCustomResource.node.addDependency(instance);
我添加了两行代码,现在正常运行。
英文:
I noticed this was due to the missing dependency code I have to define for AwsCustomResource
The code needs to make sure it's ran after the creation of the database instance(dbproxy if you have)
// run rdsCustomResource after creation (Add dependency)
rdsCustomResource.node.addDependency(dbProxy);
rdsCustomResource.node.addDependency(instance);
I added two lines and working like a charm.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论