如何使用Terraform在生产环境将Postgres数据库部署至AWS RDS
生产环境Terraform部署私有RDS的PostgreSQL资源方案
核心问题说明
你当前需要开放RDS公网访问的核心原因是:本地运行的Terraform进程需要调用postgresql provider访问RDS实例,执行数据库内部的库、角色、权限配置操作。生产环境完全不需要将RDS暴露到公网,有多种成熟方案可以解决该问题。
可选落地方式
方案1:本地执行Terraform时通过隧道访问私有RDS
不需要大幅调整现有Terraform逻辑,仅修改网络访问路径即可,适合开发测试场景:
- 先调整RDS基础配置:
- 将
aws_db_instance的publicly_accessible参数设置为false - 替换
db_subnet_group_name为RDS所属VPC的私有子网组 - 调整
aws_security_group.db-rules的入站规则,移除0.0.0.0/0的5432端口放行规则,改为仅允许VPC内部CIDR或跳板机/SSM端点的安全组访问5432端口
- 将
- 本地打通网络隧道:
- 跳板机SSH转发:在VPC公有子网部署一台跳板机,本地执行命令
ssh -L 5432:<RDS私有域名>:5432 <跳板机用户名>@<跳板机公网IP>,同时将postgresql provider的host参数改为localhost即可 - 无公网入口的SSM转发:通过AWS Systems Manager Session Manager做端口转发,本地配置好AWS权限后执行
aws ssm start-session --target <SSM托管实例ID> --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters '{"host":["<RDS私有域名>"],"portNumber":["5432"], "localPortNumber":["5432"]}',同样将provider host改为localhost即可,该方案不需要跳板机开放公网SSH端口,安全性更高。
- 跳板机SSH转发:在VPC公有子网部署一台跳板机,本地执行命令
方案2:在VPC内部运行Terraform(生产环境首选)
完全规避公网访问需求,符合生产安全规范,实现逻辑如下:
- 选择CI/CD运行环境部署到VPC私有子网内,可选环境包括AWS CodeBuild、自托管GitHub/GitLab Runner、私有子网内的EC2实例
- 给运行环境配置IAM角色授予AWS资源操作权限,不需要配置明文AK/SK
- 配置RDS安全组允许该运行环境的安全组访问5432端口
- 每次变更通过CI/CD流程自动执行terraform plan/apply,整个过程所有流量都在VPC内部,无需暴露任何资源到公网。
方案3:拆分Terraform职责,数据库内操作独立实现
彻底规避Terraform连接数据库的需求,降低架构耦合:
- 将Terraform配置拆分为两部分:第一部分仅管理AWS基础设施资源(VPC、RDS实例、安全组、ECR等),不需要调用postgresql provider
- 数据库内的库、角色、权限、Schema等操作,改用专门的数据库迁移工具(Flyway、Liquibase等)实现,迁移任务的执行环境部署在VPC内部,随应用发布流程同步执行。
核心配置修改示例
安全组调整
resource "aws_security_group" "db-rules" { name = "rds-sec-grp" vpc_id = var.rds_vpc_id tags = { Name = "rds-sec-grp" } // 仅允许VPC内部CIDR访问5432 ingress { from_port = "5432" to_port = "5432" protocol = "tcp" cidr_blocks = [var.vpc_cidr] // 替换为你的VPC CIDR } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
RDS实例调整
resource "aws_db_instance" "default" { identifier = "db-instance" allocated_storage = 10 engine = "postgres" engine_version = "13.2" port = 5432 instance_class = "db.t3.medium" multi_az = true // 生产环境建议开启多可用区 name = "db_system_${var.env}" username = var.db_master_username password = var.db_master_password publicly_accessible = false // 关闭公网访问 skip_final_snapshot = var.env == "prod" ? false : true // 生产环境禁止跳过最终快照 db_subnet_group_name = var.rds_private_subnet_group // 替换为私有子网组 vpc_security_group_ids = [aws_security_group.db-rules.id] tags = { Name = "db-instance" } }
内容的提问来源于stack exchange,提问作者raydenl
相关产品推荐
相关产品推荐

