You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform部署后无法通过Application Load Balancer访问后端实例

问题描述

我用Terraform在AWS的3个不同可用区部署了3个运行简单Web应用的实例,测试阶段通过实例公网IP能正常访问应用。但添加Application Load Balancer(ALB)后,访问ALB域名时收到错误:Hmmm… can't reach this page alb-1860663581.eu-central-1.elb.amazonaws.com refused to connect.

我尝试过修改ALB监听端口并在安全组中放行、移除实例公网IP改为私有IP,但实例到ALB DNS的Telnet均失败,不过目标组健康检查显示实例状态正常。甚至在遵循AWS基础教程部署ELB时也遇到相同问题,以下是我的配置代码:

Main.tf

#PROVIDERS
provider "aws" {
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  region     = var.aws_region
}

#DATA

data "aws_ssm_parameter" "ami" {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}

#RESOURCES

#KEY PAIR

##NETWORKING
resource "aws_vpc" "vpc" {
  cidr_block = "172.32.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support = true
  tags = local.common_tags
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id

  tags = local.common_tags
}

resource "aws_subnet" "public_subnet" {
  count                   = var.subnet_count.public
  cidr_block              = var.public_subnet_cidr_blocks[count.index]
  vpc_id                  = aws_vpc.vpc.id
  map_public_ip_on_launch = true
  availability_zone       = var.public_subnet_availability_zones[count.index]
  tags                    = local.common_tags
}

resource "aws_subnet" "private_subnets" {
  count             = var.subnet_count.private
  cidr_block        = var.private_subnet_cidr_blocks[count.index]
  vpc_id            = aws_vpc.vpc.id
  availability_zone = var.private_subnet_availability_zones[count.index]
  tags              = local.common_tags
}

##ROUTING
resource "aws_route_table" "rtb-pub" {
  vpc_id = aws_vpc.vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }

  tags = local.common_tags
}

resource "aws_route_table_association" "rta-public" {
  count          = var.subnet_count.public
  subnet_id      = aws_subnet.public_subnet[count.index].id
  route_table_id = aws_route_table.rtb-pub.id
}


resource "aws_route_table" "rtb-priv" {
  vpc_id = aws_vpc.vpc.id

  tags = local.common_tags
}

resource "aws_route_table_association" "rta-priv" {
  count          = var.subnet_count.private
  subnet_id      = aws_subnet.private_subnets[count.index].id
  route_table_id = aws_route_table.rtb-priv.id
}

#SECURITY GROUPS
#PHP-SG

resource "aws_security_group" "php-sg" {
  name   = "php-sg"
  vpc_id = aws_vpc.vpc.id

  # HTTP access from anywhere which also includes the LB's listener port
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "Allow SSH from my PC"
    from_port   = "22"
    to_port     = "22"
    protocol    = "tcp"
    cidr_blocks = ["${var.my_ip}/32"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # ingress {
  #   description = "Load balancer listener port"
  #   from_port   = 90
  #   to_port     = 90
  #   protocol    = "tcp"
  #   cidr_blocks = ["0.0.0.0/0"]
  # }

  # outbound internet access
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = local.common_tags
}

resource "aws_security_group" "rds-sg" {
  name   = "rds-sg"
  vpc_id = aws_vpc.vpc.id

  ingress {
    description     = "Allow MySQL traffic from only the php sg"
    from_port       = "3306"
    to_port         = "3306"
    protocol        = "tcp"
    security_groups = [aws_security_group.php-sg.id]
  }

  tags = local.common_tags
}

# APPLICATION LOAD BALANCER RESOURCES#
resource "aws_lb_target_group" "tg" {
  name = "targetGroup"
  port = 80
  target_type = "instance"
  vpc_id = aws_vpc.vpc.id
  protocol = "HTTP"
}

resource "aws_lb_target_group_attachment" "tgatt" {
  count = var.subnet_count.public
  target_group_arn = aws_lb_target_group.tg.arn
  port = 80
  target_id = aws_instance.phpserver[count.index].id
  #availability_zone = var.public_subnet_availability_zones
}

resource "aws_lb" "lb" {
  name = "ALB"
  internal = false
  load_balancer_type = "application"
  security_groups = [ aws_security_group.php-sg.id ]
  subnets = aws_subnet.public_subnet.*.id
}

resource "aws_lb_listener" "listener" {
  load_balancer_arn = aws_lb.lb.arn
  port = "80"
  protocol = "HTTP"

  default_action {
    type = "redirect"

    redirect {
      port = "443"
      protocol = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}

resource "aws_lb_listener_rule" "listener_rule" {
  listener_arn = aws_lb_listener.listener.arn
  priority = 100

  action {
    type = "forward"
    target_group_arn = aws_lb_target_group.tg.arn
  }

  condition {
    path_pattern {
      values = [ "/var/www/html/index.php" ]
    }
  }
}


# INSTANCE #
resource "aws_instance" "phpserver" {
  count                       = var.subnet_count.public
  ami                         = nonsensitive(data.aws_ssm_parameter.ami.value)
  instance_type               = "t3.micro"
  subnet_id                   = aws_subnet.public_subnet[count.index].id
  associate_public_ip_address = true
  vpc_security_group_ids      = [aws_security_group.php-sg.id]
  key_name = "someKeyName"
  user_data = templatefile("${path.module}/startup_script.tftpl", {
    endpoint = aws_db_instance.default.endpoint
    db_name  = aws_db_instance.default.db_name
  })

  tags = local.common_tags

  depends_on = [
    aws_db_instance.default
  ]
}

# RDS Subnet group # 

resource "aws_db_subnet_group" "db_subnet_group" {
  name       = "db_subnet_group"
  subnet_ids = [for subnet in aws_subnet.private_subnets : subnet.id]
}

# RDS #
resource "aws_db_instance" "default" {
  db_subnet_group_name   = "db_subnet_group"
  allocated_storage      = 20
  db_name                = "livanoDB"
  engine                 = "mysql"
  instance_class         = "db.t3.micro"
  username               = var.db_username
  password               = var.db_password
  skip_final_snapshot    = true
  vpc_security_group_ids = [aws_security_group.rds-sg.id]

  tags = local.common_tags
  depends_on = [
    aws_db_subnet_group.db_subnet_group
  ]
}

Variables.tf

variable "aws_access_key" {
  type        = string
  description = "AWS Access key"
  sensitive   = true
}

variable "aws_secret_key" {
  type        = string
  description = "AWS Secret key"
  sensitive   = true
}

variable "db_username" {
  description = "Database administrator username"
  type        = string
  sensitive   = true
}

variable "db_password" {
  description = "Database administrator password"
  type        = string
  sensitive   = true
}

variable "aws_region" {
  type        = string
  description = "AWS Region to use for resources"
  default     = "eu-central-1"
}

variable "my_ip" {
  description = "my IP Address"
  type        = string
  sensitive   = true
}

variable "owner" {
  type        = string
  description = "Owner of the deployed resource in the VPC"
  default     = "livano"
}

variable "public_subnet_cidr_blocks" {
  description = "CIDR blocks for public subnets"
  type        = list(string)
  default     = ["172.32.0.0/19", "172.32.32.0/19", "172.32.64.0/19", "172.32.96.0/19"]
}

variable "private_subnet_cidr_blocks" {
  description = "CIDR blocks for private subnets"
  type        = list(string)
  default     = ["172.32.224.0/19", "172.32.192.0/19", "172.32.160.0/19", "172.32.128.0/19"]
}

variable "private_subnet_availability_zones" {
  description = "list of eu-central-1 availability zones"
  type        = list(string)
  default     = ["eu-central-1a", "eu-central-1b", "eu-central-1c", ]
}

variable "public_subnet_availability_zones" {
  description = "list of eu-central-1 availability zones"
  type        = list(string)
  default     = ["eu-central-1a", "eu-central-1b", "eu-central-1c", ]
}

variable "subnet_count" {
  description = "Number of subnets"
  type        = map(number)
  default = {
    public  = 3,
    private = 2
  }
}
问题排查与修复

你的配置存在3个核心问题导致ALB无法正常访问:

1. ALB监听配置逻辑错误

当前HTTP监听(80端口)的默认动作是重定向到HTTPS 443端口,但你没有配置443端口的监听规则。外部用户访问ALB的80端口时,会被强制重定向到443,但ALB未开启该端口监听,直接导致连接被拒绝。

同时,你添加的路径规则使用了实例本地文件路径/var/www/html/index.php,这是错误的——ALB的路径规则匹配的是URL路径,而非服务器本地文件路径。

修复监听配置(测试HTTP访问场景)

修改aws_lb_listener和aws_lb_listener_rule,取消重定向,直接转发请求到目标组,并修正路径规则:

resource "aws_lb_listener" "listener" {
  load_balancer_arn = aws_lb.lb.arn
  port              = "80"
  protocol          = "HTTP"

  # 默认动作直接转发到目标组,而非重定向
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.tg.arn
  }
}

# 修正路径规则为URL路径,或直接删除该规则使用默认转发
resource "aws_lb_listener_rule" "listener_rule" {
  listener_arn = aws_lb_listener.listener.arn
  priority     = 100

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.tg.arn
  }

  condition {
    path_pattern {
      # 改为URL路径,比如匹配/index.php
      values = ["/index.php"]
    }
  }
}

2. 目标组健康检查配置缺失(可选但推荐)

虽然你提到目标组健康检查正常,但显式配置健康检查路径能避免后续因应用路径变更导致的异常。在aws_lb_target_group中添加健康检查规则:

resource "aws_lb_target_group" "tg" {
  name         = "targetGroup"
  port         = 80
  target_type  = "instance"
  vpc_id       = aws_vpc.vpc.id
  protocol     = "HTTP"

  health_check {
    path                = "/" # 匹配你的Web应用根路径
    protocol            = "HTTP"
    port                = "traffic-port"
    interval            = 30
    timeout             = 5
    healthy_threshold   = 2
    unhealthy_threshold = 2
    matcher             = "200-399"
  }
}

3. 实例到ALB的Telnet失败无需关注

ALB是面向客户端的反向代理,实例不需要主动访问ALB的DNS地址,Telnet失败属于正常现象,只要目标组健康检查正常,说明ALB能正常访问实例的80端口。

验证步骤
  1. 应用上述修改后,执行terraform apply更新配置;
  2. 等待ALB状态变为active,目标组所有实例状态为healthy;
  3. 访问ALB的HTTP域名,即可正常转发到实例的Web应用。

内容的提问来源于stack exchange,提问作者livanov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 00:09:56