你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

无服务器 Web 应用程序

Microsoft Entra ID
Azure API 管理
Azure Blob 存储
Azure 内容分发网络
Azure Functions
Azure Monitor

本参考体系结构演示一个无服务器 Web 应用程序。 该应用程序提供 Azure Blob 存储中的静态内容,并使用 Azure Functions 来实现一个 API。 该 API 从 Azure Cosmos DB 读取数据,并将结果返回到 Web 应用。

GitHub 徽标可在 GitHub 上查看此体系结构的两个参考实施:无人机配送应用 (ARM Azure Pipelines) 以及待办事项应用 (Bicep 和 GitHub Actions)

体系结构

该示意图显示无服务器 Web 应用程序的参考体系结构。

下载此体系结构的 Visio 文件

术语“无服务器”有两种不同但相关的含义:

  • 后端即服务 (BaaS)。 后端云服务(例如数据库和存储)提供 API,使客户端应用程序能够直接连接到这些服务。
  • 函数即服务 (FaaS)。 在此模型中,“函数”是部署到云的一段代码,在托管环境中运行,用于完全抽象化运行代码的服务器。

这两个定义在概念上有共同之处,即,开发人员和 DevOps 人员无需部署、配置或管理服务器。 虽然从 Azure Blob 存储提供 Web 内容算是 BaaS 的一个示例,但本参考体系结构主要侧重于使用 Azure Functions 的 FaaS。 FaaS 的部分重要特征包括:

  1. 计算资源由平台按需动态分配。
  2. 基于消耗量的定价:只需支付执行代码所用的计算资源的费用。
  3. 可根据流量按需缩放计算资源,开发人员无需执行任何配置。

函数在激发外部触发器时执行,例如,当 HTTP 请求或消息抵达队列时。 这样可以得到一种事件驱动的体系结构样式,即无服务器体系结构的原生样式。 若要协调体系结构中组件之间的工作,请考虑使用消息中转站或发布/订阅模式。 在选择 Azure 中的消息传送技术时如需帮助,请参阅选择用于传送消息的 Azure 服务

组件

该体系结构包括以下组件:

Blob 存储。 静态 Web 内容(例如 HTML、CSS 和 JavaScript 文件)存储在 Azure Blob 存储中,并通过静态网站托管提供给客户端。 所有动态交互通过调用后端 API 的 JavaScript 代码进行。 没有任何服务器端代码可以呈现网页。 静态网站托管支持索引文档和自定义的 404 错误页。

内容分发网络。 使用 Azure 内容分发网络缓存内容,以降低延迟和加快内容传送速度,并提供 HTTPS 终结点。

函数应用Azure Functions 是一个无服务器计算选项。 它使用事件驱动的模型,其中的一段代码(“函数”)由触发器调用。 在此体系结构中,当客户端发出 HTTP 请求时,会调用函数。 请求始终通过 API 网关路由,如下所述。

API 管理。 Azure API 管理提供安置在 HTTP 函数前面的 API 网关。 可以使用 API 管理来发布和管理客户端应用程序所用的 API。 使用网关有助于将前端应用程序与后端 API 分离。 例如,API 管理可以重新编写 URL、在请求抵达后端之前转换请求、设置请求或响应标头,等等。

API 管理还可用于实现横切任务,例如:

  • 强制实施用量配额和速率限制
  • 验证 OAuth 身份验证令牌
  • 启用跨域请求 (CORS)
  • 缓存响应
  • 监视和记录请求

如果不需要 API 管理提供的所有功能,可以选择使用 Functions 代理。 使用 Azure Functions 的此功能可以通过创建后端函数的路由,为多个函数应用定义单个 API 接口。 函数代理还可以针对 HTTP 请求和响应执行有限转换。 但是,这些代理提供的基于策略的功能不如 API 管理那样丰富。

Azure Cosmos DBAzure Cosmos DB 是一个多模型数据库服务。 在此方案中,函数应用程序将从 Azure Cosmos DB 中提取文档,以响应客户端发出的 HTTP GET 请求。

Microsoft Entra ID。 用户使用其 Microsoft Entra ID 凭据登录到 Web 应用程序。 Microsoft Entra ID 返回 API 的访问令牌,Web 应用程序使用该令牌对 API 请求进行身份验证(请参阅身份验证)。

Azure MonitorAzure Monitor 收集解决方案中部署的 Azure 服务的性能指标。 在仪表板中可视化这些指标可以洞察解决方案的运行状况。 Monitor 还收集应用程序日志。

Azure PipelinesAzure Pipelines 是用于生成、测试和部署应用程序的持续集成 (CI) 和持续交付 (CD) 服务。

GitHub Actions工作流是在 GitHub 存储库中设置的自动化流程 (CI/CD)。 你可以使用工作流在 GitHub 上生成、测试、打包、发布或部署任何项目。

方案详细信息

可能的用例

此 drone 交付解决方案非常适合飞机、航空航天和机器人行业。

建议

函数应用计划

Azure Functions 支持两种托管模型。 消耗计划:运行代码时自动分配计算能力。 应用程序服务计划:为代码分配一组 VM。 应用程序服务计划定义 VM 数目和 VM 大小。

请注意,根据上述定义,应用程序服务计划在严格意义上并非无服务器计划。 编程模型相同,但是,相同的函数代码既可以在消耗计划中运行,也可以在应用程序服务计划中运行。

下面是选择要使用的计划类型时要考虑的一些因素:

  • 冷启动。 使用消耗计划时,最近未调用过的函数将在下次运行时发生额外的延迟。 发生此额外延迟的原因是需要分配和准备运行时环境。 此延迟通常为几秒钟,具体时间取决于多种因素,包括需要加载的依赖项数目。 有关详细信息,请参阅了解无服务器冷启动。 通常,冷启动主要与交互式工作负荷(HTTP 触发器)而不是异步消息驱动的工作负荷(队列或事件中心触发器)有关,因为用户可直接注意到额外的延迟。
  • 超时期限。 在消耗计划中,函数执行会在一段可配置的时间(最多 10 分钟)后超时
  • 虚拟网络隔离。 使用应用程序服务计划可让函数在应用服务环境(专用的隔离托管环境)中运行。
  • 定价模型。 消耗计划根据执行次数和资源消耗量(内存 × 运行时间)计费。 应用程序服务计划根据 VM 实例 SKU 按小时计费。 通常,消耗计划比应用程序服务计划更便宜,因为你只需为所用的计算资源付费。 流量会出现高峰和低谷时,这一优势更为明显。 但是,如果应用程序一贯遇到高吞吐量,则应用程序服务计划可能比消耗计划更划算。
  • 缩放。 消耗量模型的一大优势在于,它可以根据传入流量按需动态缩放。 尽管这种缩放可快速进行,但仍然存在一个负载攀升阶段。 对于某些工作负荷,你可能会有意过度预配 VM,以便能够应对流量突发,同时避免负载攀升。 对于这种情况,请考虑使用应用程序服务计划。

函数应用边界

函数应用程序托管一个或多个函数的执行。 可以使用函数应用将多个函数作为一个逻辑单元分组到一起。 在函数应用中,函数共享相同的应用程序设置、托管计划和部署生命周期。 每个函数应用有自身的主机名。

使用函数应用将共享相同生命周期和设置的函数分组到一起。 不共享相同生命周期的函数应该托管在不同的函数应用中。

考虑采用微服务方法。在这种情况下,每个函数应用代表一个可能包含多个相关函数的微服务。 在微服务体系结构中,服务应具有松散耦合和高功能内聚的特点。 松散耦合意味着无需同时更新其他服务即可更改一个服务。 内聚表示服务具有一种妥善定义的用途。 有关这些概念的详细介绍,请参阅设计微服务:域分析

函数绑定

尽量使用函数绑定。 绑定提供声明方式用于将代码连接到数据并与其他 Azure 服务集成。 输入绑定从外部数据源填充输入参数。 输出绑定将函数的返回值发送到队列或数据库等数据接收器。

例如,参考实现中的 GetStatus 函数使用 Azure Cosmos DB 输入绑定。 此绑定配置为使用取自 HTTP 请求中查询字符串的查询参数,在 Azure Cosmos DB 中查找某个文档。 如果找到该文档,则将其作为参数传递给函数。

[Function("GetStatusFunction")]
public IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
        [CosmosDBInput(
           databaseName: "%COSMOSDB_DATABASE_NAME%",
           containerName:"%COSMOSDB_DATABASE_COL%",
           Connection  = "COSMOSDB_CONNECTION_STRING",
           Id = "{Query.deviceId}",
           PartitionKey = "{Query.deviceId}")] DeviceState? deviceStatus)
{
  ...
}

如果使用绑定,则无需编写直接与服务通信的代码,这可以简化函数代码,同时可以抽象化数据源或接收器的详细信息。 但在某些情况下,所需的逻辑可能比绑定提供的逻辑更复杂。 对于这种情况,请直接使用 Azure 客户端 SDK。

注意事项

这些注意事项实施 Azure 架构良好的框架的支柱原则,即一套可用于改善工作负荷质量的指导原则。 有关详细信息,请参阅 Microsoft Azure 架构良好的框架

可伸缩性

函数。 对于消耗计划,HTTP 触发器根据流量缩放。 并发函数实例数有限制,但每个实例一次可以处理多个请求。 对于应用程序服务计划,HTTP 触发器根据 VM 实例数缩放,该数目可以是固定值,也可以根据一组自动缩放规则自动缩放。 有关信息,请参阅 Azure Functions 的缩放和托管

Azure Cosmos DB。 Azure Cosmos DB 的吞吐量容量以请求单位 (RU) 来度量。 1-RU 吞吐量相当于获取 1KB 文档所需的吞吐量。 若要将某个 Azure Cosmos DB 容器扩展到超过 10,000 RU,必须在创建该容器时指定分区键,并在创建的每个文档中包含该分区键。 有关分区键的详细信息,请参阅 Azure Cosmos DB 中的分区和缩放

API 管理。 API 管理可横向扩展,并支持基于规则的自动缩放。 缩放过程至少需要花费 20 分钟。 如果流量突发,应针对预期的最大突发流量进行预配。 但是,自动缩放可用于处理每小时或每日流量变化。 有关详细信息,请参阅自动缩放 Azure API 管理实例

灾难恢复

下面所示的部署驻留在单个 Azure 区域中。 更具弹性的灾难恢复方法利用不同服务中的异地分发功能:

  • API 管理支持多区域部署,这种部署可用于在任意数量的 Azure 区域之间分发单个 API 管理实例。 有关详细信息,请参阅如何将 Azure API 管理服务实例部署到多个 Azure 区域

  • 使用流量管理器可将 HTTP 请求路由到主要区域。 如果该区域中运行的函数应用不可用,则流量管理器可以故障转移到次要区域。

  • Azure Cosmos DB 支持多个写入区域,因此可以向添加到 Azure Cosmos DB 帐户中的任何区域写入数据。 如果未启用多写入,仍可以故障转移主要写入区域。 Azure Cosmos DB 客户端 SDK 和 Azure 函数绑定会自动处理故障转移,因此你无需更新任何应用程序配置设置。

安全性

安全性针对蓄意攻击及滥用宝贵数据和系统提供保障措施。 有关详细信息,请参阅安全性支柱概述

身份验证

参考实现中的 GetStatus API 使用 Microsoft Entra ID 对请求进行身份验证。 Microsoft Entra ID 支持 OpenID Connect 协议(基于 OAuth 2 协议生成的身份验证协议)。

在此体系结构中,客户端应用程序是在浏览器中运行的单页应用程序 (SPA)。 此类客户端应用程序无法将客户端保密或者隐藏授权代码,因此,隐式授权流比较合适。 (请参阅应使用哪种 OAuth 2.0 流程?)。 整体流如下所述:

  1. 用户在 Web 应用程序中单击“登录”链接。
  2. 浏览器将重定向到 Microsoft Entra 登录页。
  3. 用户登录。
  4. Microsoft Entra ID 重定向回到客户端应用程序,并在 URL 片段中包含访问令牌。
  5. 当 Web 应用程序调用 API 时,会在 Authentication 标头中包含访问令牌。 应用程序 ID 在访问令牌中作为受众(“aud”)声明发送。
  6. 后端 API 验证访问令牌。

配置身份验证:

  • 在 Microsoft Entra 租户中注册应用程序。 这会生成应用程序 ID,而客户端会将此 ID 与登录 URL 包含在一起。

  • 在 Function App 中启用 Microsoft Entra 身份验证。 有关详细信息,请参阅 Azure 应用程序服务中的身份验证和授权

  • validate-jwt 策略添加到 API 管理,通过验证访问令牌为请求预先授权。

有关更多详细信息,请参阅 GitHub 自述文件

建议在 Microsoft Entra ID 中为客户端应用程序和后端 API 创建单独的应用注册。 授予客户端应用程序调用 API 的权限。 可以通过此方法灵活地定义多个 API 和客户端并控制其权限。

在 API 中,使用范围可以让应用程序对其从用户请求的具体权限进行精细的控制。 例如,API 可能有 ReadWrite 两个范围,而特定的客户端应用可能会要求用户仅授予 Read 权限。

授权

在许多应用程序中,后端 API 必须检查用户是否有权执行给定的操作。 建议使用基于声明的授权,其中,有关用户的信息将由标识提供者(在本例中为 Microsoft Entra ID)传递,并用于做出授权决策。 例如,在 Microsoft Entra ID 中注册应用程序时,可定义一组应用程序角色。 当用户登录到应用程序时,Microsoft Entra ID 会针对授予用户的每个角色(包括通过组成员身份继承的角色)包含一个“roles”声明。

Microsoft Entra ID 返回到客户端的 ID 令牌包含一些用户的声明。 在函数应用中,可在请求的 X-MS-CLIENT-PRINCIPAL 标头中找到这些声明。 但从绑定数据中读取此信息更简单。 对于其他声明,请使用 Microsoft Graph 查询 Microsoft Entra ID。 (登录期间需要用户许可。)

有关详细信息,请参阅使用客户端标识

CORS

在此参考体系结构中,Web 应用程序和 API 不共享相同的源。 这意味着,应用程序调用 API 属于一个跨域请求。 浏览器安全性将阻止网页向另一个域发出 AJAX 请求。 此限制称为“同域策略”,可防止恶意站点从另一站点读取敏感数据。 若要启用跨域请求,请将跨域资源共享 (CORS) 策略添加到 API 管理网关:

<cors allow-credentials="true">
    <allowed-origins>
        <origin>[Website URL]</origin>
    </allowed-origins>
    <allowed-methods>
        <method>GET</method>
    </allowed-methods>
    <allowed-headers>
        <header>*</header>
    </allowed-headers>
</cors>

在此示例中,allow-credentials 属性为 true。 这会授权浏览器通过请求发送凭据(包括 Cookie)。 否则,浏览器默认不会通过跨域请求发送凭据。

注意

allow-credentials 设置为 true 时请小心,因为这意味着网站可以在用户不知情的情况下,代表用户将用户的凭据发送到 API。 必须信任允许的源。

实施 HTTPS

为获得最高安全性,必须在整个请求管道中使用 HTTPS:

  • CDN。 Azure CDN 默认支持在 *.azureedge.net 子域中使用 HTTPS。 若要在 CDN 中为自定义域名启用 HTTPS,请参阅教程:在 Azure CDN 自定义域中配置 HTTPS

  • 静态网站托管。 在存储帐户中启用“需要安全传输”选项。 启用此选项后,存储帐户只允许来自安全 HTTPS 连接的请求。

  • API 管理。 将 API 配置为仅使用 HTTPS 协议。 可以通过 Azure 门户或资源管理器模板进行此配置:

    {
        "apiVersion": "2018-01-01",
        "type": "apis",
        "name": "dronedeliveryapi",
        "dependsOn": [
            "[concat('Microsoft.ApiManagement/service/', variables('apiManagementServiceName'))]"
        ],
        "properties": {
            "displayName": "Drone Delivery API",
            "description": "Drone Delivery API",
            "path": "api",
            "protocols": [ "HTTPS" ]
        },
        ...
    }
    
  • Azure Functions。 启用“仅限 HTTPS”设置。

锁定函数应用

对函数的所有调用应通过 API 网关发出。 可按如下所述实现此目的:

  • 将函数应用配置为要求提供函数密钥。 API 管理网关在调用函数应用时会包含函数密钥。 这可以防止客户端绕过网关直接调用函数。

  • API 管理网关采用一个静态 IP 地址。 将 Azure 函数限制为仅允许来自该静态 IP 地址的调用。 有关详细信息,请参阅 Azure 应用程序服务静态 IP 限制。 (此功能仅适用于标准层服务。)

保护应用程序机密

不要在代码或配置文件中存储应用程序机密(例如数据库凭据)。 请改用 Azure 中加密存储的应用设置。 有关详细信息,请参阅 Azure 应用程序服务和 Azure Functions 中的安全性

或者,可将应用程序机密存储在 Key Vault 中。 这样,便可以集中存储机密、控制其分发,并监视何时以何种方式访问机密。 有关详细信息,请参阅将 Azure Web 应用程序配置为从 Key Vault 读取机密。 但请注意,Functions 触发器和绑定从应用设置加载其配置设置。 没有任何内置的方法可将触发器和绑定配置为使用 Key Vault 机密。

DevOps

使用可靠的 CI/CD 服务(例如,Azure PipelinesGitHub Actions)自动执行安全部署做法。 这些服务用于在前端和后端自动生成和部署每个源更改。 源必须驻留在联机版本控制系统中。 有关 Azure Pipelines 的更多详细信息,请参阅创建你的第一个管道。 若要详细了解适用于 Azure 的 GitHub Actions,请参阅将应用程序部署到 Azure

前端部署

此参考体系结构的前端是单页应用程序,由 JavaScript 访问无服务器后端 API,静态内容提供快速用户体验。 下面是此类应用程序的一些重要注意事项:

  • 通过使用全球可用的 CDN,将应用程序统一部署至广大地理区域中的用户,并将静态内容托管在云上。 这样就无需使用专用 Web 服务器。 请参阅将 Azure 存储帐户与 Azure CDN 集成以开始使用。 使用 HTTPS 保护应用程序。 请参阅使用内容分发网络的最佳做法以获取其他建议。
  • 压缩网站文件以减少 CDN 上的带宽消耗并提高性能。 Azure CDN 允许在边缘服务器上进行动态压缩。 或者,此参考体系结构中的部署管道会在将文件部署到 Blob 存储之前压缩这些文件。 这样可降低存储要求,并提高选择压缩工具的自由度,无需考虑任何 CDN 限制。
  • CDN 应该要能清除其缓存,以确保所有用户都能获得最新内容。 如果生成和部署进程不是原子的,则缓存清除是必需的,例如将旧文件替换为同一源文件夹中新生成的文件的情况。
  • 其他缓存策略(例如使用目录的版本控制)可能不需要由 CDN 进行清除。 此前端应用程序中的生成管道为每个新生成的版本创建新目录。 此版本作为原子单元上传到 Blob 存储。 Azure CDN 仅在完成部署后才指向此新版本。
  • 通过将资源文件缓存更长的时间(扩展几个月)来增加缓存 TTL。 若要确保缓存的文件在更改时更新,请在重新生成文件时对文件名进行指纹识别。 该前端应用程序会为所有文件提供指纹,除了面向公众的文件(例如“index.html”)。 因为 index.html 经常更新,所以它反映了导致缓存刷新的受更改文件名。 请参阅在 Azure CDN 中管理 Web 内容的到期时间,了解详细信息。

后端部署

若要部署函数应用,建议使用包文件(“从包运行”)。 使用此方法时,请将 zip 文件上传到 Blob 存储容器,然后 Functions 运行时就会将 zip 文件作为只读文件系统装载。 这是原子操作,可以减少因部署失败而导致应用程序处于不一致状态的情况。 该操作还可以缩短冷启动时间,尤其是对 Node.js 应用而言,因为所有文件都是一次性地进行交换。

在生成和部署管道中添加足够数量的自动化测试。 请注意,组成工作负载的可部署单元越多,引入的网络边界就越多。 这些单独的单元协同工作以支持用户和数据流。 随后,这种系统的端到端测试需要在集成测试方面进行额外投资。

API 版本控制

API 是服务与客户端之间的协定。 在此体系结构中,API 协定在 API 管理层定义。 API 管理支持两个不同但互补的版本控制概念

  • 版本:API 使用者可以根据需要选择 API 版本(例如 v1 或 v2)。

  • 修订版:API 管理员可以在 API 中进行非重大更改并部署这些更改。另外还可以提供更改日志,将所做的更改告知 API 使用者。

如果在 API 中进行重大更改,请在 API 管理中发布新版本。 在独立的函数应用中,连同原始版本一样部署新版本。 这样可将现有客户端迁移到新 API,且不中断客户端应用程序。 最终可以弃用旧版本。 API 管理支持多种版本控制方案:URL 路径、HTTP 标头或查询字符串。 有关一般性的 API 版本控制的详细信息,请参阅对 RESTful Web API 进行版本控制

对于不属于重大 API 更改的更新,请将新版本部署到同一函数应用中的过渡槽。 验证部署是否成功,然后将过渡版本交换为生产版本。 在 API 管理中发布修订版。

成本优化

成本优化是关于寻找减少不必要的费用和提高运营效率的方法。 有关详细信息,请参阅成本优化支柱概述

使用 Azure 定价计算器估算成本。 请考虑这些点来优化此体系结构的成本。

Azure Functions

Azure Functions 支持两种托管模型。

  • 消耗计划

    在运行代码时自动分配计算能力。

  • 应用程序服务计划。

    为代码分配一组 VM。 该计划定义 VM 数目和 VM 大小。

在此体系结构中,当客户端发出 HTTP 请求时,会调用函数。 由于此用例中不需要一个恒定的大容量吞吐量,因此建议采用消耗计划,这样就只需为所使用的计算资源付费。

Azure Cosmos DB

Azure Cosmos DB 按小时对预配吞吐量和消耗的存储计费。 预配的吞吐量以每秒请求单位表示 (RU/s),可用于插入、读取等典型的数据库操作。 价格基于预留的容量(单位为 RU/s)。

存储费用按存储数据和索引所用的每 GB 计算。

有关详细信息,请参阅 Azure Cosmos DB 定价模型

在此体系结构中,函数应用程序将从 Azure Cosmos DB 中提取文档,以响应客户端发出的 HTTP GET 请求。 在这种情况下,Azure Cosmos DB 经济高效,因为读取操作比由 RU/s 表示的写入操作要便宜得多。

内容分发网络

根据向最终用户交付内容的源服务器的位置,费率可能会因计费区域的不同而不同。 客户端的物理位置不是计费区域。 任何到达 CDN 的 HTTP 或 HTTPS 请求都是计费事件,其中包括所有响应类型:成功、失败或其他。 不同的响应可能生成不同的流量。

在此参考体系结构中,部署驻留在单个 Azure 区域中。

若要降低成本,请考虑通过缓存资源文件更长时间来增加缓存 TTL,并对内容设置最长的 TTL。

有关详细信息,请参阅 Microsoft Azure 架构良好的框架中的“成本”部分。

部署此方案

若要部署此体系结构的参考实现,请参阅 GitHub 自述文件

后续步骤

产品文档:

Learn 模块:

若要详细了解参考实现,请参阅代码演练:使用 Azure Functions 的无服务器应用程序

相关指南: