Terraform:AWS VPC

Terraform AWS VPC 完整架构详细讲解

这是一个生产级标准的AWS双可用区VPC网络架构 ,也是AWS上部署任何应用的基础网络底座。我会从架构原理、代码逐行解析、流量走向、最佳实践、常见坑点五个维度进行全面讲解,让你不仅能看懂代码,还能理解"为什么这么写"。

一、整体架构概览

你这个Demo实现的是AWS最经典的"公网+私网"分层架构,逻辑拓扑如下:

复制代码
互联网
  ↓
Internet Gateway (IGW)
  ↓
┌─────────────────────────────────────────────────┐
│                  VPC (10.0.0.0/16)              │
│  ┌─────────────┐        ┌─────────────┐         │
│  │ 可用区eu-west-1a │        │ 可用区eu-west-1b │         │
│  │ ┌─────────┐ │        │ ┌─────────┐ │         │
│  │ │公网子网1│ │        │ │公网子网2│ │         │
│  │ │10.0.1.0/24│ │        │ │10.0.2.0/24│ │         │
│  │ └─────────┘ │        │ └─────────┘ │         │
│  │     ↓       │        │             │         │
│  │ ┌─────────┐ │        │             │         │
│  │ │NAT网关  │ │        │             │         │
│  │ └─────────┘ │        │             │         │
│  │     ↓       │        │             │         │
│  │ ┌─────────┐ │        │ ┌─────────┐ │         │
│  │ │私网子网1│ │        │ │私网子网2│ │         │
│  │ │10.0.4.0/24│ │        │ │10.0.5.0/24│ │         │
│  │ └─────────┘ │        │ └─────────┘ │         │
│  └─────────────┘        └─────────────┘         │
└─────────────────────────────────────────────────┘

核心组件职责

  • VPC:你的私有网络隔离环境,完全由你控制
  • 公网子网:直接暴露在互联网的子网,适合放Web服务器、负载均衡器
  • 私网子网:完全隔离的内网子网,适合放数据库、应用服务器等核心服务
  • Internet Gateway(IGW):VPC与互联网的出入口
  • NAT Gateway:让私网资源能"单向"访问互联网(下载更新、调用外部API),但互联网无法主动访问私网资源

二、逐文件详细解析

1. versions.tf(Terraform版本约束)

你没有贴这个文件,但这是所有Terraform项目的必备文件,标准写法如下:

hcl 复制代码
terraform {
  required_version = ">= 1.0.0"  # 要求Terraform CLI版本不低于1.0
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"  # 使用AWS Provider 5.x系列版本
    }
  }
}

为什么重要

  • 避免不同团队成员使用不同版本Terraform导致的兼容性问题
  • ~> 5.0表示只接受5.x的补丁更新,不会自动升级到6.x(大版本可能有不兼容变更)

2. provider.tf(AWS提供商配置)

同样是必备文件,告诉Terraform要操作哪个云厂商的资源:

hcl 复制代码
provider "aws" {
  region = "eu-west-1"  # AWS区域,这里是爱尔兰
  # 认证方式:优先使用环境变量,然后是~/.aws/credentials文件
  # 生产环境不要硬编码AK/SK!
}

3. vars.tf(变量定义)

你的Demo里把所有值都硬编码了,生产环境建议抽成变量,方便复用和修改:

hcl 复制代码
variable "aws_region" {
  description = "AWS区域"
  type        = string
  default     = "eu-west-1"
}

variable "vpc_cidr" {
  description = "VPC的CIDR块"
  type        = string
  default     = "10.0.0.0/16"
}

variable "public_subnet_cidrs" {
  description = "公网子网CIDR列表"
  type        = list(string)
  default     = ["10.0.1.0/24", "10.0.2.0/24"]
}

variable "private_subnet_cidrs" {
  description = "私网子网CIDR列表"
  type        = list(string)
  default     = ["10.0.4.0/24", "10.0.5.0/24"]
}

4. vpc.tf(核心网络组件)

这是整个架构的核心文件,我们逐资源解析:

4.1 VPC本身
hcl 复制代码
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"  # VPC的IP地址范围,最多65536个IP
  instance_tenancy     = "default"      # 实例租赁模式,default=共享硬件,dedicated=专用硬件(贵很多)
  enable_dns_support   = true           # 启用VPC内的DNS解析(必须开)
  enable_dns_hostnames = true           # 让EC2实例自动获得DNS主机名(必须开,否则EC2无法解析域名)
  enable_classiclink   = false          # 旧版功能,现在不需要

  tags = {
    Name = "main"  # 资源名称,AWS控制台里能看到
  }
}

关键参数说明

  • cidr_block:AWS推荐使用10.0.0.0/8、172.16.0.0/12、192.168.0.0/16这三个私有网段
  • enable_dns_hostnames:90%的新手都会踩这个坑!如果不开,EC2实例无法解析任何域名,包括AWS自己的服务(如S3)
4.2 公网子网
hcl 复制代码
resource "aws_subnet" "main-public-1" {
  vpc_id                  = aws_vpc.main.id  # 关联到上面创建的VPC
  cidr_block              = "10.0.1.0/24"    # 子网IP范围,256个IP
  map_public_ip_on_launch = true             # 自动给这个子网里的EC2分配公网IP
  availability_zone       = "eu-west-1a"     # 所在可用区

  tags = {
    Name = "main-public-1"
  }
}

公网子网的本质

  • 不是因为叫"公网"就自动能上网,而是因为后面会关联到公网路由表
  • map_public_ip_on_launch = true只是自动分配公网IP,没有路由还是上不了网
  • 生产环境建议关闭这个参数,只给需要公网IP的实例手动分配EIP
4.3 私网子网
hcl 复制代码
resource "aws_subnet" "main-private-1" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.4.0/24"
  map_public_ip_on_launch = false  # 不分配公网IP,完全内网
  availability_zone       = "eu-west-1a"

  tags = {
    Name = "main-private-1"
  }
}

私网子网的优势

  • 最高级别的安全隔离,互联网无法直接访问
  • 所有入站流量必须通过公网的跳板机或负载均衡器转发
  • 适合存放数据库、缓存、消息队列等核心敏感服务
4.4 Internet Gateway(互联网网关)
hcl 复制代码
resource "aws_internet_gateway" "main-gw" {
  vpc_id = aws_vpc.main.id  # 一个VPC只能有一个IGW

  tags = {
    Name = "main"
  }
}

IGW的工作原理

  • 是一个逻辑网关,不是物理设备,AWS自动维护高可用
  • 负责VPC内公网IP与互联网IP之间的地址转换(NAT)
  • 同时支持入站和出站流量
4.5 公网路由表与关联
hcl 复制代码
# 创建公网路由表
resource "aws_route_table" "main-public" {
  vpc_id = aws_vpc.main.id
  
  # 核心路由规则:所有目标地址(0.0.0.0/0)的流量都走IGW
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main-gw.id
  }

  tags = {
    Name = "main-public"
  }
}

# 将公网子网1关联到公网路由表
resource "aws_route_table_association" "main-public-1-a" {
  subnet_id      = aws_subnet.main-public-1.id
  route_table_id = aws_route_table.main-public.id
}

# 将公网子网2关联到公网路由表
resource "aws_route_table_association" "main-public-2-a" {
  subnet_id      = aws_subnet.main-public-2.id
  route_table_id = aws_route_table.main-public.id
}

路由表是网络的灵魂

  • 子网本身没有"公网/私网"属性,关联了什么路由表,它就是什么子网
  • 一个子网只能关联一个路由表,但一个路由表可以关联多个子网
  • 0.0.0.0/0是默认路由,当没有更精确的路由匹配时,就走这条

5. nat.tf(NAT网关与私网路由)

NAT网关是私网子网访问互联网的唯一通道,也是生产环境的必备组件。

5.1 弹性IP(EIP)
hcl 复制代码
resource "aws_eip" "nat" {
  vpc = true  # 这是VPC类型的EIP,不是经典网络的
}

为什么NAT网关需要EIP

  • NAT网关本身是一个AWS托管服务,需要一个固定的公网IP
  • 所有私网资源访问互联网时,对外显示的都是这个EIP
  • EIP是免费的,但如果没有绑定到任何资源,AWS会按小时收费
5.2 NAT网关
hcl 复制代码
resource "aws_nat_gateway" "nat-gw" {
  allocation_id = aws_eip.nat.id          # 绑定上面创建的EIP
  subnet_id     = aws_subnet.main-public-1.id  # NAT网关必须放在公网子网!
  depends_on    = [aws_internet_gateway.main-gw]  # 确保IGW先创建完成

  tags = {
    Name = "main-nat"
  }
}

关键注意事项

  • depends_on非常重要!如果没有这个,Terraform可能会先创建NAT网关再创建IGW,导致NAT网关创建失败
  • NAT网关必须放在公网子网,因为它自己需要通过IGW访问互联网
  • 你的Demo里只创建了一个NAT网关,放在eu-west-1a,这是单点故障,生产环境建议每个可用区一个
5.3 私网路由表与关联
hcl 复制代码
# 创建私网路由表
resource "aws_route_table" "main-private" {
  vpc_id = aws_vpc.main.id
  
  # 核心路由规则:所有互联网流量都走NAT网关
  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.nat-gw.id
  }

  tags = {
    Name = "main-private"
  }
}

# 将私网子网关联到私网路由表
resource "aws_route_table_association" "main-private-1-a" {
  subnet_id      = aws_subnet.main-private-1.id
  route_table_id = aws_route_table.main-private.id
}

resource "aws_route_table_association" "main-private-2-a" {
  subnet_id      = aws_subnet.main-private-2.id
  route_table_id = aws_route_table.main-private.id
}

公网vs私网路由表对比

路由表类型 目标地址 下一跳 流量方向
公网路由表 0.0.0.0/0 IGW 双向(入站+出站)
私网路由表 0.0.0.0/0 NAT网关 单向(仅出站)

三、完整流量走向分析

理解流量走向是掌握VPC的关键,我们分两种场景:

场景1:公网EC2实例访问互联网

  1. EC2实例发起请求(如ping 8.8.8.8)
  2. 流量到达公网子网的路由表
  3. 路由表匹配到0.0.0.0/0规则,将流量转发给IGW
  4. IGW将EC2的私网IP转换为公网IP,发送到互联网
  5. 互联网返回响应,IGW将公网IP转换回私网IP,转发给EC2

场景2:私网EC2实例访问互联网

  1. 私网EC2发起请求
  2. 流量到达私网子网的路由表
  3. 路由表匹配到0.0.0.0/0规则,将流量转发给NAT网关
  4. NAT网关将私网IP转换为自己的EIP,发送到IGW
  5. IGW将流量发送到互联网
  6. 互联网返回响应,IGW转发给NAT网关
  7. NAT网关将EIP转换回私网IP,转发给私网EC2

关键区别:互联网只能看到NAT网关的EIP,完全看不到私网EC2的存在。


四、生产环境最佳实践与改进

你的Demo是一个很好的起点,但生产环境还需要做以下优化:

1. 高可用改进

  • 每个可用区部署一个NAT网关:避免单可用区故障导致所有私网资源断网
  • 跨可用区部署应用:将应用和数据库分别部署在两个可用区

2. 安全改进

  • 添加安全组:控制实例级别的入站出站流量
  • 添加网络ACL:控制子网级别的入站出站流量(作为安全组的补充)
  • 关闭公网子网的map_public_ip_on_launch,只给需要的实例手动分配EIP
  • 使用跳板机AWS Systems Manager Session Manager访问私网实例,不要直接暴露SSH端口

3. 可维护性改进

  • 将所有硬编码值抽成变量
  • 添加outputs.tf文件,输出VPC ID、子网ID、NAT网关ID等关键信息
  • 将VPC架构封装成Terraform模块,方便在多个项目中复用
  • 使用Terraform工作区(Workspace)区分开发、测试、生产环境

4. 成本优化

  • NAT网关是按小时和流量收费的,如果私网资源不需要访问互联网,可以不创建NAT网关
  • 开发环境可以使用单个NAT网关,生产环境使用多NAT网关
  • 及时释放未使用的EIP,避免产生不必要的费用

五、常见坑点与排错

  1. EC2实例无法解析域名 :99%是因为VPC的enable_dns_hostnames没开
  2. NAT网关创建失败 :检查是否加了depends_on = [aws_internet_gateway.main-gw]
  3. 公网EC2无法上网:检查公网子网是否关联了公网路由表,公网路由表是否有指向IGW的默认路由
  4. 私网EC2无法上网:检查私网子网是否关联了私网路由表,私网路由表是否有指向NAT网关的默认路由,NAT网关是否在公网子网
  5. Terraform销毁失败:NAT网关必须先销毁才能销毁EIP,Terraform会自动处理,但如果手动删除了资源,会导致销毁失败

六、部署与验证步骤

  1. 安装Terraform CLI和AWS CLI,并配置AWS认证
  2. 创建上述5个文件,放在同一个目录下
  3. 执行terraform init初始化项目
  4. 执行terraform plan查看将要创建的资源
  5. 执行terraform apply确认并创建资源
  6. 验证:
    • 在公网子网启动一个EC2,SSH登录后执行ping 8.8.8.8,应该能通
    • 在私网子网启动一个EC2,通过公网EC2跳板机登录后执行ping 8.8.8.8,应该能通
    • 尝试直接SSH登录私网EC2,应该无法连接

明确结论

你现在的代码里:只有 1 个 NAT Gateway + 1 个 Internet Gateway

总共是 2 个网关 ,但只有 1 个是 NAT


网关讲解

① Internet Gateway(互联网网关)

hcl 复制代码
resource "aws_internet_gateway" "main-gw" {
  vpc_id = aws_vpc.main.id
}

数量:1 个

作用:给整个 VPC 连接互联网,一个 VPC 只需要 1 个


② NAT Gateway(出站网关)

hcl 复制代码
resource "aws_nat_gateway" "nat-gw" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.main-public-1.id  # 只在 1a 区
}

数量:1 个

作用:给两个私网子网(1a + 1b)共用


2. 所以总数是

  • Internet Gateway:1 个
  • NAT Gateway:1 个
    总共 2 个网关

3. 为什么说"只有 1 个 NAT"是问题?

因为你这个 NAT 只部署在 eu-west-1a 可用区:

  • 如果 1a 区故障
  • 你的 两个私网子网(1a + 1b)全部无法访问互联网
    这叫 单点故障,生产环境不允许。

4. 生产标准架构:应该是 2 个 NAT

高可用架构应该是:

  • 1a 区 → 自己的 NAT
  • 1b 区 → 自己的 NAT
    各自私网子网用各自的 NAT,互不影响。

5. 极简总结(一句话)

你现在:
1 IGW + 1 NAT = 2 个网关

NAT 只有 1 个,存在单点故障

先把概念对齐,再说"自带还是要配"。


一、AWS vs 阿里云

AWS

  • Internet Gateway(IGW) :VPC 自带逻辑能力,但必须手动创建这个资源并关联到 VPC,否则 VPC 没有公网出入口。
  • NAT Gateway:必须手动创建、放在公网子网、绑 EIP。

阿里云

  • 没有叫"Internet Gateway"的东西。
  • 对应公网直通能力:默认自带(不需要单独创建网关)
  • 对应集中管控的网关:IPv4网关(需要手动创建+激活)
  • 私网出网:公网NAT网关(必须手动创建)

二、阿里云:默认是"自带公网访问能力",不用配网关

新建一个阿里云 VPC + 交换机(子网):

  • 你直接给 ECS 绑个 公网IP/EIP
  • 就能上网、被外网访问
  • 不需要你创建任何"互联网网关"
    原理:阿里云 VPC 底层默认打通了公网通道,相当于 AWS IGW 默认隐含存在,不需要你手动建。

一句话:
阿里云默认:互联网网关能力是自带的,不用配。


三、什么时候需要手动配"IPv4网关"?

企业要统一管控所有公网出口、路由集中控制、安全合规时:

  • 手动创建 IPv4网关
  • 激活后,整个 VPC 公网流量都走它
  • 路由表要手动加:0.0.0.0/0 → IPv4网关

普通开发/测试、小公司:不用 IPv4 网关,直接 EIP 或 NAT 网关就行。


四、私网要上网:NAT 网关必须手动配

和 AWS 一样:

  • 私网交换机(子网)里的机器不能直接上网
  • 必须手动创建 公网NAT网关
  • 放在一个公网交换机上,绑 EIP
  • 私网路由表:0.0.0.0/0 → NAT网关

五、对照你之前的 Terraform 架构(一句话总结)

AWS:

  • IGW:手动创建
  • NAT:手动创建

阿里云(普通用法,和你 demo 对等):

  • "互联网网关":自带,不用创建
  • NAT网关:手动创建

所以:

  • 阿里云这边你不用写代码创建互联网网关
  • 只需要:VPC + 公网/私网交换机 + NAT网关 + 路由表

相关推荐
运维老郭9 小时前
Kubernetes Pod 从创建到运行全流程拆解:5 个阶段 + 排错实录
运维·云原生·kubernetes
jiayong239 小时前
微服务无感迁移上云方案深度解析
微服务·云原生·架构
JiaWen技术圈9 小时前
使用 Terraform Grafana Provider 实现 Grafana 全栈 IaC 一体化管理的完整方案
云原生·grafana·terraform
爱吃龙利鱼10 小时前
ubuntu2026.04部署k8s1.36版本的傻瓜式教程(注:运行时为docker,网络插件为calico)
运维·网络·笔记·docker·云原生·kubernetes
l167751685412 小时前
天翼云服务器失联排查完整报告_事件报告
运维·服务器·云原生·云计算
yyuuuzz12 小时前
境外云服务器使用常见问题梳理
运维·服务器·网络·aws
古城小栈13 小时前
k8s 存储练习
云原生·容器·kubernetes
无级程序员13 小时前
记一次K8S增加新节点
云原生·容器·kubernetes
wb1891 天前
Kubernetes服务优化
云原生·容器·kubernetes