【问题标题】:How to force AWS ECS migrate containers to another ASG?如何强制 AWS ECS 将容器迁移到另一个 ASG?
【发布时间】:2018-06-16 03:02:07
【问题描述】:

我正在使用 user_data 对 ECS 中用于运行容器的主机进行初始配置。一旦我更新 user_data,我希望能够告诉 AWS ECS 将容器迁移到新创建的主机。我该怎么做?

我正在使用 Terraform 进行 AWS 基础设施部署。

【问题讨论】:

  • 我们创建了一个terraform module 来使用this AWS post 中所述的lambda 来处理这个问题。它将做什么: - 创建一个 lambda 和一个 SNS 主题(具有正确的 IAM 访问权限) - 链接 lambda 和 SNS 主题 然后您可以将 SNS 主题链接到您的自动缩放组的 autoscaling:EC2_INSTANCE_TERMINATING 生命周期挂钩。现在,当您更改任何应该销毁和重新创建资源的参数时,lambda 将

标签: amazon-web-services amazon-ecs terraform


【解决方案1】:

我认为 ECS 没有内置的方法来执行此操作。因此,它通常需要一个相当繁琐的手动过程(尽管可以编写脚本)。有几种不同的方法可以做到这一点,但这通常是最简单的一种:

  1. 更改为user_data
  2. 运行terraform apply
  3. 对于您的 ASG 中每个具有旧 user_data 的 EC2 实例:
    1. 终止该 EC2 实例。您可以通过 AWS CLI 或 EC2 Web 控制台执行此操作。
    2. 稍后,ASG 将使用您的新user_data 自动启动一个新的 EC2 实例,以替换已终止的 EC2 实例。
    3. 稍后,ECS 将自动启动恰好在终止的 EC2 实例上运行的任何 ECS 任务的新副本。

完成此过程后,ASG 中的所有实例都将运行新的user_data。请注意,这可以通过 ECS 任务的零停机时间来完成,只要:

  1. 每个 ECS 任务至少有 2 个副本,每个副本位于 ASG 中的单独 EC2 实例上。
  2. 您在终止 EC2 实例之间等待足够的时间以重新启动 ECS 任务。

如果您无法满足这些要求,那么您可能会遇到一些停机时间,或者您可能需要采用更混乱的选择,包括将 ASG 的大小增加一倍,等待新的 EC2 实例(它将具有新的 @ 987654326@) 在 ASG 中部署,将 ECS 任务的数量增加一倍,等待那些新的 ECS 任务部署(它们通常会部署到新的 EC2 实例上),然后再将每个任务减少一半(理论上,旧的 ECS任务和旧的 EC2 实例将被终止,只留下新的)。

【讨论】:

  • 如果您有适当的缩放方法,您可以将3. 替换为手动将asg 缩放到<desired_count>*2。所有新实例都将具有新的user_data,当集群缩减时,ECS 将删除旧实例并保留新实例。 “移动”,即重新启动,新机器上的任务将隐式发生,AFAIK
【解决方案2】:

虽然Yevgeniyanswer 是正确的,但如果重新创建实例,则无法让 Terraform 直接将容器迁移到新实例,但使用 Terraform 的资源@987654323 可以使用更简洁的选项@。

假设您使用自动缩放组来支持您的 ECS 主机,您可以执行以下操作:

data "aws_ami" "ubuntu" {
  most_recent = true
  filter {
    name = "name"
    values = ["ubuntu/images/ebs/ubuntu-trusty-14.04-amd64-server-*"]
  }
  filter {
    name = "virtualization-type"
    values = ["paravirtual"]
  }
  owners = ["099720109477"] # Canonical
}

resource "aws_launch_configuration" "as_conf" {
  name_prefix = "terraform-lc-example-"
  image_id = "${data.aws_ami.ubuntu.id}"
  instance_type = "t1.micro"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_autoscaling_group" "bar" {
  name = "${aws_launch_configuration.as_conf.name}"
  launch_configuration = "${aws_launch_configuration.as_conf.name}"

  lifecycle {
    create_before_destroy = true
  }
}

(取自 Terraform 的 launch configuration docs

现在,当启动配置发生变化时,例如,如果用户数据或所使用的 AMI 发生变化,这将强制 Terraform 创建一个新的启动配置,由于对名称的依赖,这反过来又会强制一个新的自动缩放组.

由于 Terraform 使用 create_before_destroy 生命周期配置,它将在销毁之前创建新的启动配置和 ASG。在上述简单设置中,只要 AWS 认为单个实例健康,ASG 就会在完成后返回。

不幸的是,这只显示 EC2 实例何时运行良好,而不是它成功运行任务。正如该答案的 cmets 中所述,ECS 不会将任务自动平衡到集群中的新实例,因此 Terraform 将在 ECS 将它们重新调度到新 ASG 实例之前销毁在旧 ASG 中运行 ECS 任务的实例,从而导致停电。

要解决这个问题(并且还允许实例失败并以更好的方式被替换),您可以使用ASG lifecycle hooks 在实例被标记为终止但在它实际终止之前执行一些操作。

有一个很好的AWS blog post 正是这样做的,并且有一些 [示例 Lambda 代码] 响应钩子以在完成生命周期钩子之前耗尽标记为终止的容器实例,这将允许 ASG 终止实例。耗尽容器实例后,ECS 会自动将最少数量的健康任务重新调度到非耗尽实例(在新的 ASG 中)。

如果您的 ECS 任务已注册到负载均衡器,则一旦新的任务集运行,ECS 将从负载均衡器取消注册任务,然后这些任务将在负载均衡器的连接耗尽超时期间保留。

【讨论】:

  • 你真的尝试过这种方法吗? AFAIK,如果您尝试它,它会导致您的所有 ECS 服务完全中断,或者完全失败。这是因为 Terraform 将创建一个新的 ASG(感谢 create_before_destroy 参数),但 ECS 没有理由在其上部署任何东西。因此,新的 ASG 将是空的,并且旧的 ASG 将被杀死(带走所有 ECS 服务),或者,如果您尝试 ELB 健康检查技巧,apply 命令将失败,因为没有在新的 ASG,因此没有在 ELB 上注册。
  • 我已经按照这些思路构建了一些虚拟 POC,但我们现在没有在生产中使用 Docker。要完成这项工作,您确实需要让 userdata 脚本加入 ECS 集群,然后使用类似 aws ecs start-task --cluster $cluster --task-definition cadvisor:1 --container-instances $instance_arn --region $region(取自 aws.amazon.com/blogs/compute/…)之类的东西启动任务,但是是的,否则 ECS 只会在旧实例将任务移动到新实例实例被销毁。
  • 我已经尝试过这种方法,Yevgeniy 是正确的。由于 ECS 没有提前迁移到新的 ASG,因此会导致 ECS 集群完全中断。可能的解决方法是在 ECS 任务定义中设置一些包含 ASG 名称的 ENV 变量。这将使 ECS 使当前任务处于非活动状态并迁移到备用服务器。但是我不认为这一切都会以正确的顺序/所需的时间发生。
  • @Maklaus 很抱歉耽搁了,但我刚刚更新了我的旧答案,用一种自动化的方式很好地做到了这一点。 AWS 博客文章非常有用,尽管我发现为 Lambda 函数提供的代码有点不可靠,所以为我的新公司重写了该代码。不幸的是,它不是开源的,但它在很大程度上遵循了 AWS 博客文章中的相同原则。我已经对其进行了很多测试,但没有发现该方法有任何问题,而且它还有一个很好的副作用,即如果运行 ECS 任务的实例未能通过健康检查或正在替换底层主机,那么它将安全地耗尽任务。跨度>
猜你喜欢
  • 1970-01-01
  • 2019-12-11
  • 2022-08-10
  • 2021-06-04
  • 1970-01-01
  • 2018-04-13
  • 1970-01-01
  • 1970-01-01
  • 2014-10-01
相关资源
最近更新 更多