Terraform AWS VPC+EC2+EBS数据卷完整Demo详解

这是VPC+EC2架构的存储扩展版,在原有网络和计算资源基础上,新增了独立的EBS数据卷并挂载到EC2实例,实现了计算与存储分离——这是AWS上持久化存储数据的标准方式。

一、核心功能总结

✅ 保留了之前完整的VPC网络架构(公网/私网子网、IGW、NAT网关)
✅ 保留了可SSH登录的公网EC2实例
新增:创建一个20GB的独立EBS数据卷
新增:将EBS卷自动挂载到EC2实例的/dev/xvdh设备
✅ 提供了完整的格式化、挂载、开机自动挂载的手动操作流程

二、整体架构图

互联网
  ↓
Internet Gateway
  ↓
┌─────────────────────────────────────────────────┐
│                  VPC (10.0.0.0/16)              │
│  ┌─────────────┐        ┌─────────────┐         │
│  │ 可用区eu-west-1a │        │ 可用区eu-west-1b │         │
│  │ ┌─────────┐ │        │ ┌─────────┐ │         │
│  │ │公网子网1│ │        │ │公网子网2│ │         │
│  │ │10.0.1.0/24│ │        │ │10.0.2.0/24│ │         │
│  │ └─────────┘ │        │ └─────────┘ │         │
│  │     ↓       │        │             │         │
│  │ ┌─────────┐ │        │             │         │
│  │ │EC2实例  │ │        │             │         │
│  │ │(根卷8GB)│ │        │             │         │
│  │ └─────────┘ │        │             │         │
│  │     ↓       │        │             │         │
│  │ ┌─────────┐ │        │             │         │
│  │ │EBS数据卷│ │        │             │         │
│  │ │(20GB)   │ │        │             │         │
│  │ └─────────┘ │        │             │         │
│  └─────────────┘        └─────────────┘         │
└─────────────────────────────────────────────────┘

关键区别:EC2实例自带一个根卷(默认8GB,用于安装操作系统),而我们新增的是独立的数据卷,专门用于存储业务数据。


三、核心代码逐行解析(instance.tf)

1. 原有EC2实例代码(无变化)

resource "aws_instance" "example" {
  ami           = var.AMIS[var.AWS_REGION]  # 从变量中获取对应区域的Ubuntu AMI
  instance_type = "t2.micro"                # 免费套餐可用
  subnet_id     = aws_subnet.main-public-1.id  # 部署在eu-west-1a公网子网
  vpc_security_group_ids = [aws_security_group.allow-ssh.id]  # 关联SSH安全组
  key_name      = aws_key_pair.mykeypair.key_name  # 关联SSH密钥对
}

2. 新增:创建独立EBS数据卷

resource "aws_ebs_volume" "ebs-volume-1" {
  availability_zone = "eu-west-1a"  # 🔥 必须和EC2实例在同一个可用区!
  size              = 20            # 卷大小,单位GB
  type              = "gp2"         # 卷类型,通用型SSD
  tags = {
    Name = "extra volume data"
  }
}

关键参数详解

  • availability_zone最重要的参数。EBS是可用区级别的资源,只能挂载到同一个可用区的EC2实例上。如果这里写eu-west-1b,而实例在eu-west-1a,挂载会直接失败。
  • size:卷的大小,gp2类型最小1GB,最大16TB。
  • type:卷类型,决定了性能和价格:
    • gp2:旧版通用型SSD,已不推荐
    • gp3:新版通用型SSD,价格比gp2低20%,性能更高,生产环境必须用这个
    • io1/io2:高性能SSD,用于数据库等IO密集型应用
    • st1/sc1:吞吐量优化型HDD,用于大数据仓库

3. 新增:将EBS卷挂载到EC2实例

resource "aws_volume_attachment" "ebs-volume-1-attachment" {
  device_name = "/dev/xvdh"  # 设备名,AWS推荐使用/dev/xvdh到/dev/xvdz
  volume_id   = aws_ebs_volume.ebs-volume-1.id  # 要挂载的EBS卷ID
  instance_id = aws_instance.example.id        # 要挂载到的EC2实例ID
}

关键参数详解

  • device_name:Linux系统中识别这个卷的设备路径。
    • 系统根卷通常是/dev/xvda
    • AWS推荐从/dev/xvdh开始使用,避免和系统保留的设备名冲突
    • 注意:在某些Linux发行版中,实际显示的设备名可能是/dev/nvme1n1(Nitro实例),但挂载时写/dev/xvdh仍然有效

四、完整部署与验证流程

1. 部署所有资源

cd demo-9_EBS
terraform init   # 初始化
terraform plan   # 预览
terraform apply  # 确认部署

Terraform会自动按顺序创建:VPC → 子网 → IGW → NAT → 安全组 → 密钥对 → EC2实例 → EBS卷 → 挂载卷。

2. SSH登录实例

# 获取实例公网IP
terraform output ec2_public_ip

# 登录
ssh -i mykey ubuntu@<实例公网IP>

3. 验证EBS卷已挂载到系统

lsblk

预期输出

NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0   8G  0 disk 
└─xvda1 202:1    0   8G  0 part /
xvdf    202:80   0  20G  0 disk  # 这就是我们新增的EBS卷

注意:这里显示的是xvdf而不是xvdh是正常现象,不同实例类型的设备名映射可能不同。

4. 格式化EBS卷(仅第一次需要)

新创建的EBS卷是空白的,没有文件系统,必须格式化后才能使用

sudo mkfs -t ext4 /dev/xvdf

警告:格式化会删除卷上的所有数据,已经有数据的卷不要执行这个命令!

5. 创建挂载点并挂载

# 创建挂载目录
sudo mkdir /mnt/data

# 将卷挂载到/mnt/data目录
sudo mount /dev/xvdf /mnt/data

6. 验证挂载成功

df -h

预期输出

Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1      7.7G  1.3G  6.5G  17% /
/dev/xvdf        20G   45M   19G   1% /mnt/data  # 挂载成功

7. 测试写入数据

echo "Hello EBS!" | sudo tee /mnt/data/test.txt
cat /mnt/data/test.txt

如果能正常输出"Hello EBS!",说明卷可以正常读写。

8. 配置开机自动挂载(可选但强烈推荐)

如果不配置,重启实例后挂载会消失,需要重新手动挂载。

# 将挂载信息写入/etc/fstab文件
echo '/dev/xvdf /mnt/data ext4 defaults,nofail 0 2' | sudo tee -a /etc/fstab

# 验证fstab配置是否正确(非常重要!)
sudo mount -a

关键参数说明

  • nofail最重要的参数。如果没有这个参数,当EBS卷挂载失败时,实例会无法启动!
  • 0 2:dump和fsck选项,数据卷一般设置为0 2。

五、EBS核心概念与原理

1. 根卷 vs 数据卷

特性根卷数据卷
用途安装操作系统存储业务数据
创建方式随EC2实例自动创建手动独立创建
生命周期默认随实例终止而删除默认独立于实例,实例终止后卷仍然存在
大小默认8GB,可调整自定义,1GB-16TB

2. EBS的持久性

EBS卷的数据会自动在同一个可用区内复制三份,可用性高达99.999%。即使EC2实例故障或被终止,EBS卷上的数据仍然安全,可以挂载到其他实例上。

3. 为什么需要独立的数据卷?

  • 数据安全:实例终止时,根卷会被删除,但数据卷会保留
  • 灵活扩展:可以随时扩容数据卷,不需要重建实例
  • 性能隔离:数据卷的IO性能不会和根卷竞争
  • 备份方便:可以单独对数据卷创建快照备份

六、生产环境最佳实践

  1. 使用gp3代替gp2:gp3价格更低,性能更高,是当前的默认推荐类型

    type = "gp3"
    
  2. 明确设置卷的删除策略:默认情况下,数据卷在实例终止时会保留。如果希望实例终止时自动删除数据卷,可以添加:

    resource "aws_volume_attachment" "ebs-volume-1-attachment" {
      # ... 其他参数
      delete_on_termination = true
    }
    
  3. 不要硬编码可用区:将可用区抽成变量,避免和实例的可用区不一致:

    resource "aws_ebs_volume" "ebs-volume-1" {
      availability_zone = aws_instance.example.availability_zone
      # ... 其他参数
    }
    

    这样无论实例部署在哪个可用区,EBS卷都会自动跟随。

  4. 定期创建快照备份:EBS快照是增量备份,成本很低,可以有效防止数据丢失。

  5. 对于数据库等IO密集型应用:使用io2卷,并配置适当的IOPS。


七、常见坑点与排错

  1. 挂载失败:VolumeInUse:EBS卷已经挂载到其他实例上,或者之前的挂载没有正常卸载。
  2. 挂载失败:InvalidVolume.ZoneMismatch:EBS卷和EC2实例不在同一个可用区。
  3. 重启实例后挂载消失:没有配置/etc/fstab自动挂载。
  4. 实例无法启动:fstab配置错误,特别是没有加nofail参数。
  5. 无法写入数据:没有格式化卷,或者挂载目录权限不正确。

八、进阶改进

  1. 添加outputs.tf输出EBS卷ID和挂载点信息
  2. 使用user_data自动完成格式化和挂载操作,不需要手动执行命令
  3. 配置EBS卷的自动快照策略
  4. 实现EBS卷的在线扩容(不需要停机)

更多推荐