Skip to content

Instantly share code, notes, and snippets.

@akhoury6
Last active April 20, 2022 18:58
Show Gist options
  • Save akhoury6/c70fab357cae6b32f93948133be54fc3 to your computer and use it in GitHub Desktop.
Save akhoury6/c70fab357cae6b32f93948133be54fc3 to your computer and use it in GitHub Desktop.
Terraform Blue Green Deployments

Do we have a recommended blue/green deployment/design pattern on pTFE?

We do enable blue/green deployments, but there isn't a "one-size-fits-all" method to perform them; instead, we have a three-step process to come up with design patterns for any application. What we will do here is create a state machine for the deployments, with Terraform being the tool to move between states.

First, we break the blue-green deployment down into desired states. Second, we determine what mechanisms are needed to make the changes between the states. Third, we put it all into a single flow using a combination of terraform functions, cloud components, and/or scripts as needed.

So let's walk through a simple application deployment: a 2-tier web application, with a single instance, being routed to via a Load Balancer. Note that the site's DNS name is pointing to the Load Balancer.

States Needed

  1. Green application online, blue application down, traffic goes to green
  2. Green application online, blue application online, traffic goes to green
  3. Green application online, blue application online, traffic goes to blue
  4. Green application down, blue application online, traffic goes to blue

There are three 'sub-states' that can change: the state of the Green, the state of Blue, and the direction of traffic. So we need mechanisms to manage those three things. Additionally, we know that the actual code deployed is going to change, where the main difference is (typically) which git commit we're checking out.

We already know that the Blue and Green application state can be changed via Terraform because that's simply deploying and destroying instances. The easiest way to achieve this in Terraform code is to lay out a resource for the "blue" instance and "green" instance separately, and use variables to make the differentiation between the two instances. So we'll rely on Terraform to do that.

However, Terraform does not control traffic flow -- load balancers and DNS do. So instead of relying on Terraform to do it, we'll use Terraform to issue the correct commands to those components to do what we need them to do.

Note: The following code is untested, and there are a few required paramters left out (such as which ami_id to use) to highlight the important parts for this example

This allows us to describe the state that we want our system to be in, and uses Terraform as the engine to change between states. However, we still need a "driver" to tell terraform to switch between each of the four desired states - which can be as simple as a script that calls Terraform with the correct sequence of variables, including a script located on a CI/CD server.

(Changes highlighted in red)

State 1 (initial state): green = true blue = false route_to = green

State 2: green = true blue = true route_to = green

State 3: green = true blue = true route_to = blue

State 4: green = false blue = true route_to = blue

This same principle can be used on any application, no matter which components are used. For more complex architectures, the entire application can also be placed into a module; so instead of using two resource "aws_instance" blocks, you'd use two module "myapp" blocks with all of the application components contained within that module.

# BEGIN webmodule.tf
variable "git_repo" {} # The address to the git repo for the applicaiton
variable "blue" {} # This would be "true" or "false"
variable "green" {} # This would be "true" or "false"
variable "git_commit_blue" {} # The git commit hash for the "blue" environment
variable "git_commit_green" {} # The git commit hash for the "green" environment
variable "route_to" {} # This would be "blue" or "green"
resource "aws_instance" "web_blue" {
count = "${var.blue == true ? 1 : 0}"
tags {
Name = "Web_Tier_Blue"
}
provisioner "remote-exec" {
inline = [
"git clone ${var.git_repo} my_repo --depth=1",
"cd my_repo",
"git reset --hard ${var.git_commit_blue}",
]
}
resource "aws_instance" "web_green" {
count = "${var.green == true ? 1 : 0}"
tags {
Name = "Web_Tier_Green"
}
provisioner "remote-exec" {
inline = [
"git clone ${var.git_repo} my_repo --depth=1",
"cd my_repo",
"git reset --hard ${var.git_commit_green}"
]
}
resource "aws_elb" "bluegreen_elb" {
name = "bluegreen_elb"
listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 80
lb_protocol = "http"
}
instances = ["${var.route_to == "blue" ? aws_instance.web_blue.id : aws_instance.web_green.id}"]
cross_zone_load_balancing = true
idle_timeout = 400
connection_draining = true
connection_draining_timeout = 400
tags {
Name = "bluegreen_elb"
PointingTo = "${var.route_to}"
}
}
# END webmodule.tf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment