Terraform :AWS ELB+Auto Scaling
Terraform AWS ELB+Auto Scaling 高可用Web架构完整详解
这是AWS生产级Web应用的标准最终架构,将之前的VPC网络、Auto Scaling自动扩缩容和ELB负载均衡器完美结合,实现了流量分发、故障自动转移、零停机更新三大核心能力。这是你学习AWS基础设施的最终里程碑——掌握了这个架构,就能部署绝大多数企业级高可用Web应用。
一、核心价值与整体架构
为什么需要ELB?
没有ELB的架构存在致命缺陷:
- 用户只能访问单个实例的IP,实例故障时服务完全中断
- 无法将流量分发到多个实例,所有压力都集中在一台服务器上
- 无法实现无停机更新,更新应用时必须停机
有了ELB之后:
✅ 统一入口:所有用户访问同一个ELB域名,不需要知道后端实例的IP
✅ 流量分发:自动将流量均匀分发到所有健康的后端实例
✅ 自动故障转移:某个实例故障时,自动停止向它转发流量
✅ 零停机更新:滚动更新实例时,用户完全无感知
✅ 高可用:ELB本身是AWS托管服务,天生跨可用区高可用
整体架构拓扑图
互联网
↓
用户访问 ELB DNS: my-elb-123456789.eu-west-1.elb.amazonaws.com
↓
ELB 负载均衡器(跨 eu-west-1a 和 eu-west-1b 两个可用区)
↓
┌─────────────────────────────────────────────────────────────┐
│ 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实例1(Nginx) │ │ │ │ EC2实例2(Nginx) │ │ │
│ │ │ 10.0.1.50 │ │ │ │ 10.0.2.30 │ │ │
│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
│ └─────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
流量走向:
- 用户在浏览器输入ELB的DNS域名
- DNS将域名解析为ELB的多个IP地址(每个可用区一个)
- ELB收到请求后,选择一个健康的后端实例
- ELB将请求转发给该实例的80端口
- 实例处理请求并返回响应给ELB
- ELB将响应返回给用户
二、核心代码逐行解析
1. elb.tf(负载均衡器核心配置)
resource "aws_elb" "my-elb" {
name = "my-elb"
subnets = [aws_subnet.main-public-1.id, aws_subnet.main-public-2.id]
security_groups = [aws_security_group.elb-securitygroup.id]
# 监听器配置:定义ELB如何接收和转发流量
listener {
instance_port = 80 # 转发到实例的端口
instance_protocol = "http"# 转发到实例的协议
lb_port = 80 # ELB监听的端口
lb_protocol = "http"# ELB使用的协议
}
# 健康检查配置:ELB如何判断实例是否健康
health_check {
healthy_threshold = 2 # 连续2次健康检查通过,标记为健康
unhealthy_threshold = 2 # 连续2次健康检查失败,标记为不健康
timeout = 3 # 每次检查的超时时间(秒)
target = "HTTP:80/" # 检查目标:HTTP GET /
interval = 30 # 检查间隔(秒)
}
# 高级配置
cross_zone_load_balancing = true # 启用跨可用区负载均衡
connection_draining = true # 启用连接排空
connection_draining_timeout = 400 # 排空超时时间(秒)
tags = {
Name = "my-elb"
}
}
1.1 监听器配置
监听器是ELB的核心,定义了流量的入口和出口规则:
lb_port=80, lb_protocol=http:ELB在80端口监听HTTP请求instance_port=80, instance_protocol=http:将请求转发到实例的80端口,使用HTTP协议- 这是最简单的HTTP监听器配置,生产环境还需要配置HTTPS监听器(443端口)
1.2 健康检查配置(最关键的功能)
健康检查是ELB实现自动故障转移的基础,完整流程如下:
每30秒执行一次
↓
发送HTTP GET请求到实例的80端口根路径(/)
↓
3秒内收到200 OK响应?
├─ 是 → 健康计数+1
└─ 否 → 不健康计数+1
↓
连续2次健康 → 标记为健康,开始转发流量
连续2次不健康 → 标记为不健康,停止转发流量
关键参数说明:
interval=30:不要设置得太频繁,否则会给实例带来不必要的压力timeout=3:如果你的应用响应比较慢,可以适当调大这个值target="HTTP:80/":强烈建议使用专门的健康检查端点(如/health),而不是根路径healthy_threshold=2, unhealthy_threshold=2:平衡了故障检测速度和误判率
1.3 高级配置
| 配置项 | 值 | 作用 | 生产环境建议 |
|---|---|---|---|
cross_zone_load_balancing | true | 跨可用区负载均衡 | 必须启用 |
connection_draining | true | 连接排空(优雅关闭) | 必须启用 |
connection_draining_timeout | 400 | 排空超时时间 | 根据应用最长请求时间设置,一般300-600秒 |
跨可用区负载均衡详解:
- 不启用:ELB只会将流量平均分配到可用区,而不是实例。如果可用区1有2个实例,可用区2有1个实例,那么每个可用区各获得50%的流量,导致可用区1的每个实例只获得25%的流量,可用区2的实例获得50%的流量
- 启用:ELB会将流量平均分配到所有健康的实例,不管它们在哪个可用区。上面的例子中,每个实例都会获得33%的流量
连接排空详解:
当实例需要下线时(如缩容、更新):
- ELB立即停止向该实例发送新请求
- 等待该实例上已有的连接完成处理
- 最多等待400秒,如果400秒后还有未完成的连接,强制断开
- 然后终止实例
这保证了用户的请求不会被突然中断,实现了优雅关闭。
2. autoscaling.tf(Auto Scaling与ELB集成)
resource "aws_launch_configuration" "example-launchconfig" {
name_prefix = "example-launchconfig"
image_id = var.AMIS[var.AWS_REGION]
instance_type = "t2.micro"
key_name = aws_key_pair.mykeypair.key_name
security_groups = [aws_security_group.myinstance.id]
# 🔥 关键:User Data自动安装Nginx并创建测试页面
user_data = <<EOF
#!/bin/bash
apt-get update
apt-get -y install net-tools nginx
# 获取实例的私网IP
MYIP=`ifconfig | grep -E '(inet 10)|(addr:10)' | awk '{ print $2 }' | cut -d ':' -f2`
# 将IP写入Nginx默认页面,用于测试负载均衡
echo 'this is: '$MYIP > /var/www/html/index.html
EOF
lifecycle {
create_before_destroy = true # 先创建新实例,再销毁旧实例
}
}
resource "aws_autoscaling_group" "example-autoscaling" {
name = "example-autoscaling"
vpc_zone_identifier = [aws_subnet.main-public-1.id, aws_subnet.main-public-2.id]
launch_configuration = aws_launch_configuration.example-launchconfig.name
min_size = 2
max_size = 2
health_check_grace_period = 300
health_check_type = "ELB" # 🔥 使用ELB健康检查代替EC2健康检查
load_balancers = [aws_elb.my-elb.name] # 🔥 关联到ELB
force_delete = true
tag {
key = "Name"
value = "ec2 instance"
propagate_at_launch = true
}
}
核心升级点(对比上一章节的Auto Scaling):
- User Data自动部署应用:实例启动时自动安装Nginx并创建显示实例IP的测试页面,不需要手动部署
- 使用ELB健康检查:比EC2健康检查更准确,不仅检查实例是否运行,还检查应用是否正常响应HTTP请求
- 关联到ELB:Auto Scaling新创建的实例会自动注册到ELB,终止的实例会自动从ELB注销
- 固定实例数量为2:为了演示负载均衡效果,这里设置min_size=max_size=2,生产环境可以根据需要调整
3. securitygroup.tf(最佳安全实践)
这是这个Demo最值得学习的地方,完美体现了最小权限原则:
ELB安全组
resource "aws_security_group" "elb-securitygroup" {
vpc_id = aws_vpc.main.id
name = "elb"
# 入站:允许所有IP访问80端口
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_security_group" "myinstance" {
vpc_id = aws_vpc.main.id
name = "myinstance"
# 入站1:允许所有IP访问22端口(SSH)
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# 🔥 入站2:只允许来自ELB安全组的流量访问80端口
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [aws_security_group.elb-securitygroup.id]
}
# 出站:允许所有流量
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
安全设计亮点:
- 实例的80端口不直接开放给互联网,只允许来自ELB安全组的流量
- 这意味着:用户无法直接访问实例的80端口,所有流量必须经过ELB
- 即使实例有公网IP,用户也无法绕过ELB直接访问实例
- 这是生产环境的标准安全做法,所有Web流量必须经过负载均衡器
三、完整部署与测试流程
1. 准备工作
# 生成SSH密钥对
ssh-keygen -t rsa -b 2048 -f mykey -N ""
2. 部署所有资源
cd demo-13-ELB
terraform init
terraform plan
terraform apply
部署完成后,Terraform会输出ELB的DNS名称:
Outputs:
ELB = "my-elb-123456789.eu-west-1.elb.amazonaws.com"
3. 测试负载均衡效果
# 多次访问ELB DNS
for i in {1..10}; do curl http://$(terraform output -raw ELB); echo; done
预期输出:
this is: 10.0.1.50
this is: 10.0.2.30
this is: 10.0.1.50
this is: 10.0.2.30
...
可以看到,ELB会将请求轮流转发到两个实例,实现了负载均衡。
4. 测试自动故障转移
# 1. 查看当前运行的实例
aws ec2 describe-instances --filters "Name=tag:Name,Values=ec2 instance" --query "Reservations[*].Instances[*].{ID:InstanceId, IP:PrivateIpAddress}"
# 2. 手动终止其中一个实例(模拟故障)
aws ec2 terminate-instances --instance-ids <实例ID>
# 3. 继续访问ELB
for i in {1..10}; do curl http://$(terraform output -raw ELB); echo; done
预期结果:
- 即使终止了一个实例,ELB仍然可以正常访问,不会出现服务中断
- 所有请求都会被转发到剩下的那个健康实例
- 大约5分钟后,Auto Scaling会自动创建一个新的实例来替换被终止的实例
- 新实例启动完成并通过健康检查后,ELB会自动开始向它转发流量
四、生产环境最佳实践
1. 使用ALB代替经典ELB
经典ELB(CLB)已经是上一代产品,推荐使用**应用负载均衡器(ALB)**代替:
- 支持路径路由和主机头路由
- 支持HTTP/2和WebSocket
- 支持更灵活的健康检查
- 支持容器化应用
- 成本更低,性能更好
2. 启用HTTPS
- 申请AWS Certificate Manager(ACM)免费SSL证书
- 配置HTTPS监听器(443端口)
- 将HTTP流量重定向到HTTPS
- 配置现代TLS安全策略
3. 优化健康检查
- 使用专门的健康检查端点(如
/health),而不是根路径 - 健康检查端点应该检查应用的核心依赖(如数据库连接)
- 根据应用的启动时间调整
health_check_grace_period
4. 启用自动扩缩容
- 不要固定实例数量,根据CPU使用率或请求数自动扩缩容
- 配置目标跟踪扩展策略,将平均CPU使用率维持在70%左右
- 配置最小实例数为2,保证高可用
5. 安全改进
- 限制SSH访问的源IP,不要开放给0.0.0.0/0
- 将实例放在私网子网,不要给实例公网IP
- 使用AWS Systems Manager Session Manager代替SSH访问实例
- 启用ELB访问日志,记录所有请求
6. 运维改进
- 配置CloudWatch监控,监控ELB的请求数、延迟、错误率等指标
- 配置告警,当健康实例数低于阈值时发送通知
- 启用ELB访问日志,用于故障排查和审计
五、常见坑点与排错
-
ELB一直显示实例不健康:
- 检查实例的安全组是否允许ELB访问80端口
- 检查Nginx是否正常启动:
systemctl status nginx - 检查实例是否能正常响应HTTP请求:
curl http://localhost - 检查健康检查的路径是否正确
-
访问ELB返回503错误:
- 所有后端实例都不健康
- 检查ELB的安全组是否允许入站流量
- 检查实例是否在ELB的目标组中
-
负载均衡效果不明显:
- 浏览器缓存了DNS解析结果,导致一直访问同一个实例
- 使用curl命令测试,不要使用浏览器
- ELB的负载均衡算法是轮询,但不是严格的1:1
-
Auto Scaling创建的实例无法注册到ELB:
- 检查Auto Scaling组是否正确关联了ELB
- 检查实例的安全组是否允许ELB访问
- 检查健康检查宽限期是否足够长
-
更新Launch Configuration后实例没有更新:
- Launch Configuration是不可变的,修改后会创建新的Launch Configuration
- Auto Scaling组不会自动更新现有实例,需要手动执行滚动更新
- 可以使用
aws autoscaling start-instance-refresh命令触发滚动更新
六、下一步进阶方向
掌握了这个架构后,你已经具备了部署生产级Web应用的能力。接下来可以学习:
- HTTPS配置:使用ACM证书配置HTTPS
- 路径路由:使用ALB实现基于路径的路由
- 蓝绿部署:使用ELB实现无停机的蓝绿部署
- 容器化部署:将应用打包成Docker容器,使用ECS或EKS部署
- CDN加速:使用CloudFront加速静态资源
- 数据库高可用:配置RDS多可用区和只读副本
更多推荐
所有评论(0)