如何将Go语言编写的AWS Lambda连接到RDS数据库?
解决Go Lambda无法访问RDS Postgres的问题
问题背景
使用API Gateway、Go语言Lambda、RDS Postgres搭建API,API Gateway与Lambda通信正常,但Lambda无法访问RDS,基础设施用Terraform管理。
现有配置
1. RDS Terraform配置
resource "aws_db_instance" "bl-db" { allocated_storage = 5 # gigabytes engine = "postgres" engine_version = "16.4" identifier = "main" instance_class = "db.t3.micro" username = "myname" password = "mypassword" publicly_accessible = false }
2. Lambda IAM角色Terraform配置
resource "aws_iam_role" "hello" { name = "hello" assume_role_policy = <<POLICY { "Version": "2012-10-17", "Statement": { "Action": "sts:AssumeRole", "Principal": { "Service": "lambda.amazonaws.com" }, "Effect": "Allow" } } POLICY }
3. 当前Go Lambda代码
package main import ( "fmt" "net/http" "log" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" ) // The input type and the output type are defined by the API Gateway. func handleRequest(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { db, err := sqlx.Connect("postgres", "user=postgres dbname=main sslmode=disable password=mypassword host=myhost") if err != nil { log.Fatalln(err) } defer db.Close() // Test the connection to the database if err := db.Ping(); err != nil { log.Fatal(err) } else { log.Println("Successfully Connected") } db.Queryx("some sql query") res := events.APIGatewayProxyResponse{ StatusCode: http.StatusOK, Headers: map[string]string{"Content-Type": "text/plain; charset=utf-8"}, Body: fmt.Sprintf("Hello!\n"), } return res, nil } func main() { lambda.Start(handleRequest) }
解决方案步骤
1. 配置Lambda加入VPC,确保网络可达
由于RDS设置了publicly_accessible = false,仅允许VPC内部访问,必须将Lambda部署到与RDS同VPC的私有子网中,并配置安全组规则:
- 给Lambda添加VPC子网和安全组配置
- 让RDS安全组允许来自Lambda安全组的5432端口(Postgres默认端口)入站流量
更新Lambda的Terraform配置(替换为你的VPC/子网资源):
resource "aws_lambda_function" "hello" { # 其他原有配置(如代码包、运行时等) vpc_config { subnet_ids = [aws_subnet.private_1.id, aws_subnet.private_2.id] security_group_ids = [aws_security_group.lambda_sg.id] } } # 创建Lambda专用安全组 resource "aws_security_group" "lambda_sg" { name = "lambda-rds-access" description = "Allow Lambda to access RDS" vpc_id = aws_vpc.main.id egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } # 更新RDS配置,关联专用安全组 resource "aws_db_instance" "bl-db" { # 原有配置... vpc_security_group_ids = [aws_security_group.rds_sg.id] } # 创建RDS安全组,允许Lambda访问 resource "aws_security_group" "rds_sg" { name = "postgres-rds" description = "Allow access from Lambda" vpc_id = aws_vpc.main.id ingress { from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [aws_security_group.lambda_sg.id] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
2. 给Lambda IAM角色添加VPC运行权限
Lambda在VPC中运行需要创建和管理弹性网络接口(ENI),添加AWS托管的VPC访问执行策略:
resource "aws_iam_role_policy_attachment" "lambda_vpc_access" { role = aws_iam_role.hello.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" }
3. 修复Go代码中的数据库连接逻辑
- 修正用户名:Terraform中RDS用户是
myname,代码中误写为postgres - 替换硬编码Host:通过环境变量获取RDS endpoint,避免硬编码
- 启用SSL:AWS RDS Postgres默认要求SSL连接,将
sslmode设为require - 替换
log.Fatal:不要直接终止进程,返回错误给API Gateway - 处理查询错误与结果集关闭
更新后的Go代码:
package main import ( "fmt" "net/http" "os" "log" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" ) func handleRequest(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { // 从环境变量读取数据库配置 dbUser := os.Getenv("DB_USER") dbPass := os.Getenv("DB_PASSWORD") dbHost := os.Getenv("DB_HOST") dbName := os.Getenv("DB_NAME") dsn := fmt.Sprintf("user=%s password=%s host=%s dbname=%s sslmode=require", dbUser, dbPass, dbHost, dbName) db, err := sqlx.Connect("postgres", dsn) if err != nil { log.Printf("数据库连接失败: %v", err) return events.APIGatewayProxyResponse{ StatusCode: http.StatusInternalServerError, Body: "数据库连接失败", }, err } defer db.Close() if err := db.Ping(); err != nil { log.Printf("数据库连通性检测失败: %v", err) return events.APIGatewayProxyResponse{ StatusCode: http.StatusInternalServerError, Body: "数据库连通性检测失败", }, err } log.Println("成功连接数据库") // 执行查询并处理结果 rows, err := db.Queryx("SELECT * FROM your_table") // 替换为实际查询语句 if err != nil { log.Printf("查询执行失败: %v", err) return events.APIGatewayProxyResponse{ StatusCode: http.StatusInternalServerError, Body: "查询执行失败", }, err } defer rows.Close() // 示例:遍历查询结果(根据实际表结构调整) for rows.Next() { // var item YourStruct // err := rows.StructScan(&item) // if err != nil { // log.Printf("结果扫描失败: %v", err) // return ... // } // 处理数据逻辑 } if err := rows.Err(); err != nil { log.Printf("结果遍历出错: %v", err) return events.APIGatewayProxyResponse{ StatusCode: http.StatusInternalServerError, Body: "结果处理失败", }, err } return events.APIGatewayProxyResponse{ StatusCode: http.StatusOK, Headers: map[string]string{"Content-Type": "text/plain; charset=utf-8"}, Body: "Hello! 数据库访问成功", }, nil } func main() { lambda.Start(handleRequest) }
4. 通过Terraform给Lambda注入环境变量
将RDS的配置信息作为环境变量传递给Lambda,避免代码硬编码:
resource "aws_lambda_function" "hello" { # 其他原有配置... environment { variables = { DB_USER = aws_db_instance.bl-db.username DB_PASSWORD = aws_db_instance.bl-db.password DB_HOST = aws_db_instance.bl-db.endpoint DB_NAME = "main" # 匹配你的数据库名称 } } }
内容的提问来源于stack exchange,提问作者Marsolgen
相关产品推荐
相关产品推荐

