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

使用Terraform在VPC中部署Elastic Beanstalk失败求助

在VPC中使用Terraform部署Elastic Beanstalk应用时遇到CREATE_FAILED错误

错误信息

操作终止。当前状态:'CREATE_FAILED' 原因:以下资源创建失败:[AWSEBInstanceLaunchWaitCondition]。
EC2实例无法与AWS Elastic Beanstalk通信,可能是VPC配置问题或EC2实例故障。请检查VPC配置并重试启动环境。

我调试了以下Terraform配置,但未发现问题:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

# 配置AWS Provider
provider "aws" {
  region = "us-west-2"
}

# 创建VPC
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "testapp"
  }
}

# 创建数据库用私有子网
resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-west-2b"

  tags = {
    Name = "私有子网"
  }
}

resource "aws_subnet" "private_2" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.3.0/24"
  availability_zone = "us-west-2a"

  tags = {
    Name = "私有子网2"
  }
}

# 创建互联网网关并关联到VPC
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "主IGW"
  }
}

# 为NAT网关创建弹性IP
resource "aws_eip" "nat" {
  vpc = true
}

# 在公网子网中创建NAT网关
resource "aws_nat_gateway" "main" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.public.id

  tags = {
    Name = "主NAT网关"
  }
}

# 创建Elastic Beanstalk用公网子网
resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-west-2a"

  tags = {
    Name = "公网子网"
  }
}

resource "aws_subnet" "public_2" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.4.0/24"
  availability_zone = "us-west-2b"

  tags = {
    Name = "公网子网2"
  }
}

# 创建公网路由表并添加默认路由
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 = {
    Name = "公网路由表"
  }
}

# 关联公网路由表到公网子网
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "public_2" {
  subnet_id      = aws_subnet.public_2.id
  route_table_id = aws_route_table.public.id

}

# 创建私有子网路由表并添加NAT网关默认路由
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main.id
  }

  tags = {
    Name = "私有路由表"
  }
}

# 关联私有路由表到私有子网
resource "aws_route_table_association" "private" {
  subnet_id      = aws_subnet.private.id
  route_table_id = aws_route_table.private.id
}

resource "aws_route_table_association" "private_2" {
  subnet_id      = aws_subnet.private_2.id
  route_table_id = aws_route_table.private.id
}

# 创建Elastic Beanstalk安全组
resource "aws_security_group" "beanstalk" {
  name   = "beanstalk_sg"
  vpc_id = aws_vpc.main.id

  # 允许来自任意地址的HTTP访问
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # 允许来自任意地址的HTTPS访问
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # 允许来自堡垒机安全组的所有访问
  ingress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    security_groups = [aws_security_group.bastion.id]
  }

  # 允许所有出站互联网访问
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# 创建堡垒机安全组
resource "aws_security_group" "bastion" {
  name   = "bastion_sg"
  vpc_id = aws_vpc.main.id

  # 允许来自任意地址的SSH访问
  ingress {
    from_port   = 22
    to_port     = 22
    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"]
  }
}

# 创建RDS PostgreSQL数据库
resource "aws_db_instance" "postgres" {
  allocated_storage      = 20
  storage_type           = "gp2"
  engine                 = "postgres"
  engine_version         = "15.3"
  instance_class         = "db.t3.micro"
  db_name                = "testapp"
  username               = "testappadmin"
  password               = var.db_password
  db_subnet_group_name   = aws_db_subnet_group.private.name
  vpc_security_group_ids = [aws_security_group.rds.id]

  skip_final_snapshot = true
}

# 创建数据库子网组
resource "aws_db_subnet_group" "private" {
  name       = "private"
  subnet_ids = [aws_subnet.private.id, aws_subnet.private_2.id]
}

# 创建RDS安全组
resource "aws_security_group" "rds" {
  name   = "rds_sg"
  vpc_id = aws_vpc.main.id

  # 允许来自EB安全组的PostgreSQL访问
  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.beanstalk.id]
  }
}



# 创建静态文件存储用S3桶
resource "aws_s3_bucket" "static" {

  bucket = "testapp-static-files"

  tags = {
    Name = "testapp静态文件存储桶"
  }
}

resource "aws_s3_bucket_ownership_controls" "name" {

  bucket = aws_s3_bucket.static.id

  rule {
    object_ownership = "BucketOwnerPreferred"
  }

}

resource "aws_s3_bucket_public_access_block" "static" {
  bucket = aws_s3_bucket.static.id

  block_public_policy = false

}

resource "aws_s3_bucket_policy" "static" {
  bucket = aws_s3_bucket.static.bucket

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "PublicReadGetObject"
        Effect    = "Allow"
        Principal = "*"
        Action    = ["s3:GetObject"]
        Resource = [
          "arn:aws:s3:::testapp-static-files/*",
          "arn:aws:s3:::testapp-static-files"
        ]
      }
    ]
  })

}



# 创建S3访问用户
resource "aws_iam_user" "s3_user" {
  name = "s3-uploader"
}

resource "aws_iam_user_policy" "s3_policy" {
  name = "s3-access"
  user = aws_iam_user.s3_user.name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "s3:*",
        ]
        Effect = "Allow"
        Resource = [
          "${aws_s3_bucket.static.arn}/*",
          "${aws_s3_bucket.static.arn}"

        ]
      },
    ]
  })
}



resource "aws_iam_access_key" "s3_user" {
  user = aws_iam_user.s3_user.name
}



# Chroma向量数据库EC2实例
resource "aws_instance" "chroma" {
  ami           = "ami-002829755fa238bfa" # Amazon Linux 2
  instance_type = "t3.micro"

  subnet_id              = aws_subnet.private.id
  vpc_security_group_ids = [aws_security_group.chroma.id]

  tags = {
    Name = "Chroma向量数据库"
  }
}

# Chroma安全组
resource "aws_security_group" "chroma" {
  name   = "chroma_sg"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port   = 8000
    to_port     = 8000
    protocol    = "tcp"
    cidr_blocks = [aws_subnet.private.cidr_block]
  }
}



# 创建Elastic Beanstalk应用
resource "aws_elastic_beanstalk_application" "app" {
  name        = "testapp"
  description = "testapp Django应用"
}



# 创建生产环境
resource "aws_elastic_beanstalk_environment" "prod" {
  name                = "testapp-prod"
  application         = aws_elastic_beanstalk_application.app.name
  solution_stack_name = "64bit Amazon Linux 2023 v4.0.3 running Python 3.9"

  setting {
    namespace = "aws:ec2:vpc"
    name      = "VPCId"
    value     = aws_vpc.main.id
  }

  setting {
    namespace = "aws:ec2:vpc"
    name      = "Subnets"
    value     = "${aws_subnet.public.id},${aws_subnet.public_2.id}"
  }

  setting {
    namespace = "aws:ec2:vpc"
    name      = "ELBSubnets"
    value     = "${aws_subnet.public.id},${aws_subnet.public_2.id}"
  }


  setting {
    namespace = "aws:autoscaling:launchconfiguration"
    name      = "SecurityGroups"
    value     = aws_security_group.beanstalk.id
  }

  setting {
    namespace = "aws:autoscaling:launchconfiguration"
    name      = "IamInstanceProfile"
    value     = "aws-elasticbeanstalk-ec2-role"

  }

  setting {
    namespace = "aws:autoscaling:launchconfiguration"
    name      = "InstanceType"
    value     = "t2.medium"
  }

  setting {
    namespace = "aws:elasticbeanstalk:application"
    name      = "Application Healthcheck URL"
    value     = "/health"
  }


  setting {
    namespace = "aws:elasticbeanstalk:environment"
    name      = "LoadBalancerType"
    value     = "application"
  }


  setting {
    namespace = "aws:elasticbeanstalk:environment:process:default"
    name      = "MatcherHTTPCode"
    value     = "200"
  }


  setting {
    namespace = "aws:elasticbeanstalk:application:environment"
    name      = "DATABASE_URL"
    value     = "postgres://${aws_db_instance.postgres.username}:${var.db_password}@${aws_db_instance.postgres.endpoint}/${aws_db_instance.postgres.db_name}"
  }

  setting {
    namespace = "aws:elasticbeanstalk:application:environment"
    name      = "DJANGO_SECRET_KEY"
    value     = var.django_secret_key
  }

  setting {
    namespace = "aws:elasticbeanstalk:application:environment"
    name      = "STATIC_BUCKET"
    value     = aws_s3_bucket.static.bucket
  }


  setting {
    namespace = "aws:elasticbeanstalk:application:environment"
    name      = "CHROMA_DB_URL"
    value     = "http://${aws_instance.chroma.private_ip}:8000"
  }


  setting {
    namespace = "aws:elasticbeanstalk:application:environment"
    name      = "AWS_ACCESS_KEY_ID"
    value     = aws_iam_access_key.s3_user.id
  }

  setting {
    namespace = "aws:elasticbeanstalk:application:environment"
    name      = "AWS_SECRET_ACCESS_KEY"
    value     = aws_iam_access_key.s3_user.secret
  }

  setting {
    namespace = "aws:elasticbeanstalk:application:environment"
    name      = "AWS_STORAGE_BUCKET_NAME"
    value     = aws_s3_bucket.static.id
  }

  tags = {
    Environment = "Production"
  }
}

问题分析与解决方案

1. 公网子网未启用自动分配公网IP

自定义VPC的子网默认map_public_ip_on_launch为false,导致EB的EC2实例在公网子网中也没有公网IP,无法与EB服务通信。需为两个公网子网添加该属性:

resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-west-2a"
  map_public_ip_on_launch = true

  tags = {
    Name = "公网子网"
  }
}

resource "aws_subnet" "public_2" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.4.0/24"
  availability_zone = "us-west-2b"
  map_public_ip_on_launch = true

  tags = {
    Name = "公网子网2"
  }
}

2. NAT网关依赖顺序错误

当前配置中NAT网关定义在公网子网之前,Terraform执行时会找不到公网子网ID。需调整资源顺序,先创建公网子网再创建NAT网关。

3. 验证IAM实例角色权限

确认aws-elasticbeanstalk-ec2-role实例配置文件存在,并附加了AWSElasticBeanstalkWebTier等必要托管策略。若不存在,可通过Terraform创建:

resource "aws_iam_role" "eb_ec2_role" {
  name = "aws-elasticbeanstalk-ec2-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "eb_web_tier" {
  role       = aws_iam_role.eb_ec2_role.name
  policy_arn = "arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier"
}

resource "aws_iam_instance_profile" "eb_ec2_instance_profile" {
  name = "aws-elasticbeanstalk-ec2-role"
  role = aws_iam_role.eb_ec2_role.name
}

然后在EB环境配置中引用该实例配置文件:

setting {
  namespace = "aws:autoscaling:launchconfiguration"
  name      = "IamInstanceProfile"
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 00:03:01