Terraform实战09:ALB + Target Group + Auto Scaling
本篇目标
构建经典的Web架构:ALB负载均衡器接收流量,分发到Auto Scaling Group中的多台EC2实例。理解ALB、Target Group、Listener、Launch Template、ASG之间的关系。
学完本篇你将掌握:
- Application Load Balancer的创建和配置
- Target Group与健康检查
- Listener的转发规则
- Launch Template(EC2实例模板)
- Auto Scaling Group的弹性伸缩配置
- 安全组的精细化设计(ALB→EC2的单向放行)
前置条件
- 已完成前八篇练习
- 理解VPC公私网架构
架构图
用户浏览器 │ ▼ HTTP:80 ┌───────────────┐ │ ALB │ ← 公有子网,面向外网 │ (负载均衡器) │ └───────┬───────┘ │ 转发(Listener规则) ▼ ┌───────────────┐ │ Target Group │ ← 定义后端目标+健康检查 └───────┬───────┘ │ 分发流量 ┌───────┴───────────────┐ │ Auto Scaling Group │ ← 私有子网 │ │ │ ┌─────┐ ┌─────┐ │ │ │EC2-1│ │EC2-2│ │ ← 根据模板自动创建 │ │:80 │ │:80 │ │ │ └─────┘ └─────┘ │ │ │ │ min=1 desired=2 max=3│ └────────────────────────┘各组件的关系
| 组件 | 作用 | 类比 |
|---|---|---|
| ALB | 接收外部流量,分发到后端 | 餐厅门口的领位员 |
| Listener | 监听哪个端口,收到请求后怎么处理 | 领位员的工作规则(“80端口的客人带到Target Group”) |
| Target Group | 后端实例的集合+健康检查规则 | 一组可用的餐桌 |
| Launch Template | EC2的配置模板(AMI、规格、脚本) | 餐桌的标准配置 |
| Auto Scaling Group | 根据模板管理EC2数量(扩缩容) | 根据客流量增减餐桌 |
流量路径:用户 → ALB(:80) → Listener(转发规则) → Target Group → EC2实例
目录结构
09-alb-asg/ ├── main.tf # 所有资源(VPC+ALB+TG+ASG) ├── variables.tf # 变量(实例规格、ASG数量等) └── outputs.tf # 输出(ALB的DNS地址)完整代码
main.tf
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } required_version = ">= 1.0" } provider "aws" { region = var.region } locals { name_prefix = "${var.project_name}-${var.environment}" common_tags = { Project = var.project_name Environment = var.environment ManagedBy = "terraform" } # 启动脚本:安装httpd,显示实例ID(验证负载均衡) user_data = <<-EOF #!/bin/bash yum install -y httpd INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) echo "<h1>Hello from $INSTANCE_ID</h1><p>Environment: ${var.environment}</p>" > /var/www/html/index.html systemctl start httpd systemctl enable httpd EOF } # VPC(社区模块) module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "5.16.0" name = "${local.name_prefix}-vpc" cidr = "10.0.0.0/16" azs = ["${var.region}a", "${var.region}b"] public_subnets = ["10.0.1.0/24", "10.0.2.0/24"] private_subnets = ["10.0.10.0/24", "10.0.11.0/24"] enable_nat_gateway = true single_nat_gateway = true enable_dns_hostnames = true enable_dns_support = true tags = local.common_tags } # ============================================ # 安全组设计(重点) # ALB安全组:允许外部HTTP # EC2安全组:只允许来自ALB的流量 # ============================================ resource "aws_security_group" "alb" { name = "${local.name_prefix}-alb-sg" vpc_id = module.vpc.vpc_id ingress { description = "HTTP from anywhere" 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 = merge(local.common_tags, { Name = "${local.name_prefix}-alb-sg" }) } resource "aws_security_group" "ec2" { name = "${local.name_prefix}-ec2-sg" vpc_id = module.vpc.vpc_id ingress { description = "HTTP from ALB only" from_port = 80 to_port = 80 protocol = "tcp" security_groups = [aws_security_group.alb.id] # 【关键】只允许ALB安全组 } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = merge(local.common_tags, { Name = "${local.name_prefix}-ec2-sg" }) } # ============================================ # 【新资源】ALB - Application Load Balancer # ============================================ resource "aws_lb" "web" { name = "${local.name_prefix}-alb" internal = false # 面向外网 load_balancer_type = "application" # ALB类型 security_groups = [aws_security_group.alb.id] subnets = module.vpc.public_subnets # 至少2个AZ的公有子网 tags = merge(local.common_tags, { Name = "${local.name_prefix}-alb" }) } # ============================================ # 【新资源】Target Group - 后端目标组 # ============================================ resource "aws_lb_target_group" "web" { name = "${local.name_prefix}-tg" port = 80 protocol = "HTTP" vpc_id = module.vpc.vpc_id health_check { enabled = true path = "/" # 健康检查路径 port = "traffic-port" protocol = "HTTP" healthy_threshold = 2 # 连续2次成功→健康 unhealthy_threshold = 3 # 连续3次失败→不健康 timeout = 5 # 超时秒数 interval = 30 # 检查间隔 matcher = "200" # 期望返回200 } tags = merge(local.common_tags, { Name = "${local.name_prefix}-tg" }) } # ============================================ # 【新资源】Listener - ALB监听器 # 定义:收到80端口请求后,转发到Target Group # ============================================ 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 } } # ============================================ # 【新资源】Launch Template - EC2实例模板 # ASG根据这个模板创建新实例 # ============================================ resource "aws_launch_template" "web" { name_prefix = "${local.name_prefix}-lt-" image_id = data.aws_ami.amazon_linux.id instance_type = var.instance_type vpc_security_group_ids = [aws_security_group.ec2.id] user_data = base64encode(local.user_data) # 启动脚本(需base64编码) tag_specifications { resource_type = "instance" tags = merge(local.common_tags, { Name = "${local.name_prefix}-web" }) } tags = merge(local.common_tags, { Name = "${local.name_prefix}-launch-template" }) } # ============================================ # 【新资源】Auto Scaling Group - 弹性伸缩组 # ============================================ resource "aws_autoscaling_group" "web" { name = "${local.name_prefix}-asg" desired_capacity = var.desired_capacity min_size = var.min_size max_size = var.max_size vpc_zone_identifier = module.vpc.private_subnets # EC2在私有子网 launch_template { id = aws_launch_template.web.id version = "$Latest" } # 【关键】关联Target Group,新实例自动注册到ALB target_group_arns = [aws_lb_target_group.web.arn] # 用ELB健康检查(ALB判断实例是否健康) health_check_type = "ELB" health_check_grace_period = 60 tag { key = "Name" value = "${local.name_prefix}-web" propagate_at_launch = true } } data "aws_ami" "amazon_linux" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["al2023-ami-2023*-x86_64"] } filter { name = "state" values = ["available"] } }variables.tf
variable "region" { default = "us-east-1" } variable "project_name" { default = "tf-practice" } variable "environment" { default = "dev" } variable "instance_type" { description = "EC2实例规格" default = "t3.micro" } variable "desired_capacity" { description = "ASG期望实例数" default = 2 } variable "min_size" { description = "ASG最小实例数" default = 1 } variable "max_size" { description = "ASG最大实例数" default = 3 }outputs.tf
output "alb_dns_name" { description = "ALB的DNS地址(浏览器访问)" value = aws_lb.web.dns_name } output "alb_url" { description = "完整访问URL" value = "http://${aws_lb.web.dns_name}" } output "target_group_arn" { value = aws_lb_target_group.web.arn } output "asg_name" { value = aws_autoscaling_group.web.name }新增资源说明
ALB关键字段
| 字段 | 含义 |
|---|---|
internal = false | 面向外网(true=内部ALB) |
load_balancer_type = "application" | ALB类型(还有network、gateway) |
security_groups | ALB自己的安全组 |
subnets | ALB部署的子网(至少2个AZ) |
Target Group关键字段
| 字段 | 含义 |
|---|---|
port | 后端实例监听的端口 |
protocol | 后端使用的协议 |
health_check.path | 健康检查的URL路径 |
health_check.healthy_threshold | 连续几次成功算健康 |
health_check.unhealthy_threshold | 连续几次失败算不健康 |
health_check.interval | 检查间隔(秒) |
health_check.matcher | 期望的HTTP状态码 |
Launch Template关键字段
| 字段 | 含义 |
|---|---|
image_id | AMI ID |
instance_type | 实例规格 |
vpc_security_group_ids | 安全组 |
user_data | 启动脚本(需base64编码) |
tag_specifications | 实例标签 |
Auto Scaling Group关键字段
| 字段 | 含义 |
|---|---|
desired_capacity | 期望运行几台 |
min_size | 缩容最少保留几台 |
max_size | 扩容最多到几台 |
vpc_zone_identifier | 实例创建在哪些子网 |
launch_template | 使用哪个实例模板 |
target_group_arns | 关联的Target Group(新实例自动注册) |
health_check_type | “EC2"或"ELB”(推荐ELB) |
health_check_grace_period | 新实例启动后等多久再检查 |
安全组设计要点
外部流量 → ALB安全组(允许0.0.0.0/0:80)→ EC2安全组(只允许ALB安全组)EC2的安全组不直接开放给外网,而是用security_groups = [ALB的安全组ID]限制来源。这样:
- 外网只能通过ALB访问EC2
- 直接访问EC2的公网IP会被拒绝
- 即使EC2有公网IP也不怕(事实上私有子网里没有)
操作步骤与实际输出
Apply
terraform apply -auto-approveaws_lb.web: Creation complete after 3m17s ← ALB创建较慢 aws_autoscaling_group.web: Creation complete after 14s Apply complete! Resources: 26 added, 0 changed, 0 destroyed. Outputs: alb_dns_name = "tf-practice-dev-alb-799249280.us-east-1.elb.amazonaws.com" alb_url = "http://tf-practice-dev-alb-799249280.us-east-1.elb.amazonaws.com" asg_name = "tf-practice-dev-asg"验证负载均衡
第1次刷新:Hello from i-0abc123... 第2次刷新:Hello from i-0def456... ← 不同实例,负载均衡生效控制台验证
ALB概览:
- 状态:Active
- DNS名称
- 类型:application
Target Group健康状态:
- 2个实例注册
- 状态:Healthy
Listener规则:
- HTTP:80 → Forward to target group
ASG详情:
- Desired: 2, Min: 1, Max: 3
EC2实例:
- 2台实例,由ASG创建
Destroy
terraform destroy -auto-approve# Destroy complete! Resources: 26 destroyed.延伸思考:面试常见问题
| 面试问题 | 答案要点 |
|---|---|
| ALB和NLB的区别? | ALB工作在7层(HTTP),支持路径/域名路由;NLB工作在4层(TCP),性能更高 |
| Target Group的健康检查有什么用? | 自动摘除不健康的实例,流量不会转发到故障节点 |
| ASG怎么实现弹性伸缩? | 根据策略(CPU利用率、请求数等)自动调整desired_capacity |
| 为什么EC2安全组只允许ALB安全组? | 最小权限原则,EC2不直接暴露给外网 |
| Launch Template和Launch Configuration的区别? | Template更新,支持版本管理、混合实例等,Configuration已废弃 |
| ASG的health_check_type选EC2还是ELB? | 推荐ELB——如果应用挂了但EC2没挂,ELB能检测到,EC2检测不到 |
费用说明
| 资源 | 费用 |
|---|---|
| ALB | ~$0.022/小时 |
| NAT Gateway | ~$0.045/小时 |
| EC2 t3.micro × 2 | ~$0.021/小时 |
| VPC / 子网 / 安全组 | 免费 |
| 总计 | ~$0.09/小时 |
本次练习(约40分钟):约$0.06
小结
本篇核心收获:
- ALB + Target Group + ASG是AWS最经典的Web架构
- 流量路径:用户→ALB→Listener→Target Group→EC2
- ASG关联Target Group:新实例自动注册到ALB,无需手动操作
- 安全组链式设计:EC2只接受ALB的流量,不直接暴露
- Launch Template:EC2的"模板",ASG根据它创建新实例
- 健康检查:自动摘除不健康实例,保证服务可用性
下一篇预告
Terraform实战10:RDS + Secrets Manager
下一篇我们将学习:
- 创建RDS MySQL/PostgreSQL数据库
- 数据库子网组配置
- 用Secrets Manager管理数据库密码
- 安全组限制只允许应用访问数据库
参考链接
- 本系列配套代码(GitHub)
- Terraform aws_lb文档
- Terraform aws_lb_target_group文档
- Terraform aws_autoscaling_group文档
- AWS ALB官方文档
- AWS Auto Scaling官方文档