【问题标题】:AWS ECS 503 Service Temporarily Unavailable while deployingAWS ECS 503 服务在部署时暂时不可用
【发布时间】:2017-12-09 09:17:55
【问题描述】:

我正在为我的应用程序使用带有应用程序负载均衡器的 Amazon Web Services EC2 容器服务。当我部署一个新版本时,我得到 503 Service Temporarily Unavailable 大约 2 分钟。它比我的应用程序的启动时间多一点。 这意味着我现在无法进行零停机部署。

是否有设置在启动时不使用新任务?或者我在这里错过了什么?

更新:

ALB 目标组的健康检查号如下:

Healthy threshold:     5
Unhealthy threshold:   2
Timeout:               5 seconds
Interval:              30 seconds
Success codes:         200 OK

健康阈值是“在考虑不健康目标健康之前需要连续成功的健康检查次数”
不健康阈值是“连续健康检查失败的次数在考虑目标不健康之前需要。'
超时是“没有响应意味着健康检查失败的时间量,以秒为单位。”
间隔 是“单个目标的健康检查之间的大致时间量”

更新 2: 因此,我的集群由两个 EC2 实例组成,但可以根据需要进行扩展。所需的最小计数是 2。我每个实例运行一个任务,因为我的应用程序需要一个特定的端口号。在我部署(jenkins 运行 aws cli 脚本)之前,我将实例数设置为 4。没有这个,AWS 无法部署我的新任务(这是另一个需要解决的问题)。网络模式为桥接。

【问题讨论】:

  • 您的 ALB 到 ECS 健康检查轮询间隔是多少?我的猜测是您在分钟内有这个数字,这会导致 ALB 刷新延迟。
  • @kosa 谢谢你的评论!我添加了目标组健康检查的号码。你觉得间隔太大了吗?
  • 5 * 30 秒 = ALB 切换到健康状态需要 2 分半钟,这大致符合您的观察。如果您降低这些数字,您将看到快速响应。
  • @kosa 这不应该意味着我的新实例更长时间处于不健康状态吗?所以一个实例开始时是不健康的,如果间隔更高,它会在以后变得健康吗?到那时,旧实例仍保留在 ALB 中?
  • 这是一部分问题,还有一部分是TTL(生存时间)设置,这个设置会缓存DNS设置。这些组合将决定 1) 新实例何时可用 2) 何时转发请求新实例。

标签: amazon-web-services amazon-elb amazon-ecs http-status-code-503


【解决方案1】:

因此,问题似乎在于任务定义中容器设置的端口映射。 在我使用 80 作为主机和 8080 作为容器端口之前。我以为我需要使用这些,但主机端口实际上可以是任何值。如果将其设置为 0,则 ECS 将分配一个 32768-61000 范围内的端口,因此可以将多个任务添加到一个实例。为了使其工作,我还需要更改我的安全组,让流量来自 ALB 到这些端口上的实例。
因此,当 ECS 可以在同一个实例上运行多个任务时,50/200 最小/最大健康百分比是有意义的,并且可以在不需要添加新实例的情况下部署新的任务修订。这也确保了零停机部署。

感谢所有提问或评论的人!

【讨论】:

  • 这是否适用于 Fargate 和 awsvpc 网络?我还没有看到在哪里进行容器端口映射。我有同样的问题,我的健康检查不断失败,并且任务不断重新启动,因为它认为它们不可用。我终于,就在现在,允许 404 响应作为对负载均衡器上的健康检查的有效响应,这样我的服务才能继续工作。
  • @Beanwah 我不太了解 Fargate 和 awsvpc。端口映射位于创建任务 -> 容器定义 -> 添加容器中。对于 Fargate,这是这样写的:Host port mappings are not valid when the network mode for a task definition is host or awsvpc. To specify different host and container port mappings, choose the Bridge network mode.
  • 好的,谢谢。当我尝试切换到桥接网络模式时,它说这对于基于 Fargate 的任务/服务无效。我们绕来绕去...... :)
  • @Beanwah 出于我的实际目的,我通过更改容器上使用的端口解决了这个问题。要清楚我的意思:在我的情况下,我使用的是 Apache Tomcat,所以我只是编辑了 Tomcat server.xml 文件,以便 Tomcat 在端口 80 上提供 HTTP。然后我重建了我的 war 文件,重建了我的 docker 映像,推送了它到 AWS,并在我的任务定义中指定端口 80。换句话说,我不知道映射端口的方法,但是如果您可以配置容器,则可以解决问题。
【解决方案2】:

由于您使用的是 AWS ECS,请问服务的“最低健康百分比”和“最高健康百分比”是多少

确保您的“最大运行状况百分比”为 200,“最小运行状况百分比”为 50,以便在部署期间不会出现所有服务都停止运行的情况。

请查找这两个术语的文档定义:

最大百分比提供了部署期间正在运行的任务数量的上限,使您能够定义部署批量大小。

最低健康百分比提供了在部署期间运行任务数量的较低限制,使您能够在不使用额外集群容量的情况下进行部署。

“最低健康百分比”的限制为 50 将确保在部署新版本的容器之前只有一半的服务容器被杀死,即如果服务的所需任务值为“2”而不是部署时只有“1”个旧版本的容器将首先被杀死,一旦部署了新版本,第二个旧容器将被杀死并部署一个新版本的容器。这将确保在任何给定时间都有处理请求的服务。

类似地,“最大健康百分比”的限制为 200,告诉 ecs-agent 在部署期间的给定时间,服务的容器最多可以达到所需任务的两倍。

如有任何其他问题,请告诉我。

【讨论】:

  • 感谢您的回复!最小和最大健康设置就像你写的那样。
  • @vargen_ 这很奇怪,因为理想情况下在部署期间使用这些设置并非所有容器都会关闭。我可以知道为您的服务设置的“所需任务”是什么吗?集群中有多少个 ECS 实例?还有你正在使用的 docker 网络(主机或网桥)。由于某些端口冲突或其他问题,您的应用程序(旧版本和新版本)可能无法同时启动 2 个容器。
【解决方案3】:

根据您的设置,您的应用程序启动应该需要超过 30 秒才能通过 2 次运行状况检查并被标记为不正常(假设在您的应用程序关闭后立即进行第一次检查)。并且至少需要 2 分钟到 3 分钟,然后才能再次标记为健康(在最好的情况下,在您的应用重新上线后立即检查,或者在最坏的情况下,在您的应用重新上线之前立即检查)。

因此,一个快速而肮脏的修复方法是增加不健康阈值,以便在更新期间不会将其标记为不健康。并且可能会降低健康阈值,以便更快地再次标记为健康。

但是,如果您真的想实现零停机,那么您应该使用您的应用程序的多个实例,并告诉 AWS 按照 Manish Joshi 的建议进行部署(以便在您的 ELB 后面始终有足够的健康实例来保持您的站点正常运行)。

【讨论】:

  • 感谢您的回复!一些问题:为什么我的旧实例会进入不健康状态?新实例开始时不是很不健康吗?为什么 ALB 会杀死旧实例而新实例不处于健康状态?
  • 这很奇怪。 ALB 不会杀死您的实例 - 只会将它们标记为不健康,但我认为这就是您的意思。新实例开始运行不正常,并且会一直保持运行不正常,直到您在它们上部署应用程序、启动它并等待它们通过 5 次运行状况检查。在更新您的应用程序之前,您是否等待所有 4 个实例都标记为健康?部署和 ALB 是相互独立的。 AFAIK 部署将简单地进行更新,以便一定数量的实例始终保持运行,但它不会检查它们是否在 ALB 中被标记为健康。
  • 鉴于重新启动您的应用程序需要相当长的时间。并且该 ALB 将继续将流量路由到已被更新取消的实例,直到它们未能通过足够的健康检查并被标记为“不健康”。我可以建议将部署过程更改为以下 - 使用 jenkins 和 cli 添加两个安装了新版本应用程序的实例,等待它们被标记为健康,然后从 ALB 中删除旧实例并关闭它们。然后看看 Innocent Anigbo 回答如何优雅地关闭旧的。您还需要确保 Auto Scaling 也使用更新的版本。
  • 我所做的部署是创建我的任务定义的新修订版并更新我的服务以使用这个新修订版。如果我理解正确,从这里开始,ECS 的任务就是将 ALB 中的任务切换到新的任务(如果通过了健康检查)。为什么我需要手动启动/停止实例?
【解决方案4】:

我解决这个问题的方法是在应用程序根目录中有一个平面文件,ALB 将监控它以保持健康。在部署之前,脚本将在监视节点的同时删除此文件,直到它注册OutOfService

这样所有实时连接都会停止并耗尽。此时,通过停止节点或应用程序进程开始部署。部署后,通过添加回此平面文件并将节点添加回 LB 并进行监控,直到它为此节点注册 Inservice,然后再移动到第二个节点以完成上述相同步骤。

我的脚本如下所示

# Remove Health Check target
echo -e "\nDisabling the ELB Health Check target and waiting for OutOfService\n"
rm -f /home/$USER/$MYAPP/server/public/alive.html

# Loop until the Instance is Out Of Service
while true
do
        RESULT=$(aws elb describe-instance-health --load-balancer-name $ELB --region $REGION --instances $AMAZONID)
        if echo $RESULT | grep -qi OutOfService ; then
                echo "Instance is Deattached"
                break
        fi
        echo -n ". "
        sleep $INTERVAL
done

【讨论】:

  • 感谢您的回复!这种方法听起来可行,但我认为它有点复杂,应该有一种更现成的方法来使用 ELB 进行零停机时间部署。在我的设置中,我设置了一个非常简单的端点(如果应用程序正在运行,它总是返回 200)作为运行状况检查。因此,如果应用程序尚未启动,则健康检查将失败。这还不够吗?
  • 这很好,但问题是您将无法在不停机的情况下执行部署。这是因为,一旦您停止 APP,ELB 不会自动开始将流量重定向到 LB 后面的第二个节点。它将等到下一个运行状况检查间隔之后,具体取决于您将其设置为什么。此时用户将看到 502。但您可以通过实施上述解决方案来缓解这种情况。但首先在 ELB 上启用连接耗尽,如此处所述docs.aws.amazon.com/elasticloadbalancing/latest/classic/…
  • 如果您进行手动部署,您可能只启用我在上面发送的链接中描述的连接耗尽。但是,如果您正在执行自动部署,您仍然需要一种方法来告诉您的部署等到 ec2 被标记为 OutOfService,然后再停止 APP 和 InService,然后再开始在第二个节点上进行部署,这就是脚本将为您执行的操作。否则,您可能在 LB 后面有两个状态为 OutOfService 的节点
  • 感谢您的回复!如果我理解正确,ALB 应该能够像这样进行部署:它使用新的应用程序版本启动新任务,然后等待这些任务变得健康。发生这种情况时,它会耗尽具有旧应用程序版本的任务的连接,并将流量驱动到新任务。完成后,它可以安全地停止旧版本的任务。这样就不应该有停机时间。我不想自己管理实例的启动/停止,我只是在创建一个新的任务修订版并用它来更新服务。
  • docs.aws.amazon.com/elasticloadbalancing/latest/classic/… 我的意思是,当您按照上面链接中的说明启用连接耗尽时,当您停止 node1 上的应用程序以更新代码时,ALB 将等待所有正在运行的连接已耗尽(即在将 ALB 设为 OutofService 之前完成请求)。然而,ALB 将停止向该节点发送进一步的请求,但不会突然停止已经连接的用户请求。这样用户将永远不会看到 502 或白页。启用连接耗尽是 ALB 配置中的一个复选框
【解决方案5】:

你说的是 Jenkins,所以我会在回答时考虑到 Jenkins master 服务,但我的回答对于任何其他情况仍然有效(即使它不是ECS 的一个很好的例子,一个 Jenkins master 不能正确扩展,所以只能有一个实例)。

503 网关错误

我经常遇到与负载均衡器未能通过健康检查(无健康实例)相关的503 网关错误。查看您的负载均衡器监控选项卡,确保健康主机的数量始终高于 0。

如果您正在执行 HTTP 健康检查,它必须返回一个 代码 200(有效代码列表可在负载均衡器设置中配置)仅当您的服务器真的启动并运行。否则负载均衡器可能会处理尚未完全运行的实例。

如果问题是您总是收到 503 bad gateway,可能是因为您的实例响应时间过长(在服务初始化时),所以 ECS 将它们视为已关闭并在其初始化完成之前关闭它们。 Jenkins 第一次运行时经常出现这种情况。

为避免最后一个问题,您可以考虑调整负载均衡器ping 目标健康检查目标适用于经典负载均衡器监听器应用程序负载均衡器):

  • 使用应用程序负载平衡器,尝试使用总是返回 200 的东西(例如,对于 Jenkins,它可能是像 /robots.txt 这样的公共文件)。
  • 对于经典负载平衡器,使用TCP 端口测试 而不是HTTP 测试。如果您正确打开了端口,它将始终成功。

每个实例一个节点

如果您需要确保每个实例只有一个节点,您可以使用经典负载均衡器(它也适用于 ECS)。借助经典负载均衡器ECS 可确保每台服务器仅运行一个实例。 这也是让非 HTTP 端口可访问的唯一解决方案(例如,Jenkins 需要 80 个,但从属服务器也需要 50000 个)。

但是,由于经典负载均衡器的端口不是动态的,因此您必须进行一些端口映射,例如:

myloadbalancer.mydomain.com:80(负载均衡器的端口 80)-> instance:8081(容器的外部端口)-> service:80(容器的内部端口)。

当然,每个服务都需要一个负载均衡器。

Jenkins 健康检查

如果这确实是您想要启动的 Jenkins 服务,您应该使用 Jenkins Metrics 插件 来获得一个好的 healthcheck URL

安装它,并在全局选项中,生成一个令牌并激活 ping,您应该能够访问如下所示的 URL:http://myjenkins.domain.com/metrics/mytoken12b3ad1/ping

只有在服务器完全运行时,此 URL 才会回答 HTTP 代码 200,这对于负载均衡器只有在完全准备好时才激活它很重要。

日志

最后,如果您想知道您的实例发生了什么以及它失败的原因,您可以添加日志以查看容器在 AWS Cloudwatch 中所说的内容。

只需在任务定义中添加这个(容器配置):

日志配置: awslogs
awslogs-group: mycompany (将重新组合您的容器日志的 Cloudwatch 密钥)
awslogs-region: us-east-1 (您的集群区域)
awslogs-stream-prefix: myservice (a创建日志名称的前缀)

它会让您更深入地了解容器初始化过程中发生的事情,如果它花费的时间太长或者它是否失败。

希望对你有帮助!!!

【讨论】:

  • 非常感谢您的详细解答!我检查了健康主机数,过去一周它高于 0,并且在那段时间进行了一些部署。有一件事:我不希望 Jenkins 在 ECS 中运行,但我在 Jenkins 的帮助下部署到 ECS(它运行的工作调用 AWS CLI 来执行魔法,以及其他一些事情)。我需要使用 Application Load Balancer,因为我需要它的一些功能。我的健康检查是问我的应用程序一个非常简单的问题,它可以非常快速地回答什么(无需数据库查找或类似的)。它仅在应用启动时有效。
  • 啊好吧!很抱歉对詹金斯的误解。好吧,看来你的问题已经解决了,恭喜!
  • 关于 ECS 部署,我不知道您的过程有多顺利和令人满意,但只是为了分享一些我偶然发现的东西,如果您的 Jenkins 主人可以运行,它就像一个魅力docker 容器:图像 silintl/ecs-deploy (hub.docker.com/r/silintl/ecs-deploy)。
  • 这张图片看起来很棒,谢谢!不过,我认为只有在每个实例运行一个任务时才需要进行蓝绿部署。我正在这样做,但意识到我可以轻松地切换到每个实例的多个任务,从而能够使用 ECS 的内置零停机时间部署。
  • 确实是处理零停机部署的 ECS。蓝色/绿色部分只是它等待定义的时间以检查新服务是否已启动,否则,它会取消部署(而不是让服务尝试在循环中启动),并将作业标记为失败。
猜你喜欢
  • 2018-08-18
  • 2018-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-19
  • 2019-08-20
  • 2019-12-29
  • 2013-10-31
相关资源
最近更新 更多