dot Redis 8 来了——而且是开源的

了解更多

使用 Terraform 更新 AWS Autoscaling Groups 并保留所需容量

介绍

Terraform 可能是部署基础设施的最佳工具,因此它是在 AWS 中部署自动伸缩组的明显选择。我们正在进行的项目要求我们在 AWS 中的实例上部署一项服务。该服务是无状态的,并且具有简单的配置,可以使用 cloud-init轻松配置。

我们的目标是使用 Terraform 配置所有内容。每次需要进行配置更改时,我们都会更新 Terraform 配置并再次应用。我们希望在进行任何配置更改时拥有干净的实例,而不是就地更新。

在整个示例中,为了简洁起见,我们省略了一些配置元素的创建。 其他的作为变量注入,并且此处也未显示。

初始配置

首先创建启动模板

# Query for the latest Ubuntu 18.04 AMI in the region
data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_launch_template" "mysvc" {
  name                                 = "mysvc"
  ebs_optimized                        = true
  image_id                             = data.aws_ami.ubuntu.id
  instance_initiated_shutdown_behavior = "terminate"
  instance_type                        = "c5.large"
  key_name                             = var.key_name
  network_interfaces {
    associate_public_ip_address = true
    subnet_id                   = var.subnet_id
    security_groups             = var.security_groups
    delete_on_termination       = true
  }
  tag_specifications {
    resource_type = "instance"
    tags = {
      Name          = "mysvc"
    }
  }
  user_data = base64encode(templatefile("${path.module}/user_data.yaml", {
    web_password    = var.web_password,
    ssl_private_key = tls_private_key.mysvc.private_key_pem,
    ssl_certificate = tls_self_signed_cert.mysvc.cert_pem
  }))

  # we don't want to create a new template just because there is a newer AMI
  lifecycle {
    ignore_changes = [
      image_id,
    ]
  }
}

启动模板定义了实例的关键元素,并且还从用户数据文件中加载了服务的配置。 用户数据文件是一个模板,它评估为有效的 cloud-init 配置文件。

现在让我们创建自动伸缩组

resource "aws_autoscaling_group" "mysvc" {
  name                      = "mysvc-${aws_launch_template.mysvc.latest_version}"
  health_check_type         = "ELB"
  health_check_grace_period = 120
  termination_policies      = ["OldestInstance"]
  launch_template {
    id      = aws_launch_template.mysvc.id
    version = aws_launch_template.mysvc.latest_version
  }
  min_size = 1
  max_size = 10

  lifecycle {
    create_before_destroy = true
  }
  target_group_arns = [aws_lb_target_group.svc.arn]
}

这里有几点需要注意

  1. 我们使用启动模板的最新版本来命名自动伸缩组。 这是为了确保在更新启动模板时,我们正在创建一个新的自动伸缩组。
  2. 在创建新的自动伸缩组时,我们希望首先创建新的自动伸缩组,然后再销毁先前的自动伸缩组,因此我们使用生命周期指令 create_before_destroy = true。 由于我们的服务位于 ELB 后面,因此这将确保在配置更改期间不会停机。

这很棒,并且完全满足了我们的需求。 或者真的吗?

当我们扩展组以拥有更多实例并且更改配置时会发生什么?使用当前配置,我们将用仅包含 1 个实例的组替换正在运行的组(例如,5 个实例)。 我们希望能够创建一个包含 5 个实例的新组。

更好的配置

为了保留所需容量的当前值,我们需要首先以某种方式获取它。

让我们获取当前的启动模板(如果存在)

data "aws_launch_template" "current" {
  filter {
    name   = "launch-template-name"
    values = ["mysvc"]
  }
}

现在,让我们看看是否有自动伸缩组使用此启动模板的最新版本

data "aws_autoscaling_groups" "current" {
  filter {
    name   = "key"
    values = ["mysvc-template-version"]
  }
  filter {
    name   = "value"
    values = ["${coalesce(data.aws_launch_template.current.latest_version, 0)}"]
  }
}

我们正在使用您尚未看到添加到自动伸缩组的标签。 请参阅下面的自动伸缩组资源中的更改。

这里的想法是 data.aws_launch_template.current.latest_version 有一个值,或者它是 null。 如果有值,我们希望最多有一个大小的列表。 如果它为 null,那么我们正在寻找一个不存在的自动伸缩组,并获得一个大小为零的列表。

现在我们可以获取有关当前自动伸缩组的信息

data "aws_autoscaling_group" "current" {
  count = length(data.aws_autoscaling_groups.current.names)
  name  = data.aws_autoscaling_groups.current.names[count.index]
}

使用 count 本质上是根据 data.aws_autoscaling_groups.current.names 的大小创建一或零个资源。

现在我们可以更新自动伸缩组资源

resource "aws_autoscaling_group" "mysvc" {
  name                      = "mysvc-${aws_launch_template.mysvc.latest_version}"
  health_check_type         = "ELB"
  health_check_grace_period = 120
  termination_policies      = ["OldestInstance"]
  launch_template {
    id      = aws_launch_template.mysvc.id
    version = aws_launch_template.mysvc.latest_version
  }
  min_size = 1
  max_size = 10

  desired_capacity = length(data.aws_autoscaling_groups.current.names) > 0 ? data.aws_autoscaling_group.current[0].desired_capacity : var.default_desired_capacity

  tag {
    key                 = "mysvc-template-version"
    value               = aws_launch_template.mysvc.latest_version
    propagate_at_launch = false
  }

  lifecycle {
    create_before_destroy = true
  }
  target_group_arns = [aws_lb_target_group.svc.arn]
}

在这里,我们通过检查是否可以找到任何当前的自动伸缩组来定义所需的容量。 如果可以,那么我们将使用第一个(也是唯一的)自动伸缩组的所需容量值。 但是,如果找不到任何当前组,则使用默认值。 我们还记得添加我们需要查询自动伸缩组的标签。

考虑 Terraform

Terraform 非常酷,可以用来做很多事情。 有些需要更多跳跃,但值得拥有一个单一工具中的所有内容。