2.terraform使用案例-蓝绿&金丝雀
使用案例
Terraform 是一种基础结构即代码工具,可让您在人类可读的配置文件中定义基础结构资源,以便对其进行版本控制、重用和共享。然后,您可以使用一致的工作流在基础架构的整个生命周期内安全高效地配置和管理基础架构。
使用负载均衡器进行蓝绿和金丝雀部署
使用 Terraform 部署、发布和监控应用程序。使用 Cloudflare 托管静态网站,在负载均衡器的帮助下部署新版本,并使用 Datadog 监控应用程序。
蓝绿部署和滚动部署(金丝雀测试)可让您逐步发布新软件,从而减少失败软件发布的潜在爆炸半径。此工作流允许您在停机时间几乎为零的情况下发布软件更新。
什么是蓝绿部署
在蓝绿部署中,当前服务部署充当蓝色环境。准备好发布更新时,将新的服务版本和底层基础结构部署到新的绿色环境中。验证绿色部署后,将流量从蓝色环境重定向到绿色环境。
- 测试绿色环境并在升级之前识别任何错误。在测试时,您的配置仍会将流量路由到蓝色环境,从而确保停机时间接近零。
- 通过将所有流量重定向回蓝色环境,在发生错误时轻松回滚到以前的部署。
什么是金丝雀发布
金丝雀发布将新版本的服务发布给一小组用户,从而在发生故障时减少爆炸半径。
您可以使用蓝绿部署进行金丝雀测试。绿色环境准备就绪后,负载均衡器会将一小部分流量发送到绿色环境(在本例中为 10%)。
如果 Canary 测试成功且没有错误,则可以逐步将流量定向到绿色环境(50/50 — 拆分流量)。最后,将所有流量重定向到绿色环境。验证新部署后,可以销毁旧的蓝色环境。绿色环境现在是当前的生产服务。
大体步骤
AWS 的应用程序负载均衡器 (ALB) 允许您定义在应用程序层路由流量的规则。这与传统负载均衡器不同,后者仅允许您在多个 EC2 实例之间平衡流量。您可以定义 ALB 的侦听器(规则)和目标组,以将流量动态路由到多个服务。这些规则允许您对绿色环境运行金丝雀测试并逐步提升绿色环境。
在本教程中,您将使用 Terraform 执行以下操作:
- 预置网络资源(VPC、安全组、负载均衡器)和一组 Web 服务器以用作蓝色环境。
- 预配第二组 Web 服务器以用作绿色环境。
- 功能切换添加到 Terraform 配置以定义潜在部署策略的列表。
- 使用功能切换执行金丝雀测试并逐步推广您的绿色环境。
先决条件
- 本地安装的 Terraform 1.3+
- 一个 AWS 账户
查看配置
克隆代码仓库
git clone https://github.com/hashicorp/learn-terraform-advanced-deployments.git
cd learn-terraform-advanced-deployments
此存储库包含多个 Terraform 配置文件:
- main.tf 定义 VPC、安全组和负载均衡器。
- variables.tf 定义配置使用的变量,例如区域、CIDR 块、子网数等。
- blue.tf 定义 2 个运行用户数据脚本以启动 Web 服务器的 AWS 实例。这些实例表示示例服务的“版本 1.0”。
- init-script.sh 包含用于启动 Web 服务器的脚本。
- terraform.tf 定义 terraform 块,该块指定 Terraform 二进制文件和 AWS 提供程序版本。
- .terraform.lock.hcl 是terraform依赖关系锁文件。
为方便起见,此示例将网络(负载均衡器)和应用程序配置合并到一个目录中。在生产环境中,应单独管理这些资源,以减少任何更改的潜在爆炸半径。
打开 main.tf .此文件使用 AWS 提供商部署本教程的基本基础设施,包括 VPC、子网、应用程序安全组和负载均衡器安全组。
// main.tf
resource "aws_lb" "app" {
name = "main-app-lb"
internal = false
load_balancer_type = "application"
subnets = module.vpc.public_subnets
security_groups = [module.lb_security_group.this_security_group_id]
}
resource "aws_lb_listener" "app" {
load_balancer_arn = aws_lb.app.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.blue.arn
}
}
该配置定义一个 aws_lb 资源。当负载均衡器收到请求时,它会评估由 aws_lb_listener.app 定义的侦听器规则,并将流量路由到相应的目标组。此负载均衡器当前将所有流量定向到端口 80 上的蓝色负载均衡目标组。
// blue.tf
resource "aws_instance" "blue" {
count = var.enable_blue_env ? var.blue_instance_count : 0
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
subnet_id = module.vpc.private_subnets[count.index % length(module.vpc.private_subnets)]
vpc_security_group_ids = [module.app_security_group.this_security_group_id]
user_data = templatefile("${path.module}/init-script.sh", {
file_content = "version 1.0 - #${count.index}"
})
tags = {
Name = "blue-${count.index}"
}
}
打开 blue.tf .此配置定义了两个启动 Web 服务器的 AWS 实例,这些实例返回文本 Version 1.0 - #${count.index} 。这表示示例应用程序的第一个版本,并指示哪个服务器响应了请求。
此文件还定义蓝色负载均衡器目标组,并使用 aws_lb_target_group_attachment 将蓝色实例附加到该目标组。
// blue.tf
resource "aws_lb_target_group" "blue" {
name = "blue-tg-${random_pet.app.id}-lb"
port = 80
protocol = "HTTP"
vpc_id = module.vpc.vpc_id
health_check {
port = 80
protocol = "HTTP"
timeout = 5
interval = 10
}
}
resource "aws_lb_target_group_attachment" "blue" {
count = length(aws_instance.blue)
target_group_arn = aws_lb_target_group.blue.arn
target_id = aws_instance.blue[count.index].id
port = 80
}
初始化并apply配置
在终端中,初始化您的 Terraform 配置。
$ terraform init
Initializing modules...
Downloading terraform-aws-modules/security-group/aws 4.17.1 for app_security_group...
- app_security_group in .terraform/modules/app_security_group/modules/web
- app_security_group.sg in .terraform/modules/app_security_group
Downloading terraform-aws-modules/security-group/aws 4.17.1 for lb_security_group...
- lb_security_group in .terraform/modules/lb_security_group/modules/web
- lb_security_group.sg in .terraform/modules/lb_security_group
Downloading terraform-aws-modules/vpc/aws 3.19.0 for vpc...
- vpc in .terraform/modules/vpc
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of hashicorp/random from the dependency lock file
- Installing hashicorp/aws v4.15.1...
- Installed hashicorp/aws v4.15.1 (signed by HashiCorp)
- Installing hashicorp/random v3.4.3...
- Installed hashicorp/random v3.4.3 (signed by HashiCorp)
Terraform has been successfully initialized!
# ...
应用您的配置。对提示的响应 yes 以确认操作。
$ terraform apply
# ...
Plan: 36 to add, 0 to change, 0 to destroy.
# ...
Apply complete! Resources: 36 added, 0 changed, 0 destroyed.
Outputs:
lb_dns_name = "main-app-bursting-slug-lb-976734382.us-west-2.elb.amazonaws.com"
验证蓝色环境
通过在浏览器中访问负载均衡器的 DNS 名称或从终端对其进行 cURL 来验证您的蓝色环境。
$ for i in `seq 1 5`; do curl $(terraform output -raw lb_dns_name); done
Version 1.0 - 0!
Version 1.0 - 1!
Version 1.0 - 0!
Version 1.0 - 0!
Version 1.0 - 1!
请注意,负载均衡器在蓝色环境中的两个实例之间均匀分配流量。
部署绿色环境
创建一个名为 green.tf 的新文件,并粘贴示例应用程序版本 1.1 的配置。
//green.tf
resource "aws_instance" "green" {
count = var.enable_green_env ? var.green_instance_count : 0
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
subnet_id = module.vpc.private_subnets[count.index % length(module.vpc.private_subnets)]
vpc_security_group_ids = [module.app_security_group.security_group_id]
user_data = templatefile("${path.module}/init-script.sh", {
file_content = "version 1.1 - #${count.index}"
})
tags = {
Name = "green-${count.index}"
}
}
resource "aws_lb_target_group" "green" {
name = "green-tg-${random_pet.app.id}-lb"
port = 80
protocol = "HTTP"
vpc_id = module.vpc.vpc_id
health_check {
port = 80
protocol = "HTTP"
timeout = 5
interval = 10
}
}
resource "aws_lb_target_group_attachment" "green" {
count = length(aws_instance.green)
target_group_arn = aws_lb_target_group.green.arn
target_id = aws_instance.green[count.index].id
port = 80
}
请注意,此配置与蓝色应用程序类似,只是 Web 服务器返回 green #${count.index} 。
将以下变量添加到 variables.tf 。
// variables.tf
variable "enable_green_env" {
description = "Enable green environment"
type = bool
default = true
}
variable "green_instance_count" {
description = "Number of instances in green environment"
type = number
default = 2
}
应用配置以部署绿色应用程序。请记住以 yes 确认您的申请。
terraform apply
# ...
Plan: 5 to add, 0 to change, 0 to destroy.
# ...
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
Outputs:
lb_dns_name = "main-app-bursting-slug-lb-976734382.us-west-2.elb.amazonaws.com"
添加功能切换以路由流量
即使您部署了绿色环境,负载均衡器也尚未将流量路由到该环境。
$ for i in `seq 1 5`; do curl $(terraform output -raw lb_dns_name); done
Version 1.0 - 1!
Version 1.0 - 0!
Version 1.0 - 0!
Version 1.0 - 0!
Version 1.0 - 1!
虽然您可以手动修改负载均衡器的目标组以包含绿色环境,但使用功能切换会为您编码此更改。在此步骤中,您将向配置添加一个 traffic_distribution 变量和 traffic_dist_map 个局部变量。该配置将根据 traffic_distribution 变量更新目标组的权重。
首先,将本地值和流量分配变量的配置添加到 variables.tf 。
// variables.tf
locals {
traffic_dist_map = {
blue = {
blue = 100
green = 0
}
blue-90 = {
blue = 90
green = 10
}
split = {
blue = 50
green = 50
}
green-90 = {
blue = 10
green = 90
}
green = {
blue = 0
green = 100
}
}
}
variable "traffic_distribution" {
description = "Levels of traffic distribution"
type = string
}
请注意,局部变量定义了五个流量分布。每个流量分配指定相应目标组的权重:
blue 目标分布是当前分布 — 负载均衡器将 100% 的流量路由到蓝色环境,0% 路由到绿色环境。
blue-90 目标分布模拟金丝雀测试。此金丝雀测试将 90% 的流量路由到蓝色环境,将 10% 的流量路由到绿色环境。
split 目标分布通过增加绿色环境的流量建立在金丝雀测试之上。这会在蓝色和绿色环境 (50/50) 之间平均分配流量。
green-90 目标分布增加流向绿色环境的流量,将 90% 的流量发送到绿色环境,将 10% 的流量发送到蓝色环境。
green 目标分配完全提升了绿色环境 — 负载均衡器将 100% 的流量路由到绿色环境。
将 aws_lb_listener.app 的 default_action 块修改为 main.tf 以匹配以下内容。该配置使用 lookup 来设置目标组的权重。请注意,如果未设置任何值,则配置默认为将所有流量定向到蓝色环境。
// main.tf
resource "aws_lb_listener" "app" {
## ...
default_action {
type = "forward"
- target_group_arn = aws_lb_target_group.blue.arn
+ forward {
+ target_group {
+ arn = aws_lb_target_group.blue.arn
+ weight = lookup(local.traffic_dist_map[var.traffic_distribution], "blue", 100)
+ }
+ target_group {
+ arn = aws_lb_target_group.green.arn
+ weight = lookup(local.traffic_dist_map[var.traffic_distribution], "green", 0)
+ }
+ stickiness {
+ enabled = false
+ duration = 1
+ }
+ }
}
}
开始金丝雀
Apply 配置,将 traffic_distribution 变量设置为 blue-90 的配置以运行金丝雀测试。请记住用 yes .
terraform apply -var 'traffic_distribution=blue-90'
# ...
Terraform will perform the following actions:
aws_lb_listener.app will be updated in-place
~ resource "aws_lb_listener" "app" {
id = "arn:aws:elasticloadbalancing:us-west-2:561656980159:listener/app/main-app-bursting-slug-lb/ace5fc1e7af739e9/1823f9a776b1232e"
(4 unchanged attributes hidden)
~ default_action {
- target_group_arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/blue-tg-bursting-slug-lb/c8ed1be403ce253c" -> null
(2 unchanged attributes hidden)
+ forward {
+ stickiness {
+ duration = 1
+ enabled = false
}
+ target_group {
+ arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/blue-tg-bursting-slug-lb/c8ed1be403ce253c"
+ weight = 100
}
+ target_group {
+ arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/green-tg-bursting-slug-lb/ade9778242aab1c2"
+ weight = 0
}
}
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
# ...
aws_lb_listener.app: Modifying... [id=arn:aws:elasticloadbalancing:us-west-2:561656980159:listener/app/main-app-bursting-slug-lb/ace5fc1e7af739e9/1823f9a776b1232e]
aws_lb_listener.app: Modifications complete after 0s [id=arn:aws:elasticloadbalancing:us-west-2:561656980159:listener/app/main-app-bursting-slug-lb/ace5fc1e7af739e9/1823f9a776b1232e]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Outputs:
lb_dns_name = "main-app-bursting-slug-lb-976734382.us-west-2.elb.amazonaws.com"
验证金丝雀流量
验证负载均衡器现在是否将 10% 的流量路由到绿色环境。
$ for i in `seq 1 10`; do curl $(terraform output -raw lb_dns_name); done
Version 1.0 - 0!
Version 1.0 - 0!
Version 1.0 - 1!
Version 1.0 - 0!
Version 1.1 - 1!
Version 1.0 - 0!
Version 1.0 - 0!
Version 1.0 - 1!
Version 1.0 - 0!
Version 1.0 - 1!
请注意,负载均衡器现在将 10% 的流量路由到绿色环境。
增加绿色环境的流量
现在,金丝雀部署成功,请增加到绿色环境的流量。
apply 配置,将 traffic_distribution 变量设置为 split 的配置,以增加到绿色环境的流量。请记住用 yes .
$ terraform apply -var 'traffic_distribution=split'
# ...
Terraform will perform the following actions:
aws_lb_listener.app will be updated in-place
~ resource "aws_lb_listener" "app" {
id = "arn:aws:elasticloadbalancing:us-west-2:561656980159:listener/app/main-app-bursting-slug-lb/ace5fc1e7af739e9/1823f9a776b1232e"
(4 unchanged attributes hidden)
~ default_action {
(2 unchanged attributes hidden)
~ forward {
+ target_group {
+ arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/blue-tg-bursting-slug-lb/c8ed1be403ce253c"
+ weight = 50
}
- target_group {
- arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/blue-tg-bursting-slug-lb/c8ed1be403ce253c" -> null
- weight = 90 -> null
}
- target_group {
- arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/green-tg-bursting-slug-lb/ade9778242aab1c2" -> null
- weight = 10 -> null
}
+ target_group {
+ arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/green-tg-bursting-slug-lb/ade9778242aab1c2"
+ weight = 50
}
(1 unchanged block hidden)
}
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
# ...
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Outputs:
lb_dns_name = "main-app-bursting-slug-lb-976734382.us-west-2.elb.amazonaws.com"
验证滚动部署流量
验证负载均衡器现在是否将流量拆分到蓝色和绿色环境。
$ for i in `seq 1 10`; do curl $(terraform output -raw lb_dns_name); done
Version 1.0 - 0!
Version 1.1 - 1!
Version 1.0 - 1!
Version 1.1 - 1!
Version 1.1 - 0!
Version 1.0 - 0!
Version 1.0 - 1!
Version 1.0 - 1!
Version 1.1 - 1!
Version 1.1 - 0!
请注意,负载均衡器现在在蓝色和绿色环境之间平均分配流量。
完全切换
由于 Canary 部署和滚动部署都成功,因此将负载均衡器的流量 100% 路由到绿色环境以提升它。
通过将 traffic_distribution 变量设置为 green 来应用您的配置来提升绿色环境。请记住用 yes .
$ terraform apply -var 'traffic_distribution=green'
# ...
Terraform will perform the following actions:
aws_lb_listener.app will be updated in-place
~ resource "aws_lb_listener" "app" {
id = "arn:aws:elasticloadbalancing:us-west-2:561656980159:listener/app/main-app-bursting-slug-lb/ace5fc1e7af739e9/1823f9a776b1232e"
(4 unchanged attributes hidden)
~ default_action {
(2 unchanged attributes hidden)
~ forward {
+ target_group {
+ arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/blue-tg-bursting-slug-lb/c8ed1be403ce253c"
+ weight = 0
}
- target_group {
- arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/blue-tg-bursting-slug-lb/c8ed1be403ce253c" -> null
- weight = 50 -> null
}
+ target_group {
+ arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/green-tg-bursting-slug-lb/ade9778242aab1c2"
+ weight = 100
}
- target_group {
- arn = "arn:aws:elasticloadbalancing:us-west-2:561656980159:targetgroup/green-tg-bursting-slug-lb/ade9778242aab1c2" -> null
- weight = 50 -> null
}
(1 unchanged block hidden)
}
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
# ...
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Outputs:
lb_dns_name = "main-app-bursting-slug-lb-976734382.us-west-2.elb.amazonaws.com"
验证负载均衡器流量
验证您的负载均衡器现在是否将所有流量路由到绿色环境。
$ for i in `seq 1 5`; do curl $(terraform output -raw lb_dns_name); done
Version 1.1 - 1!
Version 1.1 - 0!
Version 1.1 - 0!
Version 1.1 - 0!
Version 1.1 - 1!
使用此部署策略,您成功地提升了绿色环境,停机时间几乎为零。
缩小蓝色环境
验证负载均衡器将所有流量定向到绿色环境后,可以安全地禁用蓝色环境。
应用您的配置,通过将 traffic_distribution 变量设置为 green 和将 enable_blue_env 设置为 false 来销毁蓝色环境资源。请记住用 yes 确认您的申请。
$ terraform apply -var 'traffic_distribution=green' -var 'enable_blue_env=false'
#...
Terraform used the selected providers to generate the
following execution plan. Resource actions are indicated with
the following symbols:
- destroy
Terraform will perform the following actions:
#...
Plan: 0 to add, 0 to change, 4 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
#...
部署新版本
在本教程中,您在蓝色环境中部署了应用程序的版本 1.0,在绿色环境中部署了新版本 1.1。当您推广绿色环境时,它就变成了当前的生产环境。将下一个版本部署到蓝色环境,通过交替使用蓝色和绿色环境,最大限度地减少对现有配置的修改。
修改 blue.tf 中的 aws_instance.blue 和 tags 块以显示新版本号 1.2。
// blue.tf
resource "aws_instance" "blue" {
## ...
user_data = templatefile("${path.module}/init-script.sh", {
- file_content = "version 1.0 - #${count.index}"
+ file_content = "version 1.2 - #${count.index}"
})
tags = {
Name = "blue-${count.index}"
}
}
启用新版本环境
应用配置以预配新版本的基础结构。请记住以 yes 确认您的申请。将 traffic_distribution 变量设置为 green ,以将所有流量直接传输到绿色环境中的当前生产部署。
$ terraform apply -var 'traffic_distribution=green'
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
#...
Plan: 4 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
#...
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
lb_dns_name = "main-app-infinite-toucan-lb-937939527.us-west-2.elb.amazonaws.com"