Terraform:构建高可用网站
description = "AWS区域"description = "VPC CIDR块"description = "公网子网CIDR列表"description = "应用子网CIDR列表"description = "数据库子网CIDR列表"description = "数据库管理员用户名"description = "数据库管理员密码"sensitive = true # 标记为敏感,不
生产级完整案例:高可用WordPress网站
这是一个整合了你之前学过的所有知识点的完整生产级案例,将部署一个真正可以对外提供服务的高可用WordPress网站。通过这个案例,你会把之前零散的知识点串联起来,真正理解如何用Terraform构建一个完整的企业级应用。
一、整体架构设计
我们将部署一个标准的三层架构,所有核心服务都部署在私网子网,只有负载均衡器暴露在公网:
互联网
↓
ALB应用负载均衡器(跨两个可用区)
↓
┌─────────────────────────────────────────────────────────────┐
│ 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 │ │ │ │ NAT网关2 │ │ │
│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
│ │ ↓ │ │ ↓ │ │
│ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │
│ │ │ 应用子网1 │ │ │ │ 应用子网2 │ │ │
│ │ │ 10.0.4.0/24 │ │ │ │ 10.0.5.0/24 │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ WordPress实例1 │ │ │ │ WordPress实例2 │ │ │
│ │ │ (Auto Scaling) │ │ │ │ (Auto Scaling) │ │ │
│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
│ │ ↓ │ │ ↓ │ │
│ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │
│ │ │ 数据子网1 │ │ │ │ 数据子网2 │ │ │
│ │ │ 10.0.8.0/24 │ │ │ │ 10.0.9.0/24 │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ RDS主节点 │ │ │ │ RDS备用节点 │ │ │
│ │ │ (多可用区) │ │ │ │ (自动故障转移) │ │ │
│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
│ └─────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
核心特性:
✅ 完全网络分层:公网层、应用层、数据层
✅ 双NAT网关高可用:每个可用区一个NAT网关
✅ 应用层自动扩缩容:根据CPU使用率自动调整实例数量
✅ 数据库多可用区:自动故障转移,RTO约1-2分钟
✅ 应用层无状态:所有数据存在数据库,实例可以随时创建和销毁
✅ 最小权限安全:所有核心服务都在私网,只有ALB暴露在公网
二、完整文件结构
wordpress-terraform/
├── versions.tf # 版本约束
├── provider.tf # AWS提供商配置
├── variables.tf # 变量定义
├── outputs.tf # 输出值
├── locals.tf # 本地变量
├── vpc.tf # VPC网络架构
├── nat.tf # NAT网关
├── securitygroup.tf # 安全组
├── rds.tf # RDS数据库
├── alb.tf # ALB应用负载均衡器
├── autoscaling.tf # Auto Scaling自动扩缩容
└── userdata.sh # CloudInit初始化脚本
三、逐文件详细解析
1. versions.tf(版本约束)
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
2. provider.tf(AWS配置)
provider "aws" {
region = var.aws_region
}
3. variables.tf(变量定义)
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 "app_subnet_cidrs" {
description = "应用子网CIDR列表"
type = list(string)
default = ["10.0.4.0/24", "10.0.5.0/24"]
}
variable "db_subnet_cidrs" {
description = "数据库子网CIDR列表"
type = list(string)
default = ["10.0.8.0/24", "10.0.9.0/24"]
}
variable "db_username" {
description = "数据库管理员用户名"
type = string
default = "wpadmin"
}
variable "db_password" {
description = "数据库管理员密码"
type = string
sensitive = true # 标记为敏感,不会在输出中显示
}
variable "instance_type" {
description = "应用服务器实例类型"
type = string
default = "t3.micro"
}
variable "min_instances" {
description = "最小应用服务器数量"
type = number
default = 2
}
variable "max_instances" {
description = "最大应用服务器数量"
type = number
default = 4
}
4. locals.tf(本地变量)
locals {
# 公共标签,所有资源都使用
common_tags = {
Project = "wordpress"
Environment = "production"
ManagedBy = "terraform"
}
# 可用区列表
availability_zones = ["eu-west-1a", "eu-west-1b"]
}
5. vpc.tf(VPC网络架构)
# 创建VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = merge(local.common_tags, {
Name = "wordpress-vpc"
})
}
# 创建公网子网
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 = local.availability_zones[count.index]
map_public_ip_on_launch = true
tags = merge(local.common_tags, {
Name = "wordpress-public-${count.index + 1}"
})
}
# 创建应用子网(私网)
resource "aws_subnet" "app" {
count = length(var.app_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.app_subnet_cidrs[count.index]
availability_zone = local.availability_zones[count.index]
map_public_ip_on_launch = false
tags = merge(local.common_tags, {
Name = "wordpress-app-${count.index + 1}"
})
}
# 创建数据库子网(私网)
resource "aws_subnet" "db" {
count = length(var.db_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.db_subnet_cidrs[count.index]
availability_zone = local.availability_zones[count.index]
map_public_ip_on_launch = false
tags = merge(local.common_tags, {
Name = "wordpress-db-${count.index + 1}"
})
}
# 创建Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = merge(local.common_tags, {
Name = "wordpress-igw"
})
}
# 公网路由表
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
}
tags = merge(local.common_tags, {
Name = "wordpress-public-rt"
})
}
# 关联公网子网到公网路由表
resource "aws_route_table_association" "public" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
6. nat.tf(双NAT网关高可用)
# 为每个可用区创建一个EIP和NAT网关
resource "aws_eip" "nat" {
count = length(aws_subnet.public)
vpc = true
tags = merge(local.common_tags, {
Name = "wordpress-nat-eip-${count.index + 1}"
})
}
resource "aws_nat_gateway" "main" {
count = length(aws_subnet.public)
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
depends_on = [aws_internet_gateway.main]
tags = merge(local.common_tags, {
Name = "wordpress-nat-${count.index + 1}"
})
}
# 应用子网路由表(指向本可用区的NAT网关)
resource "aws_route_table" "app" {
count = length(aws_subnet.app)
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = merge(local.common_tags, {
Name = "wordpress-app-rt-${count.index + 1}"
})
}
# 关联应用子网到应用路由表
resource "aws_route_table_association" "app" {
count = length(aws_subnet.app)
subnet_id = aws_subnet.app[count.index].id
route_table_id = aws_route_table.app[count.index].id
}
# 数据库子网路由表(指向本可用区的NAT网关)
resource "aws_route_table" "db" {
count = length(aws_subnet.db)
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = merge(local.common_tags, {
Name = "wordpress-db-rt-${count.index + 1}"
})
}
# 关联数据库子网到数据库路由表
resource "aws_route_table_association" "db" {
count = length(aws_subnet.db)
subnet_id = aws_subnet.db[count.index].id
route_table_id = aws_route_table.db[count.index].id
}
关键设计:每个可用区的应用子网和数据库子网都指向本可用区的NAT网关。这样即使一个可用区故障,另一个可用区的流量仍然可以正常上网。
7. securitygroup.tf(安全组,最小权限原则)
# ALB安全组:只允许80端口从互联网访问
resource "aws_security_group" "alb" {
name = "wordpress-alb"
description = "Allow HTTP access from 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"]
}
tags = local.common_tags
}
# 应用服务器安全组:只允许来自ALB的80端口和SSH访问
resource "aws_security_group" "app" {
name = "wordpress-app"
description = "Allow HTTP from ALB and SSH from anywhere"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # 生产环境请改为你的办公IP
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = local.common_tags
}
# 数据库安全组:只允许来自应用服务器的3306端口访问
resource "aws_security_group" "db" {
name = "wordpress-db"
description = "Allow MySQL access from app servers"
vpc_id = aws_vpc.main.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.app.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = local.common_tags
}
安全设计亮点:
- 数据库的3306端口完全不开放给互联网,只允许来自应用服务器安全组的流量
- 应用服务器的80端口只允许来自ALB的流量,用户无法直接访问应用服务器
- 所有流量必须经过ALB转发,形成了一个安全的多层防御体系
8. rds.tf(RDS数据库多可用区)
# 创建RDS子网组
resource "aws_db_subnet_group" "wordpress" {
name = "wordpress-db-subnet-group"
subnet_ids = aws_subnet.db[*].id
tags = local.common_tags
}
# 创建RDS参数组
resource "aws_db_parameter_group" "wordpress" {
name = "wordpress-parameter-group"
family = "mysql8.0"
parameter {
name = "character_set_server"
value = "utf8mb4"
}
parameter {
name = "collation_server"
value = "utf8mb4_unicode_ci"
}
tags = local.common_tags
}
# 创建RDS数据库实例(多可用区)
resource "aws_db_instance" "wordpress" {
identifier = "wordpress-db"
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.small"
allocated_storage = 20
max_allocated_storage = 100
db_name = "wordpress"
username = var.db_username
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.wordpress.name
parameter_group_name = aws_db_parameter_group.wordpress.name
vpc_security_group_ids = [aws_security_group.db.id]
multi_az = true # 启用多可用区高可用
storage_type = "gp3"
storage_encrypted = true # 启用存储加密
backup_retention_period = 7
skip_final_snapshot = false
final_snapshot_identifier = "wordpress-db-final-snapshot"
tags = local.common_tags
}
生产级配置:
- 启用多可用区:自动故障转移,保证数据库高可用
- 启用存储加密:加密数据库静态数据
- 启用自动存储扩展:当存储使用率达到80%时自动扩容
- 保留7天自动备份:可以恢复到过去7天内的任意时间点
- 删除时创建最终快照:防止误删数据
9. alb.tf(ALB应用负载均衡器)
# 创建ALB
resource "aws_lb" "wordpress" {
name = "wordpress-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = aws_subnet.public[*].id
enable_deletion_protection = false
tags = local.common_tags
}
# 创建目标组
resource "aws_lb_target_group" "wordpress" {
name = "wordpress-target-group"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "instance"
health_check {
path = "/"
port = "traffic-port"
protocol = "HTTP"
matcher = "200"
interval = 30
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 2
}
tags = local.common_tags
}
# 创建监听器
resource "aws_lb_listener" "wordpress" {
load_balancer_arn = aws_lb.wordpress.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.wordpress.arn
}
}
10. autoscaling.tf(Auto Scaling自动扩缩容)
# 创建启动模板
resource "aws_launch_template" "wordpress" {
name_prefix = "wordpress-launch-template"
image_id = "ami-0c55b159cbfafe1f0" # Ubuntu 20.04 LTS (eu-west-1)
instance_type = var.instance_type
key_name = "mykey" # 替换为你的密钥对名称
security_group_ids = [aws_security_group.app.id]
# CloudInit初始化脚本
user_data = base64encode(templatefile("userdata.sh", {
DB_HOST = aws_db_instance.wordpress.endpoint
DB_NAME = aws_db_instance.wordpress.db_name
DB_USER = aws_db_instance.wordpress.username
DB_PASSWORD = aws_db_instance.wordpress.password
}))
tag_specifications {
resource_type = "instance"
tags = merge(local.common_tags, {
Name = "wordpress-app-server"
})
}
}
# 创建Auto Scaling组
resource "aws_autoscaling_group" "wordpress" {
name = "wordpress-asg"
min_size = var.min_instances
max_size = var.max_instances
desired_capacity = var.min_instances
vpc_zone_identifier = aws_subnet.app[*].id
health_check_grace_period = 300
health_check_type = "ELB"
launch_template {
id = aws_launch_template.wordpress.id
version = "$Latest"
}
target_group_arns = [aws_lb_target_group.wordpress.arn]
tag {
key = "Name"
value = "wordpress-app-server"
propagate_at_launch = true
}
}
# 目标跟踪扩展策略:将平均CPU使用率维持在70%
resource "aws_autoscaling_policy" "cpu_tracking" {
name = "wordpress-cpu-tracking"
autoscaling_group_name = aws_autoscaling_group.wordpress.name
policy_type = "TargetTrackingScaling"
target_tracking_configuration {
predefined_metric_specification {
predefined_metric_type = "ASGAverageCPUUtilization"
}
target_value = 70.0
}
}
关键改进:
- 使用启动模板代替启动配置(AWS推荐)
- 使用目标跟踪扩展策略代替简单扩展策略,更智能
- 自动将数据库连接信息注入到User Data中,不需要手动配置
11. userdata.sh(CloudInit初始化脚本)
#!/bin/bash
set -ex
# 更新系统
apt-get update
apt-get upgrade -y
# 安装Apache、PHP和MySQL客户端
apt-get install -y apache2 php php-mysql mysql-client
# 下载并解压WordPress
cd /var/www/html
rm index.html
wget https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
mv wordpress/* .
rm -rf wordpress latest.tar.gz
# 配置wp-config.php
cp wp-config-sample.php wp-config.php
sed -i "s/database_name_here/${DB_NAME}/g" wp-config.php
sed -i "s/username_here/${DB_USER}/g" wp-config.php
sed -i "s/password_here/${DB_PASSWORD}/g" wp-config.php
sed -i "s/localhost/${DB_HOST}/g" wp-config.php
# 设置文件权限
chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html
# 启用Apache重写模块
a2enmod rewrite
systemctl restart apache2
作用:实例启动时自动完成所有软件安装和WordPress配置,不需要手动操作。新创建的实例会自动加入集群并开始处理流量。
12. outputs.tf(输出关键信息)
output "alb_dns_name" {
description = "ALB DNS名称"
value = aws_lb.wordpress.dns_name
}
output "db_endpoint" {
description = "RDS数据库端点"
value = aws_db_instance.wordpress.endpoint
sensitive = true
}
四、完整部署步骤
1. 准备工作
# 生成SSH密钥对
ssh-keygen -t rsa -b 2048 -f mykey -N ""
# 导入密钥对到AWS
aws ec2 import-key-pair --key-name mykey --public-key-material file://mykey.pub
# 设置数据库密码环境变量
export TF_VAR_db_password="your-strong-password-123"
2. 部署所有资源
terraform init
terraform plan
terraform apply
⚠️ 注意:RDS创建需要10-15分钟,请耐心等待。
3. 访问WordPress
部署完成后,Terraform会输出ALB的DNS名称:
Outputs:
alb_dns_name = "wordpress-alb-123456789.eu-west-1.elb.amazonaws.com"
在浏览器中输入这个地址,就可以看到WordPress的安装界面,按照提示完成安装即可。
4. 验证高可用
# 查看当前运行的实例
aws ec2 describe-instances --filters "Name=tag:Name,Values=wordpress-app-server" --query "Reservations[*].Instances[*].{ID:InstanceId, AZ:Placement.AvailabilityZone}"
# 手动终止一个实例(模拟故障)
aws ec2 terminate-instances --instance-ids <实例ID>
# 继续访问ALB,服务不会中断
# 大约5分钟后,Auto Scaling会自动创建一个新的实例替换被终止的实例
五、生产环境进一步优化
这个案例已经是一个非常不错的生产级架构,但还可以做以下优化:
- 配置HTTPS:使用ACM证书配置HTTPS,将HTTP流量重定向到HTTPS
- 添加CDN:使用CloudFront加速静态资源
- 添加Redis缓存:使用ElastiCache Redis加速数据库查询
- 配置S3存储:将WordPress的媒体文件存储在S3中
- 配置监控和告警:使用CloudWatch监控所有资源的性能和健康状态
- 配置备份策略:定期备份数据库和应用数据
- 启用WAF:使用AWS WAF防护Web攻击
六、总结
这个案例整合了你之前学过的所有知识点:
- VPC网络分层与高可用设计
- NAT网关与路由表配置
- 安全组最小权限原则
- RDS数据库多可用区部署
- ALB负载均衡器与健康检查
- Auto Scaling自动扩缩容
- CloudInit自动初始化
通过这个案例,你应该已经掌握了如何用Terraform构建一个完整的生产级Web应用。这是一个非常重要的里程碑,意味着你已经可以独立完成企业级基础设施的设计和部署。
生产级通用高可用网站完整案例(全链路HTTPS+CDN+WAF)
我给你做一个最通用、最完善的现代Web应用架构,不绑定任何特定CMS,你可以直接把自己的前端/后端代码放进去用。这个架构是目前全球互联网公司的标准配置,包含了所有生产环境必备的组件:全链路HTTPS、CloudFront CDN加速、WAF Web防火墙、静态资源分离、自动扩缩容、多可用区高可用。
一、最终生产级架构(四层架构)
互联网
↓
CloudFront CDN(全球加速 + WAF防护)
↓
ALB应用负载均衡器(HTTPS终结 + 流量分发)
↓
┌─────────────────────────────────────────────────────────────┐
│ 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 │ │ │ │ NAT网关2 │ │ │
│ │ │ ALB节点1 │ │ │ │ ALB节点2 │ │ │
│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
│ │ ↓ │ │ ↓ │ │
│ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │
│ │ │ 应用子网1 │ │ │ │ 应用子网2 │ │ │
│ │ │ 10.0.4.0/24 │ │ │ │ 10.0.5.0/24 │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ 应用实例1 │ │ │ │ 应用实例2 │ │ │
│ │ │ (Auto Scaling) │ │ │ │ (Auto Scaling) │ │ │
│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
│ └─────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
S3存储桶(静态资源:JS/CSS/图片/视频)
核心生产级特性:
✅ 全链路HTTPS:从用户浏览器到CDN到ALB全程加密
✅ CloudFront全球CDN:静态资源全球加速,降低源站压力
✅ AWS WAF防护:自动阻挡SQL注入、XSS、DDoS等常见Web攻击
✅ 静态动态分离:静态资源走S3+CDN,动态请求走ALB+应用服务器
✅ 应用层无状态:实例可以随时创建销毁,支持无限水平扩展
✅ 基于请求数的自动扩缩容:比CPU使用率更适合Web应用
✅ 零公网IP:所有应用服务器都在私网,完全不暴露给互联网
✅ 优雅关闭与滚动更新:更新应用时用户完全无感知
✅ 完整监控告警:自动监控请求数、延迟、错误率等核心指标
二、完整文件结构
production-website-terraform/
├── versions.tf # 版本约束
├── provider.tf # AWS提供商配置(双区域:eu-west-1 + us-east-1)
├── variables.tf # 变量定义
├── outputs.tf # 输出值
├── locals.tf # 本地变量
├── vpc.tf # VPC网络架构(双NAT高可用)
├── securitygroup.tf # 安全组(最小权限)
├── acm.tf # ACM SSL证书(HTTPS)
├── alb.tf # ALB负载均衡器(HTTPS终结)
├── autoscaling.tf # Auto Scaling自动扩缩容
├── s3.tf # S3静态资源存储桶
├── cloudfront.tf # CloudFront CDN配置
├── waf.tf # AWS WAF Web防火墙
└── userdata.sh # 应用初始化脚本
三、核心文件逐行解析
1. provider.tf(双区域配置,关键!)
注意:CloudFront使用的ACM证书必须在us-east-1区域申请,所以我们需要配置两个AWS Provider:
# 主区域:部署所有资源
provider "aws" {
region = var.aws_region
alias = "main"
}
# us-east-1区域:仅用于申请CloudFront的ACM证书
provider "aws" {
region = "us-east-1"
alias = "us-east-1"
}
2. variables.tf(核心变量)
variable "aws_region" {
description = "主AWS区域"
type = string
default = "eu-west-1"
}
variable "domain_name" {
description = "你的网站域名(如example.com)"
type = string
default = "your-domain.com" # 替换成你自己的域名
}
variable "app_port" {
description = "应用监听端口"
type = number
default = 3000
}
variable "min_instances" {
description = "最小应用实例数"
type = number
default = 2
}
variable "max_instances" {
description = "最大应用实例数"
type = number
default = 10
}
variable "target_requests_per_instance" {
description = "每个实例每秒处理的目标请求数"
type = number
default = 100
}
3. acm.tf(ACM SSL证书,自动验证)
# 在us-east-1区域申请证书,用于CloudFront
resource "aws_acm_certificate" "cloudfront" {
provider = aws.us-east-1
domain_name = var.domain_name
validation_method = "DNS"
subject_alternative_names = [
"www.${var.domain_name}"
]
lifecycle {
create_before_destroy = true
}
}
# 自动添加DNS验证记录(需要你的域名在Route53托管)
resource "aws_route53_record" "cert_validation" {
for_each = {
for dvo in aws_acm_certificate.cloudfront.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = aws_route53_zone.main.zone_id
}
# 等待证书验证完成
resource "aws_acm_certificate_validation" "cloudfront" {
provider = aws.us-east-1
certificate_arn = aws_acm_certificate.cloudfront.arn
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
# 在主区域申请证书,用于ALB
resource "aws_acm_certificate" "alb" {
provider = aws.main
domain_name = var.domain_name
validation_method = "DNS"
subject_alternative_names = ["www.${var.domain_name}"]
lifecycle { create_before_destroy = true }
}
resource "aws_route53_record" "alb_cert_validation" {
# 同上,省略DNS验证记录
}
resource "aws_acm_certificate_validation" "alb" {
certificate_arn = aws_acm_certificate.alb.arn
validation_record_fqdns = [for record in aws_route53_record.alb_cert_validation : record.fqdn]
}
4. alb.tf(HTTPS ALB,自动重定向HTTP到HTTPS)
# 创建ALB
resource "aws_lb" "main" {
name = "website-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = aws_subnet.public[*].id
enable_deletion_protection = true # 防止误删
tags = local.common_tags
}
# HTTP监听器:自动重定向到HTTPS
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.main.arn
port = 80
protocol = "HTTP"
default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}
# HTTPS监听器
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.main.arn
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01" # 现代TLS策略
certificate_arn = aws_acm_certificate.alb.arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.app.arn
}
}
# 目标组
resource "aws_lb_target_group" "app" {
name = "app-target-group"
port = var.app_port
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "instance"
deregistration_delay = 300 # 连接排空时间
health_check {
path = "/health" # 应用健康检查端点
port = "traffic-port"
protocol = "HTTP"
matcher = "200"
interval = 15
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 3
}
tags = local.common_tags
}
5. autoscaling.tf(基于请求数的自动扩缩容)
# 启动模板
resource "aws_launch_template" "app" {
name_prefix = "app-launch-template"
image_id = "ami-0c55b159cbfafe1f0" # Ubuntu 20.04 LTS
instance_type = "t3.small"
iam_instance_profile {
name = aws_iam_instance_profile.app.name # 授予S3访问权限
}
security_group_ids = [aws_security_group.app.id]
user_data = base64encode(templatefile("userdata.sh", {
S3_BUCKET = aws_s3_bucket.static.bucket
}))
tag_specifications {
resource_type = "instance"
tags = local.common_tags
}
}
# Auto Scaling组
resource "aws_autoscaling_group" "app" {
name = "app-asg"
min_size = var.min_instances
max_size = var.max_instances
desired_capacity = var.min_instances
vpc_zone_identifier = aws_subnet.app[*].id
health_check_grace_period = 300
health_check_type = "ELB"
launch_template {
id = aws_launch_template.app.id
version = "$Latest"
}
target_group_arns = [aws_lb_target_group.app.arn]
# 实例刷新配置:滚动更新
instance_refresh {
strategy = "Rolling"
preferences {
min_healthy_percentage = 50
}
}
tag {
key = "Name"
value = "app-server"
propagate_at_launch = true
}
}
# 目标跟踪扩展策略:基于ALB请求数
resource "aws_autoscaling_policy" "request_count" {
name = "request-count-tracking"
autoscaling_group_name = aws_autoscaling_group.app.name
policy_type = "TargetTrackingScaling"
target_tracking_configuration {
predefined_metric_specification {
predefined_metric_type = "ALBRequestCountPerTarget"
resource_label = "${aws_lb.main.arn}/${aws_lb_target_group.app.arn}"
}
target_value = var.target_requests_per_instance * 60 # 转换为每分钟请求数
}
}
6. s3.tf(静态资源存储桶)
# 静态资源存储桶
resource "aws_s3_bucket" "static" {
bucket = "${var.domain_name}-static-assets"
acl = "private"
cors_rule {
allowed_headers = ["*"]
allowed_methods = ["GET"]
allowed_origins = ["https://${var.domain_name}"]
max_age_seconds = 3000
}
tags = local.common_tags
}
# 允许CloudFront访问S3
resource "aws_s3_bucket_policy" "static" {
bucket = aws_s3_bucket.static.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = { AWS = aws_cloudfront_origin_access_identity.main.iam_arn }
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.static.arn}/*"
}
]
})
}
7. cloudfront.tf(CloudFront CDN配置)
# 源访问身份:允许CloudFront访问私有S3桶
resource "aws_cloudfront_origin_access_identity" "main" {
comment = "CloudFront OAI for ${var.domain_name}"
}
# CloudFront分发
resource "aws_cloudfront_distribution" "main" {
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
price_class = "PriceClass_100" # 北美+欧洲,成本最低
# 动态请求源:ALB
origin {
domain_name = aws_lb.main.dns_name
origin_id = "ALB-${var.domain_name}"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
# 静态资源源:S3
origin {
domain_name = aws_s3_bucket.static.bucket_regional_domain_name
origin_id = "S3-${var.domain_name}"
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.main.cloudfront_access_identity_path
}
}
# 默认缓存行为:转发到ALB
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD", "OPTIONS"]
target_origin_id = "ALB-${var.domain_name}"
forwarded_values {
query_string = true
cookies { forward = "all" }
headers {
items = ["Host", "User-Agent", "Accept"]
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 0
max_ttl = 0
}
# 静态资源缓存行为:直接从S3返回
ordered_cache_behavior {
path_pattern = "/static/*"
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD", "OPTIONS"]
target_origin_id = "S3-${var.domain_name}"
forwarded_values {
query_string = false
cookies { forward = "none" }
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 86400 # 缓存1天
default_ttl = 86400
max_ttl = 31536000
}
# SSL配置
viewer_certificate {
acm_certificate_arn = aws_acm_certificate.cloudfront.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
# 关联WAF
web_acl_id = aws_wafv2_web_acl.main.arn
tags = local.common_tags
}
8. waf.tf(AWS WAF Web防火墙)
# WAF Web ACL
resource "aws_wafv2_web_acl" "main" {
name = "website-waf-acl"
description = "WAF for ${var.domain_name}"
scope = "CLOUDFRONT" # 关联到CloudFront
# AWS托管规则组:核心规则集(阻挡90%的常见攻击)
rule {
name = "AWSManagedRulesCommonRuleSet"
priority = 1
override_action {
none {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "AWSManagedRulesCommonRuleSet"
sampled_requests_enabled = true
}
}
# AWS托管规则组:SQL注入防护
rule {
name = "AWSManagedRulesSQLiRuleSet"
priority = 2
override_action { none {} }
statement {
managed_rule_group_statement {
name = "AWSManagedRulesSQLiRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "AWSManagedRulesSQLiRuleSet"
sampled_requests_enabled = true
}
}
# 默认动作:允许
default_action {
allow {}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "website-waf"
sampled_requests_enabled = true
}
}
9. userdata.sh(应用初始化脚本)
这是一个通用的Node.js应用示例,你可以替换成任何语言的应用:
#!/bin/bash
set -ex
# 更新系统
apt-get update
apt-get upgrade -y
# 安装Node.js
curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
apt-get install -y nodejs
# 创建应用目录
mkdir -p /opt/app
cd /opt/app
# 下载你的应用代码(这里用一个简单的示例)
cat > app.js << EOF
const express = require('express');
const app = express();
const port = ${app_port};
app.get('/', (req, res) => {
res.send(\`
<h1>Hello from Production Website!</h1>
<p>Served by instance: \${require('os').hostname()}</p>
<p>Static resource: <img src="/static/logo.png" alt="Logo"></p>
\`);
});
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
app.listen(port, () => {
console.log(\`App listening on port \${port}\`);
});
EOF
# 安装依赖并启动应用
npm init -y
npm install express
npm install -g pm2
pm2 start app.js
pm2 startup
pm2 save
10. outputs.tf(关键输出)
output "cloudfront_domain" {
description = "CloudFront CDN域名"
value = aws_cloudfront_distribution.main.domain_name
}
output "alb_dns_name" {
description = "ALB DNS名称"
value = aws_lb.main.dns_name
}
output "s3_static_bucket" {
description = "静态资源S3存储桶"
value = aws_s3_bucket.static.bucket
}
四、完整部署步骤
1. 前置准备
- 注册一个域名(如example.com)
- 将域名的DNS托管到AWS Route53
- 配置AWS CLI认证
2. 部署所有资源
# 初始化
terraform init
# 预览资源
terraform plan
# 部署(大约需要20-30分钟,主要是CloudFront和RDS)
terraform apply
3. 配置DNS
部署完成后,Terraform会输出CloudFront的域名:
Outputs:
cloudfront_domain = "d1234567890.cloudfront.net"
在Route53中添加一条CNAME记录,将你的域名指向这个CloudFront域名。
4. 上传静态资源
# 上传静态资源到S3
aws s3 sync ./static s3://${var.domain_name}-static-assets/static/
5. 访问网站
在浏览器中输入你的域名(https://your-domain.com),就可以看到网站了。
五、如何部署你自己的应用
只需要修改userdata.sh脚本,替换成你自己的应用部署步骤即可:
- 安装你的应用所需的依赖(如Python、Java、Go等)
- 下载你的应用代码(从Git、S3或其他地方)
- 配置应用的环境变量(数据库连接、API密钥等)
- 启动应用并配置开机自启
六、生产环境成本估算(eu-west-1区域)
| 组件 | 规格 | 月成本(美元) |
|---|---|---|
| ALB | 标准 | ~22 |
| CloudFront | 1TB流量 | ~85 |
| WAF | 标准 | ~12 |
| EC2 | 2个t3.small | ~36 |
| NAT网关 | 2个 | ~66 |
| S3 | 10GB存储 + 1TB流量 | ~3 |
| Route53 | 1个托管区域 | ~0.5 |
| 总计 | ~224.5美元/月 |
成本优化建议:
- 开发环境可以关闭CloudFront和WAF,直接使用ALB
- 开发环境可以使用1个NAT网关
- 使用Spot实例代替按需实例,可节省70%的EC2成本
- 配置CloudFront缓存策略,减少回源流量
七、下一步优化方向
- 添加数据库层:集成RDS MySQL/PostgreSQL多可用区数据库
- 添加缓存层:集成ElastiCache Redis,加速数据库查询
- 配置CI/CD:使用GitHub Actions实现应用的自动部署
- 添加监控告警:配置CloudWatch告警,当错误率超过阈值时发送通知
- 配置日志收集:将应用日志和访问日志收集到CloudWatch Logs
- 配置备份策略:定期备份数据库和S3数据
需要我帮你补充数据库层和Redis缓存层的代码,或者帮你写一个GitHub Actions自动部署脚本吗?
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)