如何将 CloudFormation 模板与在 RDS 中的 Aurora PostgreSQL 实例上创建新数据库的 Lambda 函数连接?
How to connect a CloudFormation Template with a Lambda Function that creates a new database on an Aurora PostgreSQL Instance in RDS?
希望能帮到你!
目标
我正在尝试在 AWS 上配置以下场景:
- 在 AWC CLI 中:调用 CloudFormation 模板创建新堆栈。
- 在模板中:调用 Lambda 函数。
- 在 Lambda 函数中:连接到 Aurora PostgreSQL Instance in RDS via pg 模块。
- 在 PostgresSQL 中:创建一个新数据库。
短篇小说
一旦我将我的 Lambda 函数添加到默认 VPC,它就无法再将响应发送回 CloudFormation,并且堆栈卡在 CREATE_IN_PROGRESS
。当我从 Lambda 函数的配置中删除 VPC 时,它起作用了。但后来我无法再连接到另一个组件,如 RDS 数据库实例。如何处理?
长话短说
为了让它工作,我的第一步是手动连接到 PostgreSQL 并创建一个新数据库:
- 配置默认安全组以允许所有入站和所有出站流量。不用担心,仅用于测试。我稍后会更改它。
- 将安全组添加到 RDS 实例以接受来自同一 VPC 内其他组件的流量。步骤 3 所必需的。
- 创建了一个 Cloud9 环境以访问 VPC 中的终端。连接到 PostgreSQL 数据库并创建数据库成功!
现在我的目标是通过 Lambda 函数创建一个新数据库:
- 手动创建了一个新的 Lambda 函数,包括一个具有基本 Lambda 授权的新角色。还为角色添加了
AdministratorAccess
。不用担心,再次仅用于测试。 Lambda 函数在 Node.js 12.x 上运行,超时为 15 秒。
- 将我的 Lambda 脚本和 pg 模块打包成一个 zip 文件并上传。脚本如下所示:
const { Client } = require('pg');
exports.handler = async (event, context) => {
var databaseName = event.DatabaseName;
var dbSuperUser = event.DBSuperUser;
var dbSuperPassword = event.DBSuperPassword;
const dbClient = new Client({
host: "<Database Instance Endpoint>",
port: 5432,
database: "postgres",
user: dbSuperUser,
password: dbSuperPassword
});
await dbClient.connect();
await dbClient.query("CREATE DATABASE " + databaseName);
await dbClient.end();
var response = {status: 'SUCCESS'};
return response;
};
- 通过
aws lambda invoke
从 Cloud9 调用 Lambda 脚本会导致超时。这是预期的行为,因为 Lambda 函数与 RDS 数据库实例不在同一个 VPC 中。
- 在 Lambda 函数配置中添加了带有三个默认子网和默认安全组的默认 VPC。瞧,数据库已创建!我们走上正轨。
现在我尝试调用 Lambda 函数并从基本的 CloudFormation 模板创建数据库:
- 创建的模板包含 Custom Ressource:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"DatabaseName": {"Type": "String"}
},
"Resources": {
"SampleInstance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t1.micro",
"ImageId": {"Fn::GetAtt": ["CreateDatabase", "Id"]}
}
},
"CreateDatabase": {
"Type": "Custom::CreateDatabase",
"Properties": {
"ServiceToken": "<Lambda Function ARN>",
"DBSuperUser": "<DBSuperUser>",
"DBSuperPassword": "<DBSuperPassword>",
"DatabaseName": {"Ref": "DatabaseName"}
}
}
}
}
EC2 实例只是一个示例,无论如何都无法创建,因为 ImageId 将无效。它只是用来测试我是否收到 Lambda 函数的响应。
- 更改了 Lambda 函数以从 CloudFormation 接收事件并发回响应。我可以使用 cfc-response 模块,但决定将它的代码复制到我的 Lambda 函数中,以查看底层发生了什么。请点击上面的 link 并向下滚动到 Module Source Code 以查看源代码。
我还更改了获取输入参数的方式:
var customerName = event.ResourceProperties.CustomerName;
var dbSuperUser = event.ResourceProperties.DBSuperUser;
var dbSuperPassword = event.ResourceProperties.DBSuperPassword;
并像这样将响应发送到 CloudFormation:
responseData = {Id: '1234567890'};
send(event, context, SUCCESS, responseData);
运行 通过 AWS CLI 命令 aws cloudformation create-stack
的模板导致堆栈卡在 CREATE_IN_PROGRESS
。但是数据库创建成功!
只要我从 Lambda 函数中删除 VPC 配置,问题就消失了。 CloudFormation 成功收到函数的响应并尝试创建 EC2 示例实例。但是现在Lambda函数不能再访问RDS来创建数据库了。
这会导致以下问题:
- Lambda 函数中的 VPC 配置是否导致与 CloudFormation 模板的连接出现问题?
- 如果是,我该如何解决?
- 网络配置是否正确?还是我必须更改编码?
我检查过的其他内容:
- 我在两次运行(成功和失败响应)中比较了 Lambda 脚本的以下变量和对象:
event
、context
、responseBody
、parsedUrl
、 options
、request
。它们是相同的(某些 ID 等除外)。
期待您对此的看法!
干杯
您发布的代码是一个简单的 Lambda,并没有按照 CloudFormation custom resource 的要求报告其状态。因此,第一步是确保您符合要求。
如果您是,并且您的代码在向 S3 报告状态时被阻止,那么最可能的原因是您 运行 在既没有 NAT nor an S3 endpoint 的子网中。如果您不需要 NAT,S3 端点是免费的,因此将是最好的解决方案。如果您已经拥有 NAT,请确保 Lambda 在 NAT 可用的子网中运行。
希望能帮到你!
目标
我正在尝试在 AWS 上配置以下场景:
- 在 AWC CLI 中:调用 CloudFormation 模板创建新堆栈。
- 在模板中:调用 Lambda 函数。
- 在 Lambda 函数中:连接到 Aurora PostgreSQL Instance in RDS via pg 模块。
- 在 PostgresSQL 中:创建一个新数据库。
短篇小说
一旦我将我的 Lambda 函数添加到默认 VPC,它就无法再将响应发送回 CloudFormation,并且堆栈卡在 CREATE_IN_PROGRESS
。当我从 Lambda 函数的配置中删除 VPC 时,它起作用了。但后来我无法再连接到另一个组件,如 RDS 数据库实例。如何处理?
长话短说
为了让它工作,我的第一步是手动连接到 PostgreSQL 并创建一个新数据库:
- 配置默认安全组以允许所有入站和所有出站流量。不用担心,仅用于测试。我稍后会更改它。
- 将安全组添加到 RDS 实例以接受来自同一 VPC 内其他组件的流量。步骤 3 所必需的。
- 创建了一个 Cloud9 环境以访问 VPC 中的终端。连接到 PostgreSQL 数据库并创建数据库成功!
现在我的目标是通过 Lambda 函数创建一个新数据库:
- 手动创建了一个新的 Lambda 函数,包括一个具有基本 Lambda 授权的新角色。还为角色添加了
AdministratorAccess
。不用担心,再次仅用于测试。 Lambda 函数在 Node.js 12.x 上运行,超时为 15 秒。 - 将我的 Lambda 脚本和 pg 模块打包成一个 zip 文件并上传。脚本如下所示:
const { Client } = require('pg');
exports.handler = async (event, context) => {
var databaseName = event.DatabaseName;
var dbSuperUser = event.DBSuperUser;
var dbSuperPassword = event.DBSuperPassword;
const dbClient = new Client({
host: "<Database Instance Endpoint>",
port: 5432,
database: "postgres",
user: dbSuperUser,
password: dbSuperPassword
});
await dbClient.connect();
await dbClient.query("CREATE DATABASE " + databaseName);
await dbClient.end();
var response = {status: 'SUCCESS'};
return response;
};
- 通过
aws lambda invoke
从 Cloud9 调用 Lambda 脚本会导致超时。这是预期的行为,因为 Lambda 函数与 RDS 数据库实例不在同一个 VPC 中。 - 在 Lambda 函数配置中添加了带有三个默认子网和默认安全组的默认 VPC。瞧,数据库已创建!我们走上正轨。
现在我尝试调用 Lambda 函数并从基本的 CloudFormation 模板创建数据库:
- 创建的模板包含 Custom Ressource:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"DatabaseName": {"Type": "String"}
},
"Resources": {
"SampleInstance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t1.micro",
"ImageId": {"Fn::GetAtt": ["CreateDatabase", "Id"]}
}
},
"CreateDatabase": {
"Type": "Custom::CreateDatabase",
"Properties": {
"ServiceToken": "<Lambda Function ARN>",
"DBSuperUser": "<DBSuperUser>",
"DBSuperPassword": "<DBSuperPassword>",
"DatabaseName": {"Ref": "DatabaseName"}
}
}
}
}
EC2 实例只是一个示例,无论如何都无法创建,因为 ImageId 将无效。它只是用来测试我是否收到 Lambda 函数的响应。
- 更改了 Lambda 函数以从 CloudFormation 接收事件并发回响应。我可以使用 cfc-response 模块,但决定将它的代码复制到我的 Lambda 函数中,以查看底层发生了什么。请点击上面的 link 并向下滚动到 Module Source Code 以查看源代码。
我还更改了获取输入参数的方式:
var customerName = event.ResourceProperties.CustomerName;
var dbSuperUser = event.ResourceProperties.DBSuperUser;
var dbSuperPassword = event.ResourceProperties.DBSuperPassword;
并像这样将响应发送到 CloudFormation:
responseData = {Id: '1234567890'};
send(event, context, SUCCESS, responseData);
运行 通过 AWS CLI 命令
aws cloudformation create-stack
的模板导致堆栈卡在CREATE_IN_PROGRESS
。但是数据库创建成功!只要我从 Lambda 函数中删除 VPC 配置,问题就消失了。 CloudFormation 成功收到函数的响应并尝试创建 EC2 示例实例。但是现在Lambda函数不能再访问RDS来创建数据库了。
这会导致以下问题:
- Lambda 函数中的 VPC 配置是否导致与 CloudFormation 模板的连接出现问题?
- 如果是,我该如何解决?
- 网络配置是否正确?还是我必须更改编码?
我检查过的其他内容:
- 我在两次运行(成功和失败响应)中比较了 Lambda 脚本的以下变量和对象:
event
、context
、responseBody
、parsedUrl
、options
、request
。它们是相同的(某些 ID 等除外)。
期待您对此的看法!
干杯
您发布的代码是一个简单的 Lambda,并没有按照 CloudFormation custom resource 的要求报告其状态。因此,第一步是确保您符合要求。
如果您是,并且您的代码在向 S3 报告状态时被阻止,那么最可能的原因是您 运行 在既没有 NAT nor an S3 endpoint 的子网中。如果您不需要 NAT,S3 端点是免费的,因此将是最好的解决方案。如果您已经拥有 NAT,请确保 Lambda 在 NAT 可用的子网中运行。