

# Amazon ECS 容量和可用性
<a name="capacity-availability-best-practice"></a>

应用程序可用性对于提供无错误体验和最大限度降低应用程序延迟至关重要。可用性取决于是否拥有可访问的资源并有足够的容量来满足需求。AWS 提供了几种管理可用性的机制。对于托管在 Amazon ECS 上的应用程序，这些机制包括自动扩缩和可用区（AZ）。自动扩缩根据您定义的指标管理任务或实例的数量，而可用区则允许您将应用程序托管在孤立但地理位置接近的位置。

与任务规模一样，容量和可用性也存在您必须考虑的某些权衡。理想情况下，容量将与需求完全一致。总是有足够的容量来提供请求和处理作业，以满足服务级别目标（SLO），包括低延迟和错误率。容量永远不会太高，导致成本过高；也永远不会太低，导致高延迟和错误率。

自动扩缩是一个潜在过程。首先，CloudWatch 必须接收实时指标。然后，CloudWatch 需要将它们汇总起来进行分析，这可能需要长达几分钟的时间，具体取决于指标的粒度。CloudWatch 将这些指标与警报阈值进行比较，以确定资源短缺或过剩情况。为防止不稳定，您应配置警报，要求在警报响起前必须超过设置的阈值几分钟。预调配新任务和终止不再需要的任务也需要时间。

由于系统存在这些潜在的延迟，应通过过度预置来保持一定的余量。过度预置有助于适应短期的需求爆增。这也有助于您的应用程序在不达到饱和的情况下服务额外的请求。作为一种良好的实践，您可以将扩缩目标设置在利用率的 60-80% 之间。这可以帮助您的应用程序更好地处理额外需求激增，同时仍在预调配更多容量。

建议您过度配置的另一个原因是，您可以快速响应可用区故障。AWS 建议从多个可用区提供生产工作负载。这是因为，如果一个可用区发生故障，则在剩余可用区中运行的任务仍然可以满足需求。如果应用程序在两个可用区中运行，则需要将正常任务数增加一倍。这样，您就可以在任何潜在故障期间提供即时容量。如果您的应用程序在三个可用区中运行，则建议您运行正常任务数的 1.5 倍。也就是说，对正常服务所需的每两个任务执行三个任务。

## 最大程度提高缩放速度
<a name="capacity-availability-speed"></a>

自动扩缩是一个被动的过程，需要时间才能生效。但是，有一些方法可以帮助最大限度地缩短横向扩展所需的时间。

**最小化映像大小。**较大的映像需要更长的时间才能从映像存储库中下载和解压缩。因此，缩小映像大小可以减少容器启动所需的时间。要缩小映像大小，您可以遵循以下具体建议：
+ 如果您可以构建静态二进制文件或使用 Golang，则请 `FROM` 从头开始构建映像，并在生成的映像中仅包含您的二进制应用程序。
+ 使用来自上游 Distro 供应商（例如 Amazon Linux 或 Ubuntu）的最小化基础映像。
+ 请勿在最终映像中包含任何生成构件。使用多阶段生成可以帮助解决这个问题。
+ 尽可能压缩 `RUN` 阶段。每个 `RUN` 阶段都会创建一个新的映像层，从而发起另一轮下载层的循环。具有由 `&&` 联接的多个命令的单个 `RUN` 阶段的层数少于具有多个 `RUN` 阶段的命令。
+ 如果您想在最终映像中包含机器学习推理数据等数据，则请仅包含启动和开始提供流量所需的数据。如果您在不影响服务的情况下按需从 Amazon S3 或其他存储中提取数据，则请改为将数据存储在这些位置。

**保持映像相互靠近。**网络延迟越高，下载映像所需的时间越长。将您的映像托管在与您的工作负载所在 AWS 区域相同的存储库中。Amazon ECR 是一个高性能映像存储库，在 Amazon ECS 所在的每个区域都可用。避免遍历互联网或使用 VPN 链接下载容器映像。将您的映像托管在同一区域可以提高整体可靠性。它缓解了不同区域出现网络连接问题和可用性问题的风险。或者，您还可以实施 Amazon ECR 跨区域复制来帮助解决这个问题。

**减小负载均衡器运行状况检查阈值。**负载均衡器在向您的应用程序发送流量之前执行运行状况检查。目标组的默认运行状况检查配置可能需要 90 秒或更长时间。在此期间，负载均衡器会检查运行状况，并接收请求。降低运行状况检查间隔和阈值计数可以使您的应用程序更快接受流量并减少其他任务的负载。

**请考虑冷启动性能。**有些应用程序使用运行时，例如 Java 执行即时（JIT）编译。编译过程至少可以在开始时显示应用程序的性能。一种解决方法是用不会造成冷启动性能损失的语言重新编写工作负载中延迟关键的部分。

**使用步进缩放，而不是目标跟踪缩放策略。**Amazon ECS 任务有数个 Application Auto Scaling 选项。目标跟踪是最易于使用的模式。有了它，您所需要做的就是为指标设置一个目标值，例如 CPU 平均利用率。然后，自动定标器会自动管理实现该值所需的任务数量。使用分步扩展，您可以更快地对需求变化做出反应，因为您可以定义扩展指标的特定阈值，以及在超过阈值时要添加或删除的任务数量。并且，更重要的是，您可以通过最大限度地减少突破阈值警报的时间来对需求变化做出快速反应。有关更多信息，请参阅《Amazon Elastic Container Service 开发人员指南》**中的[服务自动扩缩](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-auto-scaling.html)。

如果您使用 Amazon EC2 实例提供集群容量，则请考虑以下建议：

**使用更大的 Amazon EC2 实例和更快的 Amazon EBS 卷。**您可以使用更大的 Amazon EC2 实例和更快的 Amazon EBS 卷来提高映像下载和准备速度。在给定的 Amazon EC2 实例系列中，网络和 Amazon EBS 最大吞吐量会随着实例大小的增加而增加（例如，从 `m5.xlarge` 到 `m5.2xlarge`）。此外，您也可以自定义 Amazon EBS 卷以提高其吞吐量和 IOPS。例如，如果您使用的是 `gp2` 卷，则请使用能提供更高基准吞吐量的更大的卷。如果您使用的是 `gp3` 卷，则请在创建卷时指定吞吐量和 IOPS。

**请对在 Amazon EC2 实例上运行的任务使用桥式网络模式。**在 Amazon EC2 上使用 `bridge` 网络模式的任务比使用 `awsvpc` 网络模式的任务启动得更快。使用 `awsvpc` 网络模式时，Amazon ECS 会在启动任务之前将弹性网络接口（ENI）附加到实例。这样会带来额外的延迟。不过，使用桥式网络需要进行几项权衡。这些任务没有自己的安全组，并且会对负载均衡产生一些影响。有关更多信息，请参阅《弹性负载均衡用户指南》**中的[负载均衡器目标组](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html)。

## 处理需求冲击
<a name="capacity-availability-shocks"></a>

一些应用程序会突然经受巨大的需求冲击。发生这种情况的原因有很多：新闻事件、大促销、媒体事件或其他一些可以使流量在很短的时间内迅速显著增加的病毒式传播事件。如果是意外发生的，则可能会导致需求迅速超过可用资源。

处理需求冲击的最佳方法是预测它们并做出相应的计划。由于自动扩缩可能需要时间，因此建议您在需求冲击开始之前横向扩展应用程序。为了获得最佳结果，建议制定一项业务计划，其中包括使用共享日历的团队之间的紧密协作。计划活动的团队应提前与负责应用程序的团队密切合作。这使得该团队有足够的时间来制定明确的安排计划。他们可以安排容量，以便在活动开始前进行横向扩展，在活动结束后进行横向缩减。有关更多信息，请参阅《Application Auto Scaling 用户指南》**中的[计划扩展](https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-scheduled-scaling.html)。

如果您有企业级支持计划，则请同时确保与您的技术客户经理（TAM）合作。您的 TAM 可以验证您的服务配额，并确保在活动开始之前提高所有必要的配额。这样，您将不会意外达到任何服务配额。他们还可以通过预热服务（如负载均衡器等）来帮助您，以确保您的活动顺利进行。

处理计划外的需求冲击是一个更为困难的问题。计划外的冲击，如果幅度足够大，则会迅速导致需求超出容量。它还可以超越自动扩缩的反应能力。应对计划外冲击的最佳方法是过度配置资源。您必须拥有足够的资源来随时处理最大的预期流量需求。

在预期会出现计划外的需求冲击时保持最大容量可能会付出高昂的代价。为了缓解成本影响，请找到预测大规模需求冲击即将来临的领先指标或事件。如果指标或事件可靠地提供了重要的提前通知，则在事件发生或指标超过您设置的特定阈值时，立即开始横向扩展流程。

如果您的应用程序容易出现突然的计划外需求冲击，则请考虑在应用程序中添加一种高性能模式，该模式会牺牲掉非关键功能，但为客户保留关键功能。例如，假设您的应用程序可以切换，从生成昂贵的自定义响应切换到提供静态响应页面。在这种情况下，您可以在不扩缩应用程序的情况下显著提高吞吐量。

最后，您可以考虑拆分单体服务，以更好地应对需求冲击。如果您的应用程序是一项运行成本高昂且扩展速度较慢的单体服务，则可以提取或重写性能关键部分并将其作为单独的服务运行。然后，这些新服务可以独立于不太关键的组件进行扩展。能够灵活地将性能关键型功能与应用程序的其他部分分开横向扩展，既可以减少增加容量所需的时间，又有助于节省成本。