# 使用Terraform在AWS上实践基础设施即代码

简介

通过AWS控制台手动管理云基础设施容易出错且无法扩展。HashiCorp的Terraform让你可以将基础设施定义为代码(IaC),实现版本控制、可重现性和自动化。

本教程将构建一个生产就绪的AWS环境:

  • 带有公共和私有子网的自定义VPC
  • 带安全组的EC2 Web服务器
  • 私有子网中的RDS MySQL数据库
  • 方便访问资源的输出值
  • 前置条件

  • 拥有IAM凭证的AWS账户
  • 已安装Terraform >= 1.6
  • 基本了解AWS网络
  • 步骤1: 安装和配置Terraform

    # 安装Terraform (macOS)
    

    brew tap hashicorp/tap

    brew install hashicorp/tap/terraform

    # 验证安装

    terraform version

    # 配置AWS凭证

    export AWS_ACCESS_KEY_ID="your-access-key"

    export AWS_SECRET_ACCESS_KEY="your-secret-key"

    export AWS_DEFAULT_REGION="ap-northeast-1"

    步骤2: 项目结构

    terraform-aws-demo/
    

    ├── main.tf # 根模块

    ├── variables.tf # 输入变量

    ├── outputs.tf # 输出值

    ├── terraform.tfvars # 变量值

    ├── modules/

    │ ├── vpc/

    │ ├── ec2/

    │ └── rds/

    步骤3: 定义VPC模块

    # modules/vpc/main.tf
    

    resource "aws_vpc" "main" {

    cidr_block = var.vpc_cidr

    enable_dns_hostnames = true

    enable_dns_support = true

    tags = {

    Name = "${var.project_name}-vpc"

    Environment = var.environment

    }

    }

    resource "aws_subnet" "public" {

    count = length(var.public_subnet_cidrs)

    vpc_id = aws_vpc.main.id

    cidr_block = var.public_subnet_cidrs[count.index]

    availability_zone = var.availability_zones[count.index]

    map_public_ip_on_launch = true

    tags = {

    Name = "${var.project_name}-public-${count.index + 1}"

    }

    }

    resource "aws_subnet" "private" {

    count = length(var.private_subnet_cidrs)

    vpc_id = aws_vpc.main.id

    cidr_block = var.private_subnet_cidrs[count.index]

    availability_zone = var.availability_zones[count.index]

    tags = {

    Name = "${var.project_name}-private-${count.index + 1}"

    }

    }

    resource "aws_internet_gateway" "main" {

    vpc_id = aws_vpc.main.id

    }

    resource "aws_route_table" "public" {

    vpc_id = aws_vpc.main.id

    route {

    cidr_block = "0.0.0.0/0"

    gateway_id = aws_internet_gateway.main.id

    }

    }

    步骤4: 定义EC2模块

    # modules/ec2/main.tf
    

    resource "aws_security_group" "web" {

    name_prefix = "${var.project_name}-web-"

    vpc_id = var.vpc_id

    ingress {

    from_port = 80

    to_port = 80

    protocol = "tcp"

    cidr_blocks = ["0.0.0.0/0"]

    }

    ingress {

    from_port = 443

    to_port = 443

    protocol = "tcp"

    cidr_blocks = ["0.0.0.0/0"]

    }

    ingress {

    from_port = 22

    to_port = 22

    protocol = "tcp"

    cidr_blocks = [var.ssh_cidr]

    }

    egress {

    from_port = 0

    to_port = 0

    protocol = "-1"

    cidr_blocks = ["0.0.0.0/0"]

    }

    }

    resource "aws_instance" "web" {

    ami = data.aws_ami.amazon_linux.id

    instance_type = var.instance_type

    subnet_id = var.subnet_id

    vpc_security_group_ids = [aws_security_group.web.id]

    user_data = <<-EOF

    #!/bin/bash

    yum update -y

    yum install -y httpd php php-mysqlnd

    systemctl start httpd

    systemctl enable httpd

    EOF

    root_block_device {

    volume_size = 20

    volume_type = "gp3"

    encrypted = true

    }

    tags = {

    Name = "${var.project_name}-web"

    }

    }

    步骤5: 定义RDS模块

    # modules/rds/main.tf
    

    resource "aws_db_subnet_group" "main" {

    name = "${var.project_name}-db-subnet"

    subnet_ids = var.private_subnet_ids

    }

    resource "aws_security_group" "db" {

    name_prefix = "${var.project_name}-db-"

    vpc_id = var.vpc_id

    ingress {

    from_port = 3306

    to_port = 3306

    protocol = "tcp"

    security_groups = [var.web_sg_id]

    }

    }

    resource "aws_db_instance" "main" {

    identifier = "${var.project_name}-db"

    engine = "mysql"

    engine_version = "8.0"

    instance_class = var.db_instance_class

    allocated_storage = 20

    storage_encrypted = true

    db_name = var.db_name

    username = var.db_username

    password = var.db_password

    db_subnet_group_name = aws_db_subnet_group.main.name

    vpc_security_group_ids = [aws_security_group.db.id]

    skip_final_snapshot = true

    }

    步骤6: 部署基础设施

    # 初始化
    

    terraform init

    # 预览变更

    terraform plan

    # 应用变更

    terraform apply -auto-approve

    # 查看输出

    terraform output

    # 完成后销毁(避免产生费用!)

    terraform destroy -auto-approve

    最佳实践

  • 远程状态: 使用S3 + DynamoDB共享管理状态文件
  • 环境隔离: 通过tfvars文件管理不同环境的配置
  • 代码格式化: 始终运行terraform fmtterraform validate
  • 密钥管理: 不要将密码放在terraform.tfvars中,使用环境变量或AWS Secrets Manager
  • 总结

    本教程涵盖了:

  • 使用可复用模块构建Terraform项目
  • 完整的VPC、EC2、RDS环境搭建
  • IaC最佳实践

Terraform让基础设施具有可重现性和可审计性。结合CI/CD流水线实现完全自动化部署。