同VPC内AWS Lambda连接Postgres RDS的SSL连接问题排查
问题背景
我在AWS搭建了以下架构:API Gateway -> Lambda -> RDS(Postgres),Lambda与RDS实例处于同一VPC,安全组已配置为允许所有入站和出站流量。
初始错误表现
通过API Gateway触发Lambda时,CloudWatch日志显示:
INIT_REPORT Init Duration: 3002.41 ms Phase: invoke Status: error Error Type: Runtime.Unknown INIT_REPORT Init Duration: 3002.41 ms Phase: invoke Status: error Error Type: Runtime.Unknown
初始Postgres连接字符串配置:
host=%s port=%s user=%s dbname=%s sslmode=verify-full sslrootcert=us-east-2-bundle.pem password=%s
我下载了对应区域的证书,RDS实例使用rds-ca-rsa2048-g1作为CA。
修改连接字符串后的错误
将连接字符串改为sslmode=disable后,日志错误变为:
failed to connect to `host=myHOST user=postgres database=api`: server error (FATAL: no pg_hba.conf entry for host "172.2.0.12", user "postgres", database "api", no encryption (SQLSTATE 28000))
这个错误说明问题不在IAM/安全组/权限上,必须配置SSL验证。
代码细节
建立数据库连接的Go代码(使用gORM):
func OpenDB(DbUser, DbPassword, DbPort, DbHost, DbName string) (*gorm.DB, error) { DBURL := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=verify-full sslrootcert=us-east-2-bundle.pem password=%s", DbHost, DbPort, DbUser, DbName, DbPassword) return gorm.Open(postgres.Open(DBURL), &gorm.Config{}) }
后续更新
Runtime.Unknown实际是由初始化超时导致:
INIT_REPORT Init Duration: 10007.03 ms Phase: init Status: timeout
已尝试将Lambda超时设置为10秒、30秒,也换用了全局证书包,但问题依旧。代码连接本地Postgres正常,但因RDS所在VPC无互联网访问,无法本地测试RDS连接。
解决方案
1. 确认证书文件路径正确
Lambda运行时找不到指定的证书文件是高频触发问题的原因:
- 打包Lambda代码时,务必将证书文件放在代码根目录,或在连接字符串中指定正确的相对/绝对路径
- 检查部署包结构,确保证书文件和Go二进制文件在同一层级
2. 调整SSL模式适配RDS配置
针对rds-ca-rsa2048-g1 CA,推荐调整SSL模式:
- 用
sslmode=verify-ca替代verify-full:verify-full要求主机名与证书CN严格匹配,RDS端点证书可能不满足该条件,容易导致连接超时;verify-ca仅验证CA合法性,更适配RDS场景 - 调整后的连接字符串示例:
host=%s port=%s user=%s dbname=%s sslmode=verify-ca sslrootcert=us-east-2-bundle.pem password=%s
3. 优化Lambda初始化性能
初始化超时10秒说明连接建立耗时过长,可通过以下方式优化:
- 将数据库连接初始化放在Lambda全局作用域(而非处理函数内部),利用Lambda执行环境复用特性,避免每次调用都重新建立连接
- 修改后的代码示例:
import ( "log" "os" "context" "github.com/aws/aws-lambda-go/events" "gorm.io/driver/postgres" "gorm.io/gorm" ) var db *gorm.DB func init() { var err error DBURL := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=verify-ca sslrootcert=us-east-2-bundle.pem password=%s", os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USER"), os.Getenv("DB_NAME"), os.Getenv("DB_PASSWORD")) db, err = gorm.Open(postgres.Open(DBURL), &gorm.Config{}) if err != nil { log.Fatalf("Failed to initialize DB: %v", err) } } func Handler(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { // 使用全局db对象操作数据库 return events.APIGatewayProxyResponse{StatusCode: 200}, nil }
4. 检查RDS参数组配置
确认RDS参数组的SSL相关设置:
- 查看
rds.force_ssl参数是否设为1(强制SSL连接,这也是之前禁用SSL后报错的原因) - 若需临时允许非SSL连接(仅调试用),可修改参数组中的
pg_hba.conf规则,但生产环境不建议这么做
5. 验证VPC内部网络连通性
即使安全组配置了全量流量,仍需确认:
- Lambda所在子网与RDS子网的路由表配置正确(同一VPC内默认路由即可,但要排除自定义路由的阻断情况)
- 在Lambda中添加调试代码,执行
nslookup <RDS端点>,确认RDS私有DNS能被正常解析
内容的提问来源于stack exchange,提问作者CJW
相关产品推荐
相关产品推荐

