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       │ │     │
│  │ └─────────────────┘ │        │ └─────────────────┘ │     │
│  └─────────────────────┘        └─────────────────────┘     │
└─────────────────────────────────────────────────────────────┘

流量走向

  1. 用户在浏览器输入ELB的DNS域名
  2. DNS将域名解析为ELB的多个IP地址(每个可用区一个)
  3. ELB收到请求后,选择一个健康的后端实例
  4. ELB将请求转发给该实例的80端口
  5. 实例处理请求并返回响应给ELB
  6. 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_balancingtrue跨可用区负载均衡必须启用
connection_drainingtrue连接排空(优雅关闭)必须启用
connection_draining_timeout400排空超时时间根据应用最长请求时间设置,一般300-600秒

跨可用区负载均衡详解

  • 不启用:ELB只会将流量平均分配到可用区,而不是实例。如果可用区1有2个实例,可用区2有1个实例,那么每个可用区各获得50%的流量,导致可用区1的每个实例只获得25%的流量,可用区2的实例获得50%的流量
  • 启用:ELB会将流量平均分配到所有健康的实例,不管它们在哪个可用区。上面的例子中,每个实例都会获得33%的流量

连接排空详解
当实例需要下线时(如缩容、更新):

  1. ELB立即停止向该实例发送新请求
  2. 等待该实例上已有的连接完成处理
  3. 最多等待400秒,如果400秒后还有未完成的连接,强制断开
  4. 然后终止实例

这保证了用户的请求不会被突然中断,实现了优雅关闭。

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)

  1. User Data自动部署应用:实例启动时自动安装Nginx并创建显示实例IP的测试页面,不需要手动部署
  2. 使用ELB健康检查:比EC2健康检查更准确,不仅检查实例是否运行,还检查应用是否正常响应HTTP请求
  3. 关联到ELB:Auto Scaling新创建的实例会自动注册到ELB,终止的实例会自动从ELB注销
  4. 固定实例数量为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访问日志,用于故障排查和审计

五、常见坑点与排错

  1. ELB一直显示实例不健康

    • 检查实例的安全组是否允许ELB访问80端口
    • 检查Nginx是否正常启动:systemctl status nginx
    • 检查实例是否能正常响应HTTP请求:curl http://localhost
    • 检查健康检查的路径是否正确
  2. 访问ELB返回503错误

    • 所有后端实例都不健康
    • 检查ELB的安全组是否允许入站流量
    • 检查实例是否在ELB的目标组中
  3. 负载均衡效果不明显

    • 浏览器缓存了DNS解析结果,导致一直访问同一个实例
    • 使用curl命令测试,不要使用浏览器
    • ELB的负载均衡算法是轮询,但不是严格的1:1
  4. Auto Scaling创建的实例无法注册到ELB

    • 检查Auto Scaling组是否正确关联了ELB
    • 检查实例的安全组是否允许ELB访问
    • 检查健康检查宽限期是否足够长
  5. 更新Launch Configuration后实例没有更新

    • Launch Configuration是不可变的,修改后会创建新的Launch Configuration
    • Auto Scaling组不会自动更新现有实例,需要手动执行滚动更新
    • 可以使用aws autoscaling start-instance-refresh命令触发滚动更新

六、下一步进阶方向

掌握了这个架构后,你已经具备了部署生产级Web应用的能力。接下来可以学习:

  1. HTTPS配置:使用ACM证书配置HTTPS
  2. 路径路由:使用ALB实现基于路径的路由
  3. 蓝绿部署:使用ELB实现无停机的蓝绿部署
  4. 容器化部署:将应用打包成Docker容器,使用ECS或EKS部署
  5. CDN加速:使用CloudFront加速静态资源
  6. 数据库高可用:配置RDS多可用区和只读副本

更多推荐