通过 Terraform 更改 AWS 中的目标端口时,删除目标组时出错:ResourceInUse

aef*_*aef 8 cloud amazon-web-services terraform devops infrastructure-as-code

我目前正在阅读测试版“Terraform Up & Running, 2nd Edition”。在第 2 章中,我在 AWS 中创建了一个 Auto Scaling 组和一个负载均衡器。

现在我使我的后端服务器 HTTP 端口可配置。默认情况下,它们侦听端口 8080。

variable "server_port" {
    …
    default = 8080
}

resource "aws_launch_configuration" "example" {
    …
    user_data = <<-EOF
                #!/bin/bash
                echo "Hello, World" > index.html
                nohup busybox httpd -f -p ${var.server_port} &
                EOF
    …
}

resource "aws_security_group" "instance" {
    …
    ingress {
        from_port = var.server_port
        to_port = var.server_port
        …
    }
}
Run Code Online (Sandbox Code Playgroud)

还需要在应用程序负载均衡器的目标组中配置相同的端口。

resource "aws_lb_target_group" "asg" {
    …
    port = var.server_port
    …
}
Run Code Online (Sandbox Code Playgroud)

当我的基础设施已经部署好时,例如将端口的配置设置为 8080,然后我通过运行将变量更改为 80 terraform apply --var server_port=80,报告以下错误:

> Error: Error deleting Target Group: ResourceInUse: Target group
> 'arn:aws:elasticloadbalancing:eu-central-1:…:targetgroup/terraform-asg-example/…'
> is currently in use by a listener or a rule   status code: 400,
Run Code Online (Sandbox Code Playgroud)

如何改进我的 Terraform 基础架构定义以使这种更改成为可能?我想它可能与lifecycle某个地方的选项有关,但我还没有弄清楚。


为了您的参考,我在下面附上了我的整个基础设施定义:

provider "aws" {
    region = "eu-central-1"
}

output "alb_location" {
    value = "http://${aws_lb.example.dns_name}"
    description = "The location of the load balancer"
}

variable "server_port" {
    description = "The port the server will use for HTTP requests"
    type = number
    default = 8080
}

resource "aws_lb_listener_rule" "asg" {
    listener_arn = aws_lb_listener.http.arn
    priority = 100

    condition {
        field = "path-pattern"
        values = ["*"]
    }

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

resource "aws_lb_target_group" "asg" {
    name = "terraform-asg-example"
    port = var.server_port
    protocol = "HTTP"
    vpc_id = data.aws_vpc.default.id

    health_check {
        path = "/"
        protocol = "HTTP"
        matcher = "200"
        interval = 15
        timeout = 3
        healthy_threshold = 2
        unhealthy_threshold = 2
    }
}

resource "aws_lb_listener" "http" {
    load_balancer_arn = aws_lb.example.arn
    port = 80
    protocol = "HTTP"

    default_action {
        type = "fixed-response"

        fixed_response {
            content_type = "text/plain"
            message_body = "404: page not found"
            status_code = 404
        }
    }
}

resource "aws_lb" "example" {
    name = "terraform-asg-example"
    load_balancer_type = "application"
    subnets = data.aws_subnet_ids.default.ids
    security_groups = [aws_security_group.alb.id]
}

resource "aws_security_group" "alb" {
    name = "terraform-example-alb"

    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_autoscaling_group" "example" {
    launch_configuration = aws_launch_configuration.example.name
    vpc_zone_identifier = data.aws_subnet_ids.default.ids

    target_group_arns = [aws_lb_target_group.asg.arn]
    health_check_type = "ELB"

    min_size = 2
    max_size = 10

    tag {
        key = "Name"
        value = "terraform-asg-example"
        propagate_at_launch = true
    }
}

resource "aws_launch_configuration" "example" {
    image_id = "ami-0085d4f8878cddc81"
    instance_type = "t2.micro"
    security_groups = [aws_security_group.instance.id]

    user_data = <<-EOF
                #!/bin/bash
                echo "Hello, World" > index.html
                nohup busybox httpd -f -p ${var.server_port} &
                EOF
    lifecycle {
        create_before_destroy = true
    }
}

resource "aws_security_group" "instance" {
    name = "terraform-example-instance"

    ingress {
        from_port = var.server_port
        to_port = var.server_port
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
}

data "aws_subnet_ids" "default" {
    vpc_id = data.aws_vpc.default.id
}

data "aws_vpc" "default" {
    default = true
}
Run Code Online (Sandbox Code Playgroud)

FGr*_*reg 15

无法重命名 ALB 目标组如果存在侦听器的评论中的问题链接:

向目标组添加生命周期规则,使其变为:

resource "aws_lb_target_group" "asg" {
    name     = "terraform-asg-example"
    port     = var.server_port
    protocol = "HTTP"
    vpc_id   = data.aws_vpc.default.id

    health_check {
      path                = "/"
      protocol            = "HTTP"
      matcher             = "200"
      interval            = 15
      timeout             = 3
      healthy_threshold   = 2
      unhealthy_threshold = 2
    }

    lifecycle {
      create_before_destroy = true
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,您还需要选择一种方法来更改目标组的名称。关于如何做到这一点,还有进一步的讨论和建议。

但一种可能的解决方案是简单地使用 guid 但忽略对名称的更改:

resource "aws_lb_target_group" "asg" {
    name     = "terraform-asg-example-${substr(uuid(), 0, 3)}"
    port     = var.server_port
    protocol = "HTTP"
    vpc_id   = data.aws_vpc.default.id

    health_check {
      path                = "/"
      protocol            = "HTTP"
      matcher             = "200"
      interval            = 15
      timeout             = 3
      healthy_threshold   = 2
      unhealthy_threshold = 2
    }

    lifecycle {
      create_before_destroy = true
      ignore_changes        = [name]
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Ruenzuo 这就是 `lifecycle{ignore_changes = [name] }` 的作用。它告诉 terraform 在计算是否应该替换资源时忽略资源名称的更改。我同意,这不是一个很好的解决方案,但它是我能找到的最好的解决方案 (3认同)

小智 11

比 @FGreg 的解决方案稍微简单一些,添加生命周期策略并切换到name可以name_prefix防止命名冲突的策略。

resource "aws_lb_target_group" "asg" {
    name_prefix = "terraform-asg-example"
    port = var.server_port
    protocol = "HTTP"
    vpc_id = data.aws_vpc.default.id
    lifecycle {
        create_before_destroy = true
    }

    health_check {
        path = "/"
        protocol = "HTTP"
        matcher = "200"
        interval = 15
        timeout = 3
        healthy_threshold = 2
        unhealthy_threshold = 2
    }
}
Run Code Online (Sandbox Code Playgroud)

无需uuidignore_changes设置。

  • 由于后缀太长,`name_prefix` 目前有 [6 个字符限制](https://github.com/hashicorp/terraform-provider-aws/issues/1666)。 (7认同)