在没有代理的情况下从 Lambda 函数访问 AWS RDS MySQL 数据库

Access AWS RDS MySQL database from a Lambda Function without a proxy

我正在尝试从 lambda 函数 (LF) 连接到包含 MySQL 数据库的 AWS RDS 实例,但我无法让它工作。

我还不想使用 RDS 代理,因为目前,一次只有 1 个连接到 RDS 数据库。

LF 和 RDS 实例都是:

LF 分配了一个包含 AmazonRDSFullAccess & AWSLambdaVPCAccessExecutionRole IAM 策略的角色。

所有三个子网都有一个共同的 NACL,其中包含允许所有流量的入站和出站规则 to/from 0.0.0.0/0。

所有三个子网都有一个公共路由 table,其中包含一条以默认 VPC 的 CIDR 为目的地、以 'local' 为目标的路由。

RDS 实例public仅可用。

VPC 上启用了 DNS 主机名和 DNS 解析。

Lambda应用代码如下:

using System;
using System.Data.SqlClient;

namespace TestRDSAccess
{
    public class Function
    {
        public void FunctionHandler()
        {
            string server = Environment.GetEnvironmentVariable("DB_ENDPOINT");
            string database = Environment.GetEnvironmentVariable("DATABASE");
            string username = Environment.GetEnvironmentVariable("USER");
            string password = Environment.GetEnvironmentVariable("PASSWORD");

            string connectionString = String.Format("server={0};database={1};user={2};password={3}", server, database, username, password);

            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                conn.Open();
            }
        }
    }
}

关于连接字符串,当它以任何方式不正确时,它会超时而不是 return 立即出错。

环境变量字符串 return 如果我将它们写入控制台则为正确的值。 'server' 字符串包含端点和“,3306”。 (请注意,这与 AWS 自己的文档相反,它说在连接字符串中指定 'port=',这会导致 'parameter not recognised' 错误。)

调用 Lambda 函数时我的错误:

  "errorType": "InvalidOperationException",
  "errorMessage": "Internal connection fatal error.",
  "stackTrace": [
    "at System.Data.SqlClient.TdsParserStateObject.TryProcessHeader()",
    "at System.Data.SqlClient.TdsParser.ConsumePreLoginHandshake(Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean& marsCapable, Boolean& fedAuthRequired)",
    "at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean withFailover)",
    "at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover)",
    "at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)",
    "at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)",
    "at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken)",
    "at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)",
    "at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)",
    "at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)",
    "at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)",
    "at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)",
    "at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)",
    "at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)",
    "at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)",
    "at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)",
    "at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)",
    "at System.Data.SqlClient.SqlConnection.Open()",
    "at TestRDSAccess.Function.FunctionHandler() in [local path to cs file]:line 62"
  ]

最后一行是对 conn.Open(); 行的引用,即使它不在第 62 行。

此外,在 RDS 实例的日志和事件部分,在 'error/mysql-error-running.log' 中,我看到以下错误:

2022-05-01T15:05:08.928116Z 609 [Warning] [MY-010055] [Server] IP address '172.31.34.94' could not be resolved: Name or service not known

这听起来很像 DNS 问题。由于 RDS 实例设置为 public,因此它将端点解析为 public IP 地址,当我尝试从同一 VPC 中访问它时,这可能没有任何意义(来自 LF),我尝试将 RDS 实例上的 'publicly available' 设置为 false,但没有任何区别。

172.31.34.94 在 VPC 的 CIDR (172.31.0.0/16) 内。

有人知道我错过了什么吗?抱歉 post 太长了;我想解释一切!

您不能使用 SqlConnection 连接到 MySQL 数据库。

安装 MySqlConnector 并改用 MySqlConnection 类型。

The 'server' string contains both the endpoint and ",3306". (Note that this is contrary to AWS's own documentation, which says to specify 'port=' in the connection string, which causes a 'parameter not recognised' error.)

按照 AWS 文档,将 ServerPort 分别放在连接字符串中。 MySqlConnection 不支持在逗号后添加端口。 (请注意,您实际上可以省略 Port=3306;,因为这是默认设置。)