You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 07:02:04