面向 App Engine 标准环境用户的 App Engine 柔性环境介绍

本指南向熟悉标准环境的用户介绍柔性环境。文中阐述了这两种环境之间的相似之处和主要区别,并为使用这两种环境的应用提供通用的架构建议。

如需了解如何将标准环境中可用的服务映射到柔性环境中的类似服务,请参阅将将服务从标准环境迁移到柔性环境

相似之处和主要区别

这两种环境都为您提供 App Engine 的部署、传送和扩缩基础架构。主要区别有以下四点:环境执行应用的方式、应用访问外部服务的方式、您在本地运行应用的方式,以及应用扩缩的方式。您还可参阅选择环境,查看有关这些区别的简要说明。

应用执行

在标准环境中,您的应用在沙盒中的轻量级实例上运行。此沙盒限制了应用可执行的操作。例如,沙盒只允许应用使用数量有限的一组二进制文件库,且应用无法向磁盘执行写入操作。标准环境还限制了应用可用的 CPU 和内存选项。由于这些限制,大多数 App Engine 标准应用往往是无状态的 Web 应用,可以快速响应 HTTP 请求。

相比之下,柔性环境在 Google Compute Engine 虚拟机 (VM) 上的 Docker 容器中运行您的应用,因此限制更少。例如,您可以使用所选择的任何编程语言、写入磁盘、使用选择的任何库,甚至可运行多个进程。通过柔性环境,您还可为实例选择任意 Compute Engine 机器类型,让应用能够使用更多内存和 CPU。

访问外部服务

在标准环境中,您的应用通常通过内置的 google.appengine API 访问 Datastore 等服务。但是,在柔性环境中,这些 API 不再可用。您需要改用 Google Cloud 客户端库。这些客户端是通用的,这意味着您应用的可移植性更强。 必要时,您通常无需对在柔性环境中运行的应用做出大量修改,即可在 Google Kubernetes EngineCompute Engine 上运行该应用。

本地开发

在标准环境中,您通常可以使用 App Engine SDK 在本地运行应用。SDK 处理您的应用运行并模拟 App Engine 服务。而在柔性环境中,系统不再使用 SDK 运行您的应用。柔性环境中的应用编写应采用与标准 Web 应用相同的方式进行,可在任何位置运行。如上文所述,柔性环境仅在 Docker 容器中运行您的应用。也就是说,只需直接运行应用即可在本地测试应用。例如,要使用 Django 运行 Python 应用,您只需运行 python manage.py runserver

还有一项主要区别是,在本地运行的柔性环境应用会使用实际的 Cloud Platform 服务,例如 Datastore。进行本地测试时使用一个单独的项目,并且使用模拟器(如有)。

扩缩特性

虽然这两种环境都使用 App Engine 的自动扩缩基础架构,但它们的扩缩方式却不相同。标准环境可以非常快速地从零个实例扩展到数千个实例。而柔性环境中,每个活跃版本必须至少有一个正在运行的实例,并且根据流量情况,横向扩容所需的时间可能更长。

标准环境使用定制设计的自动扩缩算法。柔性环境使用 Compute Engine 自动调节程序。请注意,并非所有 Compute Engine 可用的自动扩缩选项均受柔性环境支持。App Engine 会遵循区域中您拥有与您的配置匹配的任何 Compute Engine 虚拟机预留。拥有虚拟机预留可提高您在资源暂时不足期间获得资源分配的可能性。

开发者应在各种条件下测试其应用行为。例如,您应该验证当远程服务调用的延迟增大,受 CPU 限制的应用变为受 I/O 限制时,自动扩缩如何响应。

健康检查

标准环境不使用健康检查来确定是否将流量发送到实例。柔性环境允许应用开发者编写自己的健康检查处理程序,负载均衡器将使用这些处理程序来确定是否将流量发送到某个实例以及是否应该对其进行自动修复。在向健康检查添加逻辑时,开发者应该十分小心。例如,如果健康检查调用外部服务,则该服务中的临时故障可能会导致所有实例健康状况不佳,进而可能导致级联故障。

过载时删除请求

为避免级联故障,应用可以在过载时删除请求。此功能内置于标准环境中的流量路由层。对于柔性环境中 QPS 非常高的应用,我们建议开发者通过限制并发请求的数量来实现此功能,删除发送到应用的过载流量。

如需验证柔性环境应用是否能抵御此类故障的影响,您可以创建具有最大实例数限制的版本,然后稳步增加流量,直到请求被删除。您应该确保应用在过载期间始终能够通过健康检查。

对于 Java,使用 Jetty 运行时的 Java 应用可配置服务质量过滤器来删除过载流量。您可以设置应用处理的并发请求数上限,以及使用此功能将请求加入队列的时长。

实例规模

柔性环境实例可拥有的 CPU 和内存限额高于标准环境实例。因此柔性环境实例可以运行更多内存和 CPU 密集型的应用。但是,单个实例中线程的增加会导致发生并发错误的可能性增大。

开发者可以通过 SSH 连接到柔性环境实例,获取线程转储信息,对此类问题进行排查。

例如,如果您使用的是 Java 运行时,则可以运行以下命令:

$ ps auwwx | grep java
$ sudo kill -3 
$ sudo docker logs gaeapp

请求超时上限

虽然标准环境请求超时因所选扩缩类型而异,但柔性环境始终实施 60 分钟超时。 为避免请求在 60 分钟内一直处于执行状态并可能占用 Web 服务器上的所有线程,请执行以下操作:

  • 在调用外部服务时,指定超时。

  • 实现 servlet 过滤器以停止耗费时间过长(例如 60 秒)的请求。请确保您的应用在过滤器停止请求后能够恢复到一致的状态。

线程管理

Java 8 之前的标准环境 Java 运行时只能使用 采用 App Engine 标准环境 SDK 创建的线程。 开发者在将应用从第一代 Java 标准环境运行时移植到柔性环境时,必须改为使用原生线程库。对于需要大量线程的应用,使用线程池时的运行效率可能要比使用明确的线程创建时的运行效率高一些。

流量迁移

标准环境提供的流量迁移功能可逐步将流量移至新版本,以最大限度地减少延迟时间急剧增加的情况。有关确保在将流量移至新版本时避免延迟时间急剧增加的方法,请参阅流量迁移文档

单个地区故障

标准环境应用采用单宿主形式,也就是说应用的所有实例都位于单个可用性地区中。如果该可用区发生故障,应用将在同一区域的不同可用区中启动新实例,并且负载均衡器会将流量路由到新实例。您将看到由于加载请求和 Memcache 刷新而导致的延迟时间急剧增加。

柔性环境应用使用区域托管实例组,这意味着实例分布在一个区域内的多个可用性地区中。如果单个可用区发生故障,负载均衡器会停止将流量路由到该可用区。如果您将自动扩缩设置为尽可能热运行实例,那么在自动扩缩创建更多实例之前,您将看到一段短暂的过载。

成本比较

在标准环境和柔性环境中运行的工作负载之间的成本比较涉及许多因素。其中包括:

  • 为每个 MCycle 支付的价格。
  • CPU 平台功能,它影响每个 MCycle 期间可以完成的工作。
  • 在每个平台上运行实例的热度。
  • 部署成本(可能因平台而异)。如果您为应用使用持续部署,则成本可能显著增加。
  • 运行时开销。

您需要运行实验来确定每个平台上的工作负载成本。在柔性环境中,您可以在运行实验时将每个核心的 QPS 作为应用成本效益的代表,以此确定更改是否会对成本产生影响。标准环境中没有这样的机制来获得应用成本效益的实时指标。您必须进行更改并等待每日结算周期结束以查看更改对成本的影响。

微服务

标准环境允许使用 X-Appengine-Inbound-Appid 请求标头在应用之间进行安全身份验证。柔性环境没有这样的功能。建议使用 OAuth 在应用之间进行安全身份验证。

部署

标准环境中的部署通常快于柔性环境中的部署。在柔性环境中扩容现有版本比部署新版本更快,因为新版本的网络编程通常是柔性环境部署中耗时最长的过程。在柔性环境中快速回滚的一种策略,是保持一个缩小为单个实例的已知良好版本。然后,您可以扩容该版本,然后使用流量分配将所有流量路由到该版本。

何时使用柔性环境

柔性环境旨在与标准环境互补。如果您当前有一个应用在标准环境中运行,通常无需将整个应用迁移到柔性环境。您需要做的是,确定在您的应用中,哪些部分需要更多的 CPU、更多的 RAM、专用第三方库或程序,或者需要执行无法在标准环境中实现的操作。 一旦确定了应用的这些部分,即可创建小型的 App Engine 服务,它们使用柔性环境来专门处理这些部分。您在标准环境中运行的现有服务可以使用 HTTP、Cloud Tasks 或 Cloud Pub/Sub 调用这些其他服务。

例如,如果您在标准环境中运行现有 Web 应用并且想要添加新功能以将文件转换为 PDF,则可以编写一个单独的微服务,该服务可以在柔性环境中运行,仅用于将文件转换为 PDF。这个微服务可以是一个只包含一个或两个请求处理程序的简单的程序。它能够安装和使用任何可用的 Linux 程序(如 unoconv)来帮助完成转换。

您的主应用仍在标准环境中运行,并可通过 HTTP 直接调用此微服务。如果您预计转换将花费很长时间,则该应用可以使用 Cloud TasksPub/Sub 将请求加入队列。

后续步骤

将应用在标准环境中使用的服务映射到柔性环境中的类似服务