已解决:使用AWS CDK CustomResource和Lambda解决了MySQL PROTOCOL_CONNECTION_LOST错误。

huangapple go评论69阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年6月13日 12:44:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76461754.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定