使用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"
相关产品推荐
相关产品推荐

