运维笔记

AWS VPC 上 Terraform 翻车实录:我的 10 条生产级最佳实践

Cloud & DevOps 技术可视化

说真的,搞 Terraform 管 AWS VPC 这事儿,我踩过的坑比我吃过的盐还多。上个月我们团队刚把一个三层的生产 VPC 从手动配置迁移到 Terraform,中间炸了好几次监控,翻车翻得我头皮发麻。今天不整那些虚头巴脑的"最佳实践",我就把真正用血泪换来的 10 条硬核经验甩出来。

别信"默认配置",那是个坑

AWS 给的 VPC 默认配置,讲真,就是给新手体验用的。生产环境你敢用默认 CIDR 块?我见过太多人直接 cidr_block = "10.0.0.0/16",结果跟其他 VPC 对等连接时直接撞车。

我的做法:10.xxx.0.0/16 这种模式,xxx 按环境编号(dev=10, staging=20, prod=30)。别问我为什么,某次 prod 和 staging 对等连接时 IP 重叠,修了整整 4 个小时。

模块化?别过度设计

Reddit 上有个哥们说得对——“模块应该赚取它们的存在价值”。别一上来就搞十几个嵌套模块,VPC 这种东西,一个 aws_vpc 资源加几个 aws_subnet 就够了。

# 这是我踩坑后的最终方案
resource "aws_vpc" "main" {
  cidr_block           = "10.${var.env_id}.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = {
    Name        = "${var.env_name}-vpc"
    Environment = var.env_name
    ManagedBy   = "terraform"
    CreatedAt   = timestamp()  # 别学我,这会导致每次 plan 都变
  }
}

等等,上面那个 timestamp() 我后来去掉了——每次 terraform plan 都提示变化,烦死。血的教训。

子网划分:别省那点 IP

我见过最离谱的配置是一个 /24 子网里跑了 200 多个 EC2。NAT 网关的带宽直接被打满,P99 从 200ms 飙到 3.2s。

推荐子网大小:

子网类型推荐 CIDR可用 IP适用场景
公有子网/24251ALB、NAT 网关、堡垒机
私有应用子网/204091ECS、EKS worker nodes
私有数据子网/221019RDS、ElastiCache、Aurora

别问我为什么数据子网用 /22——RDS 多 AZ 部署加上 read replica,IP 消耗比你想象的快得多。

路由表:显式优于隐式

Terraform 的 aws_route_tableaws_main_route_table 这俩资源,我用一次翻车一次。主路由表太隐式了,某次同事手贱改了关联,结果所有私有子网都跑到了互联网上——幸亏是 staging 环境。

# 显式关联,别偷懒
resource "aws_route_table_association" "private_app" {
  count          = length(var.private_app_subnet_cidrs)
  subnet_id      = aws_subnet.private_app[count.index].id
  route_table_id = aws_route_table.private.id
}

安全组 vs NACL:别搞混了

这是社区里吵得最凶的话题之一。我的结论很简单:

  • 安全组:有状态,用于 EC2/ELB 级别。99% 的场景用这个就够了。
  • NACL:无状态,用于子网级别。只有需要显式拒绝特定 IP 时才用。

某次我们被 DDoS,安全组没法快速封禁来源 IP,NACL 救了一命。但平时真别用 NACL——调试无状态规则能让你怀疑人生。

VPC Flow Logs:不开等于裸奔

我看到 Reddit 上有人问"为什么我的流量莫名其妙被拒绝了?"——没有 Flow Logs,你连查都查不了。我们团队把 Flow Logs 丢到 CloudWatch Logs,然后接 Athena 查询。

resource "aws_flow_log" "main" {
  iam_role_arn    = aws_iam_role.flow_log.arn
  log_destination = aws_cloudwatch_log_group.flow_log.arn
  traffic_type    = "ALL"
  vpc_id          = aws_vpc.main.id
}

traffic_type = "ALL" 别省,ACCEPTREJECT 都要看。上次排查一个跨 VPC 访问问题,就是靠 REJECT 日志发现安全组规则写错了。

NAT 网关:贵,但值得

单 AZ 的 NAT 网关一个月大概 $32,加上数据传输费。我们试过用 NAT 实例省钱,结果运维成本翻了三倍。某次实例挂了,整个私有子网出不去,EKS 节点拉不了镜像,全线瘫痪。

结论: 生产环境至少两个 AZ 各部署一个 NAT 网关,别心疼那点钱。单点故障的代价远高于 NAT 网关的费用。

Terraform State:别放在本地

“Remote state or go home”——这话虽然糙,但理不糙。我们团队用 S3 + DynamoDB 锁,配置如下:

terraform {
  backend "s3" {
    bucket         = "mycompany-terraform-state"
    key            = "vpc/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-state-lock"
    encrypt        = true
  }
}

DynamoDB 锁是必须的,不然两个人同时 apply 直接炸裂。我们有一次就是忘了加锁,结果 state 文件损坏,恢复花了整整一天。

环境隔离:Workspace 不够用

Terraform Workspace 听起来很美,但实践下来问题一堆——state 文件混在一起,一不小心就把 prod 的配置 apply 到 dev 上了。

我的方案: 用目录隔离。

terraform/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   ├── staging/
│   └── prod/
└── modules/
    ├── vpc/
    └── security-groups/

每个环境有自己的 backend 配置,永远不可能搞混。多花 5 分钟创建目录,省下 5 小时排查环境混淆的问题。

标签:不是装饰品

AWS 账单按标签分组,没标签你连哪个部门花了多少钱都查不出来。我们团队强制以下标签:

Environment: dev/staging/prod
Team: platform/data/ml
CostCenter: xyz-123
Owner: user@company.com
ManagedBy: terraform

说个真事:某个月 AWS 账单突然多了 $2000,查了半天发现是一个废弃的 NAT 网关没删。如果当时有 Owner 标签,直接找到负责人,5 分钟解决。

常见问题 FAQ

Q: VPC 对等连接还是 Transit Gateway? A: 少于 5 个 VPC 用对等连接,超过就用 Transit Gateway。对等连接不支持传递路由,这是最大的坑。

Q: 每个环境都要独立的 VPC 吗? A: 必须的。共享 VPC 的风险太大——dev 的误操作可能影响 prod。虽然 AWS 有 RAM 共享,但生产环境我建议完全隔离。

Q: Terraform 版本怎么管理? A: required_version 锁定大版本,比如 >= 1.0, < 2.0。别用 latest,某次 Terraform 1.5 升级把我们的 provider 搞崩了。

Q: VPC 的 CIDR 怎么规划? A: 预留足够空间。我们一个中型项目用了 3 个 /16,现在后悔没留更大的。建议用 RFC 1918 地址,10.0.0.0/8 够你用到退休。

Q: 私有子网访问 S3 用啥? A: VPC Endpoint (Gateway 类型),免费且不走公网。别用 NAT 网关访问 S3,浪费钱还慢。

总结表

实践推荐不推荐
State 存储S3 + DynamoDB本地文件
环境隔离目录隔离Workspace
子网大小/20 起/24 以下
NAT 方案NAT 网关NAT 实例
流量日志Flow Logs不开
路由关联显式资源主路由表
标签策略强制标签无标签

最后说一句:Terraform 是个好工具,但它不会替你思考。每个 terraform apply 之前,想想这行代码在生产环境会不会炸。我们团队现在强制 PR review + terraform plan 输出审查,翻车率从 30% 降到了 5% 以下。

别让你的 VPC 成为下一个事故报告的主角。