Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto-Scaling Groups Don't Update on Launch Configuration Change

I have an AWS Auto-Scaling Group, a Launch Configuration, and an Auto-Scaling Group Policy defined in Terraform like this:

resource "aws_autoscaling_group" "default" {
  name = "..."

  health_check_type = "EC2"
  vpc_zone_identifier = ["${...}"]

  min_size = "${var.asg_capacity}"
  max_size = "${var.asg_capacity * 2}"
  desired_capacity = "${var.asg_capacity}"

  launch_configuration = "${aws_launch_configuration.default.id}"

  termination_policies = ["OldestInstance"]
}

resource "aws_autoscaling_policy" "default" {
  name = "..."
  autoscaling_group_name = "${aws_autoscaling_group.default.name}"

  scaling_adjustment = "${var.asg_capacity}"
  adjustment_type = "ChangeInCapacity"
  cooldown = 300
}

resource "aws_launch_configuration" "default" {
  name_prefix = "..._"

  image_id = "${var.coreos_ami_id}"
  instance_type = "${var.ec2_instance_type}"
  iam_instance_profile = "${aws_iam_instance_profile.default.arn}"
  key_name = "..."

  security_groups = ["${aws_security_group.default.id}"]

  user_data = "${data.template_file.cloud_init.rendered}"

  lifecycle {
    create_before_destroy = true
  }
}

When I change my user data, a new launch configuration is created and then attached to the auto-scaling group. I would assume that this would cause the auto-scaling group to scale up by var.asg_capacity instances, wait 300 seconds, and then tear down the old ones as per OldestInstance.

When I have done similar things in CloudFormation, I have used the following configuration options:

ASG:
  Type: AWS::AutoScaling::AutoScalingGroup
  UpdatePolicy:
    AutoScaleRollingUpdate:
      # during a scale, 6 instances in service
      MaxBatchSize: 3
      MinInstancesInService: 3
      PauseTime: PT5M
  Properties:
    ...

Is there an analog for this in Terraform? I would really like my auto-scaling groups to change when I change the launch configuration.

like image 747
Naftuli Kay Avatar asked Dec 05 '16 23:12

Naftuli Kay


2 Answers

I would assume that this would cause the auto-scaling group to scale up by var.asg_capacity instances, wait 300 seconds, and then tear down the old ones as per OldestInstance.

This assumption, unfortunately, is incorrect. When you change the launch configuration, the only thing that happens is a new launch configuration is created in your AWS account and associated with the Auto Scaling Group (ASG). That means all future Instances in that ASG will launch with the new launch configuration. However, merely changing the launch configuration does not trigger the launch of any instances, so you won't see your changes.

To force new instances to launch, you have two options:

Updated answer from 2022

In 2020, AWS introduced AWS instance refresh, which is a native way to roll out changes to Auto Scaling Groups. If you configure the instance_refresh block in your aws_autoscaling_group resource, then when you make a change to the launch configuration and run apply, AWS will kick off the instance refresh process automatically (note: this process runs in the background, so apply will complete very quickly, but the refresh will happen after, often taking 5-30 min).

resource "aws_launch_configuration" "example" {
  image_id        = var.ami
  instance_type   = var.instance_type

  user_data = data.template_file.user_data.rendered

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_autoscaling_group" "example" {
  name                 = "example-asg"
  launch_configuration = aws_launch_configuration.example.id
  availability_zones   = data.aws_availability_zones.all.names

  min_size         = var.min_size
  max_size         = var.max_size

  instance_refresh {
    strategy = "Rolling"
    preferences {
      min_healthy_percentage = 50
    }
  }
}

Original answer from 2016

You can use some native Terraform functionality to do a rolling deploy:

  1. Configure the name parameter of the ASG to depend directly on the name of the launch configuration. That way, each time the launch configuration changes (which it will when you update the AMI or User Data), Terraform will try to replace the ASG.
  2. Set the create_before_destroy parameter of the ASG to true, so each time Terraform tries to replace it, it will create the replacement before destroying the original.
  3. Set the min_elb_capacity parameter of the ASG to the min_size of the cluster so that Terraform will wait for at least that many servers from the new ASG to register in the ELB before it'll start destroying the original ASG.

Here's a rough idea of what the Terraform code would look like:

resource "aws_launch_configuration" "example" {
  image_id        = "${var.ami}"
  instance_type   = "${var.instance_type}"

  user_data = "${data.template_file.user_data.rendered}"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_autoscaling_group" "example" {
  name                 = "${var.cluster_name}-${aws_launch_configuration.example.name}"
  launch_configuration = "${aws_launch_configuration.example.id}"
  availability_zones   = ["${data.aws_availability_zones.all.names}"]

  min_size         = "${var.min_size}"
  max_size         = "${var.max_size}"
  min_elb_capacity = "${var.min_size}"

  lifecycle {
    create_before_destroy = true
  }
}

For a fully working example, check out the zero-downtime deployment example code from the book Terraform: Up & Running.

like image 75
Yevgeniy Brikman Avatar answered Nov 19 '22 21:11

Yevgeniy Brikman


As of version 3.22.0 of the AWS provisioner, you can add an instance_refresh configuration block to your aws_autoscaling_group resource. The simplest config that can possibly work is:

instance_refresh {
  strategy = "Rolling"
}
like image 4
James_pic Avatar answered Nov 19 '22 19:11

James_pic