Welcome to Day 5 of 21 Days of AWS using Terraform, So far we build VPC ,EC2 and Application Load Balancer, let’s build Auto Scaling Group and rather then creating an instance via EC2 module, let auto-scaling group take care of it based on load.
What is Auto Scaling?
What auto-scaling will do, it ensures that we have a correct number of EC2 instances to handle the load of your applications.
How Auto Scaling works
- It all started with the creation of the Auto Scaling group which is the collection of EC2 instances.
- You can specify a minimum number of instances and AWS EC2 Auto Scaling ensures that your group never goes below this size.
- The same way we can specify the maximum number of instances and AWS EC2 Auto Scaling ensures that your group never goes above this size.
- If we specify the desired capacity, AWS EC2 Auto Scaling ensures that your group has this many instances.
- Configuration templates(launch template or launch configuration): Specify Information such as AMI ID, instance type, key pair, security group
- If we specify scaling policies then AWS EC2 Auto Scaling can launch or terminate instances as demand on your application increased or decreased. For eg: We can configure a group to scale based on the occurrence of specified conditions(dynamic scaling) or on a schedule.
Reference:https://docs.aws.amazon.com/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html
In the above example, Auto Scaling has
- A minimum size of one instance.
- Desired Capacity of two instances.
- The maximum size of four instances.
- Scaling policies we define adjust the minimum or a maximum number of instances based on the criteria we specify.
Step1: The first step in creating the AutoScaling Group is to create a launch configuration, which specifies how to configure each EC2 instance in the autoscaling group.
resource "aws_launch_configuration" "my-test-launch-config" {
image_id = "ami-01ed306a12b7d1c96"
instance_type = "t2.micro"
security_groups = ["${aws_security_group.my-asg-sg.id}"]
user_data = <<-EOF
#!/bin/bash
yum -y install httpd
echo "Hello, from Terraform" > /var/www/html/index.html
service httpd start
chkconfig httpd on
EOF
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group" "my-asg-sg" {
name = "my-asg-sg"
vpc_id = "${var.vpc_id}"
}
resource "aws_security_group_rule" "inbound_ssh" {
from_port = 22
protocol = "tcp"
security_group_id = "${aws_security_group.my-asg-sg.id}"
to_port = 22
type = "ingress"
cidr_blocks = ["0.0.0.0/0"]
}
resource "aws_security_group_rule" "inbound_http" {
from_port = 80
protocol = "tcp"
security_group_id = "${aws_security_group.my-asg-sg.id}"
to_port = 80
type = "ingress"
cidr_blocks = ["0.0.0.0/0"]
}
resource "aws_security_group_rule" "outbound_all" {
from_port = 0
protocol = "-1"
security_group_id = "${aws_security_group.my-asg-sg.id}"
to_port = 0
type = "egress"
cidr_blocks = ["0.0.0.0/0"]
}
- Most of the parameters look similar to EC2 configuration except lifecycle parameter which is required for using a launch configuration with an ASG
- One of the available
lifecycle
settings arecreate_before_destroy
, which, if set totrue
, tells Terraform to always create a replacement resource before destroying the original resource. For example, if you setcreate_before_destroy
totrue
on an EC2 Instance, then whenever you make a change to that Instance, Terraform will first create a new EC2 Instance, wait for it to come up, and then remove the old EC2 Instance. - The catch with the
create_before_destroy
the parameter is that if you set it totrue
on resource X, you also have to set it totrue
on every resource that X depends on (if you forget, you’ll get errors about cyclical dependencies).
https://www.terraform.io/docs/configuration/resources.html#lifecycle-lifecycle-customizations
Step2: Define auto-scaling group
data "aws_availability_zone" "available" {}
resource "aws_autoscaling_group" "example" {
launch_configuration = "${aws_launch_configuration.my-test-launch-config.name}"
vpc_zone_identifier = "${var.subnet_id}"
availability_zones = ["${data.aws_availability_zone.available.name}"]
target_group_arns = ["${var.target_group_arn}"]
health_check_type = "ELB"
min_size = 2
max_size = 10
tag {
key = "Name"
value = "my-test-asg"
propagate_at_launch = true
}
}
- Next step is to create an auto-scaling group using the aws_autoscaling_group resource.
- This autoscaling group will spin a minimum of 2 instance and a maximum of 10 instances OR completely based on your requirement.
- It’s going to use the launch configuration we created in the earlier step 1.
- We are using an aws_availibity_zone resource which will make sure instance will be deployed in different Availability Zone.
- v
pc_zone_identifier
A list of subnet IDs to launch resources in, we will get this value from vpc module. target_group_arns
A list ofaws_alb_target_group
ARNs, for use with Application or Network Load Balancing, this value we will get from ALB modulehealth_check_type
– Can be “EC2” or “ELB”. Controls how health checking is done. In this case we are using ELB.
- In case if you are planning to use the default VPC, you can use data sources to look up the data for your default VPC
data "aws_vpc" "default" {
default = true
}
- We can combine this with another data source, aws_subnet_ids to lookup the subnets within that VPC.
data "aws_subnet_ids" "default" {
vpc_id = data.aws_vpc.default.id
}
- Now to use it within your code
vpc_zone_identifier = data.aws_subnet_ids.default.ids
- We need to make slight changes to our VPC module outputs.tf file, as the output of subnet block, will not act as an input to autoscaling group vpc_zone_identifier
output "public_subnets" {
value = "${aws_subnet.public_subnet.*.id}"
}
- Same way we need to output the target arn from alb module so that it can act as an input to auto-scaling group module(target_group_arn)
output "alb_target_group_arn" {
value = "${aws_lb_target_group.my-target-group.arn}"
}
- Auto-Scaling Module will look like this
module "auto_scaling" {
source = "./auto_scaling"
vpc_id = "${module.vpc.vpc_id}"
subnet_id = "${module.vpc.public_subnets}"
target_group_arn = "${module.alb.alb_target_group_arn}"
}
- Final word, as we are now using auto-scale/auto-launch configuration to spin up our instance we probably don’t need EC2 module. I am leaving it for the time being but for ALB let me comment out the code
/*resource "aws_lb_target_group_attachment" "my-alb-target-group-attachment1" {
target_group_arn = "${aws_lb_target_group.my-target-group.arn}"
target_id = "${var.instance1_id}"
port = 80
}
resource "aws_lb_target_group_attachment" "my-alb-target-group-attachment2" {
target_group_arn = "${aws_lb_target_group.my-target-group.arn}"
target_id = "${var.instance2_id}"
port = 80
}*/
Final auto-scaling code will look like this
provider "aws" {
region = "us-west-2"
}
resource "aws_launch_configuration" "my-test-launch-config" {
image_id = "ami-01ed306a12b7d1c96"
instance_type = "t2.micro"
security_groups = ["${aws_security_group.my-asg-sg.id}"]
user_data = <<-EOF
#!/bin/bash
yum -y install httpd
echo "Hello, from Terraform" > /var/www/html/index.html
service httpd start
chkconfig httpd on
EOF
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "example" {
launch_configuration = "${aws_launch_configuration.my-test-launch-config.name}"
vpc_zone_identifier = "${var.subnet_id}"
target_group_arns = ["${var.target_group_arn}"]
health_check_type = "ELB"
min_size = 2
max_size = 10
tag {
key = "Name"
value = "my-test-asg"
propagate_at_launch = true
}
}
resource "aws_security_group" "my-asg-sg" {
name = "my-asg-sg"
vpc_id = "${var.vpc_id}"
}
resource "aws_security_group_rule" "inbound_ssh" {
from_port = 22
protocol = "tcp"
security_group_id = "${aws_security_group.my-asg-sg.id}"
to_port = 22
type = "ingress"
cidr_blocks = ["0.0.0.0/0"]
}
resource "aws_security_group_rule" "inbound_http" {
from_port = 80
protocol = "tcp"
security_group_id = "${aws_security_group.my-asg-sg.id}"
to_port = 80
type = "ingress"
cidr_blocks = ["0.0.0.0/0"]
}
resource "aws_security_group_rule" "outbound_all" {
from_port = 0
protocol = "-1"
security_group_id = "${aws_security_group.my-asg-sg.id}"
to_port = 0
type = "egress"
cidr_blocks = ["0.0.0.0/0"]
}
GitHub Link
https://github.com/100daysofdevops/21_days_of_aws_using_terraform/tree/master/auto_scaling
Complete Code
https://github.com/100daysofdevops/21_days_of_aws_using_terraform
Looking forward for you guys to join this journey
- Website: https://100daysofdevops.com/
- Twitter: @100daysofdevops OR @lakhera2015
- Facebook: https://www.facebook.com/groups/795382630808645/
- Medium: https://medium.com/@devopslearning
- GitHub: https://github.com/100daysofdevops/100daysofdevops
- Slack: https://join.slack.com/t/100daysofdevops/shared_invite/enQtNzg1MjUzMzQzMzgxLWM4Yjk0ZWJiMjY4ZWE3ODBjZjgyYTllZmUxNzFkNTgxZjQ4NDlmZjkzODAwNDczOTYwOTM2MzlhZDNkM2FkMDA
- YouTube Channel: https://www.youtube.com/user/laprashant/videos?view_as=subscriber
In addition to that, I am going to host 5 meetups whose aim is to build the below architecture.
- Meetup: https://www.meetup.com/100daysofdevops
- Day1(Nov 10): Introduction to Terraform https://www.meetup.com/100daysofdevops/events/266192294/
- Day 2(Nov 16): Building VPC using Terraform
- Day 3(Nov 17): Creating EC2 Instance inside this VPC using Terraform
- Day 4(Nov 23): Adding Application Load Balancer and Auto-Scaling to the EC2 instance created on Day 3
- Day5(Nov 24): Add Backend MySQL Database and CloudWatch Alarm using Terraform