支持平行运行最新稳定版本的代码的发布

75次阅读
没有评论

问题描述

在生产环境(AWS)中有一个服务,遵循不可变服务器模式。他的部署过程如下:
1. 使用 Packer 创建新的 AMI。
2. 创建新的 CloudFormation 堆栈,起始大小为 1 的自动伸缩组。

当确认新版本的发布是可行的时候,用户希望能够增加实例数量,并最终关闭旧实例,并从之前版本的 CloudFormation 堆栈中移除。

初始版本的部署中,用户使用了一个堆栈,对其进行了就地更新。对于正常发布,这意味着 CloudFormation 会修改自动伸缩组,以指向新的 AMI。然后用户不得不要么终止现有实例,要么增加自动伸缩组以获取正在运行发布的实例。

现在用户更倾向于使用 Terraform 而不是 CloudFormation。他想知道是否可以使用 Terraform 来适应他的部署方式。用户不介意使用其他工具,他的主要目标是保留以下基本概念:
1. 允许发布新版本,而不影响稳定版本。
2. 不直接更新现有设置,而是创建新的资源并终止旧资源。

用户之前使用了一些 Terraform 来管理基础设施的一部分,保持状态在 S3 存储桶中。在这个问题中,他在问题中提供了一个简化的 Terraform 配置,但他还没有弄清楚如何将流量切换到新的实例。所有实例都应该在负载均衡器(ELB)后面,用户认为可以使用独立的 ELB 设置,将流量分发到旧版本和新版本的实例。

用户希望了解以下问题:
1. 使用不同的 S3 键来存储 Terraform 状态是否具有与使用单独的 CloudFormation 堆栈相同的效果?(用户寻找的是一种能够让多个 Terraform 设置不会相互干扰的方法。)
2. 是否有解决将所有实例(来自旧版本和新版本)放在共享 ELB 后面的方法?
3. 用户读到可以在 Terraform 中导出资源。你认为,是否可以创建一个用于 ELB 的 Terraform 设置,导出 ELB 作为资源,并在实例(以及自动伸缩组)的 Terraform 设置中使用它,使它们连接到共享的 ELB?

解决方案

请注意以下操作可能涉及版本差异,操作前请做好备份。

方案1:使用 create_before_destroy 机制

在 Terraform 中,可以使用 create_before_destroy 机制以及自动伸缩组来实现最新稳定版本的平行发布。这个方法相对简单,但会失去一些控制。在这个方法中,首先改变 AMI ID 会导致创建新的启动配置(launch configuration)。由于 create_before_destroy 的机制,新的启动配置会首先被创建,然后创建一个新的自动伸缩组,将新的实例添加到连接的 ELB(负载均衡器)中。aws_autoscaling_groupmin_elb_capacity 参数可以确保在考虑自动伸缩组已创建之前,在连接的 ELB 中有一定数量的实例健康运行,从而延迟旧自动伸缩组和启动配置的销毁,直到新的自动伸缩组提供服务为止。

尽管这种方法简单,但由于 Terraform 将整套更改视为单次运行,无法在创建新实例后暂停以允许其他检查在销毁旧资源之前进行。因此,ELB 健康检查是决定新发布是否“好”的唯一输入,一旦销毁旧资源,回滚是不可能的。

方案2:蓝/绿部署模式

蓝/绿部署模式是另一种常见的方法,它在 Terraform 中实现了平行发布,并且具有一定的控制。该模式通过将所有版本相关的资源放在一个子模块中,并使用不同的参数实例化该模块两次。顶级模块类似于以下示例:

resource "aws_elb" "example" {
  instances = "${concat(module.blue.ec2_instance_ids, module.green.ec2_instance_ids)}"
  # ...
}

module "blue" {
  source = "./app"
  ami_id = "ami-1234"
  count  = 10
}

module "green" {
  source = "./app"
  ami_id = "ami-5678"
  count  = 0
}

在“稳定状态”(无发布进行中)中,只有一个模块的 count 为非零,另一个为零。在发布期间,它们都被设置为相同的非零 count,但具有不同的 ami_id 值。每个发布步骤都是一个独立的 Terraform 操作:

  1. 更改“非活动”模块的 count 为非零,并设置其 AMI ID。
  2. 使用 Terraform 执行此更改,从而激活新模块。
  3. 验证新发布是否正常。
  4. 将较旧模块的 count 更改为零。
  5. 使用 Terraform 执行此更改,从而停用旧模块。

尽管这种方法具有更多的步骤,但它允许在第 3 步期间随意进行验证并且允许在第 5 步后经过任意的时间。它还允许通过将先

正文完