最后审核时间:2026年5月
使用原生 Terraform 构建 SAA-C03 考试中的 AWS 服务——每次构建一个代码块,并紧扣考试领域。相同的代码可在 OpenTofu 上运行。
在本实验结束时,你将使用纯 Terraform 部署一个教科书式的多可用区高可用性 Web 堆栈——一个包含跨两个可用区的公共和私有子网的自定义 VPC、一个面向互联网的应用程序负载均衡器、一个位于私有层的 EC2 实例的 Auto Scaling 组,以及一个供应用程序通信的多可用区 RDS 数据库。五个步骤对应五个考试支柱。
每个资源都是纯 Terraform——相同的代码在 OpenTofu 上无需修改即可运行。没有变量,没有模块,没有远程状态。将这些代码片段放入一个 main.tf 文件中,运行一次 terraform init,然后逐步运行 terraform apply。
>= 1.5 或 OpenTofu >= 1.6。us-east-1 区域进行身份验证的 AWS CLI(本实验的默认区域——你可以选择任何区域,但步骤 3 中的 AMI 查找假设该区域有 Amazon Linux 2023)。skip_final_snapshot = true,因此销毁时不会留下快照——切勿在生产环境中使用此设置。本实验的大部分内容都属于免费套餐或接近免费套餐:VPC、ALB、ASG、安全组、IAM 和 CloudWatch 监控都是免费的。以下两项在空闲时会产生费用:
如果您对成本敏感,可以在实验中将 db_instance.multi_az = false,并单独查阅多可用区文档。即使您不运行完整的高可用模式,考试仍然会奖励对该属性的了解。完成后请销毁。
我们固定 AWS provider 版本为 ~> 5.60,并选择 us-east-1(任何至少有两个可用区的区域都可以)。data 块在计划时获取可用区列表——硬编码诸如 us-east-1a 之类的可用区名称是 SAA-C03 的反模式,因为可用区名称到物理区域的映射是账户特定的(你的 us-east-1a 可能与我的 us-east-1a 对应不同的物理可用区)。
我们下游创建的所有资源都通过 default_tags 标记回单个项目名称——Cost Explorer + AWS Config 规则都支持标签,考试也期望你对所有内容进行标记。
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.60"
}
}
}
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
Project = "certlabpro-saa-c03"
ManagedBy = "terraform"
}
}
}
data "aws_availability_zones" "available" {
state = "available"
}每一个 SAA-C03 高可用性问题都假设你会将工作负载分散到至少两个可用区。我们创建一个 /16 VPC,并划分为四个 /20 子网——两个公共子网(用于 ALB),两个私有子网(用于 EC2 实例和 RDS)。互联网网关连接到 VPC;公共路由表通过它发送 0.0.0.0/0 的流量。
本实验中的私有子网故意没有 NAT 网关——NAT 网关在空闲时每月收费约 32 美元(每个可用区),SAA-C03 在成本问题中明确测试了这种权衡。如果你的应用程序在生产环境中需要从私有子网访问外部互联网,你会添加一个 NAT(或一个 NAT 实例,这是 SAA-C03 更便宜的替代方案)。
此步骤中的 VPC + 子网 + IGW + 路由表是后续所有内容的基础——ALB 连接到公共子网,ASG 实例位于私有子网中,而步骤 5 中的 RDS 子网组则跨越两个私有可用区。
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = { Name = "certlabpro-saa-c03-vpc" }
}
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = { Name = "certlabpro-saa-c03-public-${count.index}" }
}
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 10}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = { Name = "certlabpro-saa-c03-private-${count.index}" }
}
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
}
}
resource "aws_route_table_association" "public" {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}当问题提到“HTTP / HTTPS”或“基于路径的路由”或“针对特定服务”时,应用程序负载均衡器(Application Load Balancer, ALB)是 SAA-C03 期望你选择的负载均衡器。网络负载均衡器(Network Load Balancers)处理 TCP/UDP 和超低延迟;经典负载均衡器(Classic Load Balancer)是旧版。所以,选择 ALB。
ALB 位于步骤 2 中的两个公共子网中——这就是“多可用区”在结构上的含义。它的安全组接受来自任何地方的 HTTP(端口 80)流量;目标组将从我们在步骤 4 中创建的 ASG 获取成员。健康检查是 ALB 和目标之间的契约——如果 / 返回非 200,ALB 将停止向该实例发送流量。
本实验中没有 HTTPS——那需要 ACM 证书和 DNS 名称,这是我们无需为了展示高可用模式而引入的额外两个服务。在生产环境中请添加 ACM。
resource "aws_security_group" "alb" {
name = "certlabpro-saa-c03-alb"
description = "ALB ingress on port 80 from the internet"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_lb" "web" {
name = "certlabpro-saa-c03-alb"
load_balancer_type = "application"
internal = false
subnets = aws_subnet.public[*].id
security_groups = [aws_security_group.alb.id]
}
resource "aws_lb_target_group" "web" {
name = "certlabpro-saa-c03-tg"
port = 80
protocol = "HTTP"
target_type = "instance"
vpc_id = aws_vpc.main.id
health_check {
path = "/"
matcher = "200-299"
interval = 30
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 3
}
}
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.web.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.web.arn
}
}Auto Scaling 是 AWS 良好架构框架的“弹性”支柱,也是 SAA-C03 考试中仅次于多可用区的第二大考点。结合步骤 3 中的 ALB,ASG 为你提供了容错能力(实例在失败时被替换)和可伸缩性(实例数量随需求变化)——SAA-C03 持续关注的两个结果。
启动模板定义了每个新实例的外观。我们使用最新的 Amazon Linux 2023 AMI、t3.micro 实例类型、一个仅接受来自 ALB SG 流量的安全组(网络层的最小权限),以及一个启动 Nginx 以通过健康检查的最小用户数据脚本。
ASG 将三件事联系起来:实例运行的位置(vpc_zone_identifier = 我们的私有子网,两个可用区),应存在的实例数量(min_size = 2,max_size = 4——两个用于高可用性,留出扩展空间),以及新实例如何加入负载均衡器(target_group_arns)。当 ASG 启动一个实例时,它会自动注册到步骤 3 的目标组中。
resource "aws_security_group" "app" {
name = "certlabpro-saa-c03-app"
description = "App instances accept traffic only from the ALB SG"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
data "aws_ami" "al2023" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-kernel-6.1-x86_64"]
}
}
resource "aws_launch_template" "app" {
name_prefix = "certlabpro-saa-c03-app-"
image_id = data.aws_ami.al2023.id
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.app.id]
metadata_options {
http_tokens = "required"
}
user_data = base64encode(<<-EOT
#!/bin/bash
dnf install -y nginx
systemctl enable --now nginx
EOT
)
}
resource "aws_autoscaling_group" "app" {
name = "certlabpro-saa-c03-app-asg"
vpc_zone_identifier = aws_subnet.private[*].id
target_group_arns = [aws_lb_target_group.web.arn]
health_check_type = "ELB"
min_size = 2
max_size = 4
desired_capacity = 2
launch_template {
id = aws_launch_template.app.id
version = "$Latest"
}
}任何 SAA-C03 多可用区方案的精髓都是一个多可用区 RDS 数据库。设置 multi_az = true 是整个考试中最常考的属性之一:它告诉 RDS 在第二个可用区中预置一个同步备用副本。当主数据库失败时,RDS 会自动提升备用数据库——典型的故障转移需要大约 60 秒,且不会丢失数据。两个单可用区数据库无法提供此功能;手动复制也无法提供此功能。多可用区是解决任何“亚分钟级故障转移,零数据丢失”问题的答案。
数据库子网组需要至少两个可用区中的子网——我们使用步骤 2 中的私有子网。安全组仅接受来自应用安全组的数据库端口(PostgreSQL 为 5432),这反映了步骤 4 的分层安全模式。
完成最后一步后,堆栈就完整了:流量进入任一公共可用区中的 ALB,然后分发到任一私有可用区中的 ASG 实例,这些实例再与 RDS 主数据库通信(主数据库在另一个可用区有一个热备用副本等待)。除了整个区域故障之外,任何情况都有恢复路径。
resource "aws_db_subnet_group" "main" {
name = "certlabpro-saa-c03"
subnet_ids = aws_subnet.private[*].id
}
resource "aws_security_group" "db" {
name = "certlabpro-saa-c03-db"
description = "DB accepts traffic only from the app SG"
vpc_id = aws_vpc.main.id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.app.id]
}
}
resource "aws_db_instance" "main" {
identifier = "certlabpro-saa-c03"
engine = "postgres"
engine_version = "16"
instance_class = "db.t3.micro"
allocated_storage = 20
storage_encrypted = true
multi_az = true # the SAA-C03 headline
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
username = "appuser"
password = "ChangeMe-Not-For-Prod-1234" # use Secrets Manager in production
skip_final_snapshot = true # lab-only setting; never use on prod
apply_immediately = true
}terraform destroy 将销毁本实验中的所有内容。请注意两点:
skip_final_snapshot = true,因此 destroy 会在几分钟内完成,而不会留下快照。切勿在生产数据库上设置此标志——丢失最终快照意味着永久性数据丢失。destroy 直到所有实例都消失后才会完成,这可能需要 2-3 分钟。请耐心等待。Ctrl-C 中断——让它自行完成。SAA-C03 涵盖的架构模式远超任何单个实验所能展示的——包括用于全球内容分发的 CloudFront、用于基于 DNS 故障转移的 Route 53、S3 跨区域复制、Aurora 全球数据库、Direct Connect、VPC 对等连接、Transit Gateway、ElastiCache、EFS、FSx 等等。
我们特意专注于考试中最常考的单一模式:单区域内的多可用区高可用性。这是每个解决方案架构师面试以及 SAA-C03 约 30% 问题中都会出现的架构。其余 70%——全球模式、缓存、高级网络、混合云——在本认证页面的浏览和 Editorial 部分进行了概念性介绍。每个跨区域或高级网络主题都值得单独设立实验;构建一个庞大的堆栈会冲淡本格式所依赖的构建故事的严谨性。
如果你想练习跨区域模式(S3 CRR、Aurora Global、Route 53 健康检查),这些模式也能清晰地映射到相同的纯 Terraform 形式,并且是后续实验的良好候选。