terraform:配置负载均衡器以使用 AWS 中的 ECS 任务/服务的动态端口

terraform:配置负载均衡器以使用 AWS 中的 ECS 任务/服务的动态端口

这是一个关于动态端口分配应该如何工作的一般性问题,但我的具体背景是试图弄清楚是否有一种自然的方式让目标群体知道服务的动态分配端口,而无需进行一些手动管道来告诉它。

ECS 动态端口分配文档 (https://aws.amazon.com/premiumsupport/knowledge-center/dynamic-port-mapping-ecs) 指出您只需要在任务定义中将主机端口设置为 (0),无需专门向目标组提供端口,并暗示它应该会神奇地工作。我以前试过这个,但我无法让事情正常运转。我不记得具体故障在哪里。

现在我尝试使用 Terraform 来执行此操作,我的问题是,是的,我可以将任务定义设置为具有 (0) 端口,但目标组资源中的端口参数必须存在且非零。那么,动态端口分配的另一侧应该如何工作?我假设 AWS 解决了整个问题。或者,只是动态端口分配只提出了端口分配的一半,但需要自动化来将该端口提供给另一侧,而 AWS 没有为您执行此操作的机制?这似乎是一个显而易见的问题,出于某种原因,没有人发布任何文档/讨论。我需要一些澄清。

我专门使用 ALB(应用程序负载均衡器),但这可能并不重要。

谢谢。

答案1

请记住,我是带着自己的一个问题来到这里(您很快就会看到),所以我可能不是最适合回答您的问题的人,但是......简而言之“它就是神奇地起作用了”。

当您对负载平衡服务 (引用 TaskDef) 进行 Terraform 时,您必须附加一个 ALB。附加此 ALB 需要容器名称和端口:

resource "aws_ecs_service" "ecs_service" {
  cluster = aws_ecs_cluster.ecs_cluster.id
  iam_role = aws_iam_role.ecs_service_role.arn
  launch_type = "EC2"
  task_definition = "${aws_ecs_task_definition.ecs_container.family}:${aws_ecs_task_definition.ecs_container.revision}"
  propagate_tags = "SERVICE"
  enable_ecs_managed_tags = true
  health_check_grace_period_seconds = 120
  desired_count = 2
  deployment_minimum_healthy_percent = 50
  deployment_maximum_percent = 200
  force_new_deployment = true
  load_balancer {
    target_group_arn = aws_lb_target_group.ecs_front_end_targetgroup.arn
    container_name = "FOO"
    container_port = ?????
  }
}

在 TaskDef 中,您的容器有一个端口(例如 8080),但主机端口设置为 0,以便您在实例上获得随机端口分配。ECS 代理将自动为您处理目标组中目标的更新。

但是,当实例化带有“load_balancer”子句的服务时,从技术上讲,您不知道用于创建 ALB 目标组框架的任何“临时端口”(例如映射的高端口)。这些端口尚未分配,并且在创建第一个任务之前不存在。您不能使用 0,因为它需要容器端口,而不是实例港口。

解决方案是在此处使用文字容器端口(例如 8080)。此技术有效。服务已实例化并创建一个目标组,其中实例引用未映射的端口 8080。稍后,ECS 代理出现,并在创建任务时使用工作临时端口用其他目标进行回填。

唯一奇怪的是为每个实例创建一个指向未映射的 8080 的目标,由于显而易见的原因,该目标一直处于不健康状态。没有清理操作。其他目标都很好,因此不健康的目标被忽略。它们也不会计入 AutoScaling 的所需计数。但我很想知道是否有办法在自动化中清理它们。

  • 我可以手动取消注册每个失败的未映射目标,但这很麻烦。
  • 在底层,我发现 Terraform 会将 ALB 与服务关联起来AutoScale 组。因此,我还可以转到 AutoScale 并分离 ALB——使 Service 上的 ALB 保持完整——这样可以一次性杀死所有不健康的未映射目标。

不过,大多数时候我只是让它们保持原样......

答案2

我遇到了同样的问题,并找到了解决方案。为了解决这个问题,我清除了自动扩展组上的可选负载平衡目标组设置。这可以防止每次将新实例添加到自动扩展组时在运行状况检查中定义的端口上添加自动目标组注册。

您应该只会看到 ECS 针对指向正在运行的任务的映射临时端口的目标注册。要清除设置弹性负载均衡器目标组的自动扩展组的设置,请参阅以下对话框的图像: 1

答案3

让我详细说明一下对达斯汀最初问题的回答,并且为我的“幻影不健康目标”劫持该帖子提供一些结束语……

ECS 使用多个组件发挥作用:

  1. 集群由一个或多个实例(运行 Docker 服务和 ECS 代理)组成,通常由自动扩展组生成
  2. 集群上的服务由 TaskDef(类似于实例启动模板)组成,它定义了要在服务下运行的容器(任务)
  3. 附加到负载均衡器的目标组,指定容器的运行状况检查并用于将流量路由到容器

在 Terraform 中,您可以使用(非零)服务端口创建目标组,并定义确定容器健康状况的 URL。同时,在 TaskDef 中使用 0 端口值。当 Docker 为服务生成新容器时,它会将容器内的服务端口(例如 8080)映射到实例上的随机高端口(例如 32767)。ECS 代理已参与通过 /etc/ecs/ecs.config 将实例加入集群,并会将此端口映射传达给目标组以进行更新。

因此,仅仅说“它只是奇迹般地起作用了”是不够的。ECS 代理魔法。这就是目标组如何在容器(任务)来来去去时获取动态端口分配的方式。

现在说到我的问题/难题,正如 MoK 所验证的:Auto-Scaling Group 用于生成新实例,但无需将负载均衡器连接到 ASG。这是因为路由和运行状况决策与容器 (ECS) 有关,而不是与实例 (ASG) 有关,并且 ECS 代理加上目标组处理所有这些。在 ECS 上下文中,ASG 使用“EC2”运行状况而不是“ELB”运行状况来确定整体实例状态。因此,当使用 Terraform 定义 ASG 时,不应引用目标组:

resource "aws_autoscaling_group" "ecs_autoscaling" {
  name = "ecs_autoscaling"
  #TARGETS ARE FOR CONTAINERS, NOT INSTANCES! target_group_arns = aws_lb_target_group.ecs_front_end_target_group.arn
  vpc_zone_identifier = data.aws_subnets.selected.ids
  min_size = 1
  desired_capacity = 2
  max_size = 4
  capacity_rebalance = true
  instance_refresh {
    strategy = "Rolling"
    preferences {
      min_healthy_percentage = 50
      instance_warmup = 120
    }
  }
  termination_policies = [ "OldestInstance" ]
  protect_from_scale_in = true
  default_cooldown = 120
  health_check_type = "EC2"
  health_check_grace_period = 30
  enabled_metrics = [ "GroupDesiredCapacity", "GroupInServiceInstances", "GroupTotalInstances" ]
  metrics_granularity = "1Minute"
  lifecycle {
    create_before_destroy = true
    ignore_changes = [ desired_capacity ]
  }

如果未使用目标组 ARN 属性,则 ASG 不会附加负载均衡器;具有非零服务端口的目标组只是成为 ECS 代理进行回填的占位符,并且不再存在处于持续不健康状态的“幻影”服务端口目标。我的强迫症得到了满足。

相关内容