「AWS 基础设施实战」

用 Terraform 落地一套真实可用的 AWS 基础设施:VPC/EC2/安全组组合编排、S3 与 CloudFront 静态托管、RDS 与自动备份、IAM 最小权限设计,并给出常见 AWS 排障与治理清单。

1. AWS Provider 配置与认证

一句话总结: AWS Provider 通过 region 与认证链完成初始化,认证推荐 IAM Role(instance profile 或 assume role),密钥优先用环境变量或 SSO 而非硬编码。

AWS 是 Terraform 生态中资源最丰富的 Provider 之一。动手之前先保证认证链路可靠、region 明确、Provider 版本可复现,这三个基础决定后续所有资源的可用性。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.region
}

1.1 认证方式的取舍

方式配置途径适用场景
环境变量AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY本地开发、CI 注入
共享凭据文件~/.aws/credentials单机日常开发
SSO / 临时令牌aws sso login 后由 SDK 自动刷新团队统一身份
Instance Role运行在 EC2 上的 instance_profile云端运行、Pipeline Runner
Assume Rolerole_arn + source_profile跨账户、最小权限切角色

生产环境最推荐 Assume Role + Instance Role 的组合:代码里不出现任何长期密钥,权限由 IAM 策略统一治理。

aws sts get-caller-identity   # 确认认证可用
export AWS_PROFILE=platform
terraform plan

1.2 alias 多区域

AWS 资源常需要跨区域编排(如主区域 + 灾备区域),用 provider "aws" 的 alias 声明多个实例,再由资源显式指定用哪一份配置。

provider "aws" {
  alias  = "us_west"
  region = "us-west-2"
}

resource "aws_s3_bucket" "backup" {
  provider = aws.us_west
  bucket   = "backup-${var.account_id}-${var.environment}"
}

一句话:Provider 是「配置模板」,alias 是「多份模板」,资源通过 provider = aws.alias 选择用哪一份。

2. VPC / EC2 / 安全组组合

一句话总结: VPC 提供网络边界,子网划分公网/私网,安全组是实例级防火墙,三者组合时先规划网段,再让资源通过引用而非硬编码 ID 建立依赖。

一套最小可用的 Web 架构,网络层的正确组合顺序是:VPC → 子网 → 网关 → 路由表 → 安全组 → EC2。

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  tags = { Name = "${var.environment}-vpc" }
}

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "ap-northeast-1a"
  map_public_ip_on_launch = true
  tags = { Name = "${var.environment}-public" }
}

resource "aws_internet_gateway" "gw" {
  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.gw.id
  }
}

2.1 安全组只放行必要端口

安全组遵循最小放行原则:SSH 只对办公网段开放,Web 只开放 80/443。

resource "aws_security_group" "web" {
  name   = "${var.environment}-web-sg"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = var.office_cidrs
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

2.2 EC2 实例与 user_data

EC2 引用 VPC、子网、安全组时一律用属性引用(aws_subnet.public.id),让 Terraform 自动构建依赖图,避免手工填 ID 造成的「删了重建」风险。

data "aws_ami" "ubuntu" {
  most_recent = true
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-22.04-amd64-server-*"]
  }
  owners = ["099720109477"]
}

resource "aws_instance" "web" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.public.id
  vpc_security_group_ids = [aws_security_group.web.id]
  key_name               = var.key_name
  user_data              = <<-EOT
    #!/bin/bash
    apt-get update -y
    apt-get install -y nginx
    systemctl enable nginx
  EOT

  tags = { Name = "${var.environment}-web", Role = "web" }
}

一句话:网络层编排的要诀是「引用不硬编码」——每个 ID 都从上游资源取,删除重建时依赖图会自动按序执行。

3. S3 与 CloudFront 静态托管

一句话总结: S3 负责对象存储,CloudFront 负责 CDN 加速与 HTTPS 终结,两者组合是静态站点/前端资源托管的标配,注意在 S3 上关闭对公网的直接写权限。

静态资源托管把「Web 服务器」换成「对象存储 + CDN」,成本更低、扩展性更好,还能避免 EC2 常驻浪费。

resource "aws_s3_bucket" "static" {
  bucket = "${var.environment}-web-static"
}

resource "aws_s3_bucket_public_access_block" "static" {
  bucket                  = aws_s3_bucket.static.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

3.1 CloudFront 分发

CloudFront 通过 origin 指向 S3 桶,并把默认证书换成自己的 ACM 证书,实现 HTTPS 加速。

resource "aws_cloudfront_distribution" "cdn" {
  enabled             = true
  default_root_object = "index.html"
  aliases             = ["static.example.com"]

  origin {
    domain_name = aws_s3_bucket.static.bucket_regional_domain_name
    origin_id   = aws_s3_bucket.static.id
  }

  default_cache_behavior {
    target_origin_id       = aws_s3_bucket.static.id
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
  }

  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate.main.arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }
}

4. RDS 与备份

一句话总结: RDS 用 aws_db_instance 声明数据库,多可用区与自动备份是默认的安全底线,备份保留期、存储加密与参数组在创建时就要定好。

RDS 部署要点:子网组要覆盖数据库子网、安全组只放行应用端口、开启 backup_retention_period 与 storage_encrypted。

resource "aws_db_subnet_group" "db" {
  name       = "${var.environment}-db-subnet"
  subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
}

resource "aws_security_group" "db" {
  name   = "${var.environment}-db-sg"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = [aws_security_group.web.id]
  }
}

resource "aws_db_instance" "main" {
  identifier             = "${var.environment}-mysql"
  engine                 = "mysql"
  engine_version         = "8.0"
  instance_class         = "db.t3.small"
  allocated_storage      = 50
  multi_az               = true
  db_subnet_group_name   = aws_db_subnet_group.db.name
  vpc_security_group_ids = [aws_security_group.db.id]
  storage_encrypted      = true
  backup_retention_period = 7
  skip_final_snapshot    = false
  final_snapshot_identifier = "${var.environment}-mysql-final"
  username               = var.db_username
  password               = var.db_password
  tags = { Name = "${var.environment}-mysql" }
}

4.1 密码与敏感值

RDS 密码绝不要写死在代码里:用变量 + 敏感标记,或直接从 Secrets Manager 读取。

variable "db_password" {
  type      = string
  sensitive = true
  description = "数据库密码,由 CI 密钥或 Secrets Manager 注入"
}

output "db_endpoint" {
  value     = aws_db_instance.main.endpoint
  sensitive = true
}

一句话:数据库是「不可随意重建」的资源,因此备份、加密、快照这三件事必须在首次创建时配置到位。

5. IAM 最小权限

一句话总结: IAM 最小权限用「角色 + 托管策略 + 信任关系」组合,先给最小动作集,再按需扩展,用条件与资源限定避免权限泛滥。

IAM 是 AWS 安全的核心。原则只有一条:每个身份只拥有完成任务所需的最少权限。

5.1 角色、策略与信任关系

resource "aws_iam_role" "app" {
  name = "${var.environment}-app-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
        Action = "sts:AssumeRole"
      }
    ]
  })
}

resource "aws_iam_policy" "s3_read" {
  name   = "${var.environment}-app-s3-read"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = ["s3:GetObject", "s3:ListBucket"]
        Resource = [
          aws_s3_bucket.static.arn,
          "${aws_s3_bucket.static.arn}/*",
        ]
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "app_s3" {
  role       = aws_iam_role.app.name
  policy_arn = aws_iam_policy.s3_read.arn
}

5.2 权限最小化清单

检查项做法
动作最小化只给 s3:GetObject,不给 s3:*
资源限定Resource 指向具体 ARN 而非 *
条件收紧用 Condition 绑定 VPC、标签、时间
定期回收审计未使用的角色与密钥
避免长期密钥用 Assume Role / 临时令牌

6. 私网出网与分层防护

一句话总结: 安全组、子网、NAT 网关构成 AWS 网络的访问控制矩阵,私网资源出网依赖 NAT,公网入站依赖安全组与网络 ACL 双重把关。

当实例放进私网子网后,它没有公网 IP,出网需要 NAT 网关,入站则只允许来自安全组的流量。

resource "aws_eip" "nat" {
  domain = "vpc"
}

resource "aws_nat_gateway" "main" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.public.id
}

resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id
  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main.id
  }
}

安全组是有状态防火墙(放行的应答流量自动放行),网络 ACL 是无状态防火墙,两者职责不同,不要相互替代;NAT 网关只解决私网出网,入站仍由安全组与 ACL 把关。

7. 常见 AWS 排障与坑

一句话总结: AWS 排障高发区集中在删除顺序、AMI 地域差异、S3 桶名全局唯一、RDS 密码变更触发重建这四类,先看 terraform plan 的 diff 再动手。

坑现象正确做法
删除顺序错误安全组仍被引用导致删除失败依赖图不完整时用 depends_on 显式声明
AMI 跨区域别的区域 ID 找不到AMI 用 data 按名称+拥有者动态查询
桶名冲突BucketAlreadyExists桶名加 account/环境后缀保证全局唯一
RDS 密码变更密码变量变化触发实例重建密码存 Secrets Manager,变更走轮换而非重建
子网扩容子网 CIDR 不可改规划时预留充足网段,避免事后重划

7.1 排查工作流

terraform plan -detailed-exitcode   # 查看真实差异
terraform state show aws_instance.web  # 单资源调试
terraform plan -refresh-only        # 只看刷新后的差异
TF_LOG=DEBUG terraform apply        # 看 provider 日志

8. 总结

一套真实可用的 AWS 基础设施,核心是把网络、计算、存储、数据库、权限五类资源按「引用组合」的方式组织起来:

环节要点
Providerregion + 认证链 + alias 多区域
网络VPC/子网/路由/安全组引用组合
计算EC2 + user_data,AMI 用 data 动态查询
存储S3 + CloudFront,内容上传与 IaC 解耦
数据库RDS 开启多可用区、加密、自动备份
权限IAM 最小权限:角色 + 限定策略 + 条件
治理网络 ACL / 安全组 / IAM 分层把关

一句话收尾:AWS 实战的关键不是「创建资源」,而是「编排资源之间的引用关系与权限边界」。把网段规划、删除顺序、敏感值管理这三件事做对,这套模板就能在 dev/staging/prod 之间稳定复制。下一篇「多环境管理」将讲解如何用 workspace 与目录布局,把这套单环境模板扩成多环境体系。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「terraform」更多文章

  1. 「漂移检测与收敛」
  2. 「资源重构与迁移」
  3. 「数据源与远程数据读取」