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

适用于 .NET 的可靠 Web 应用模式

Azure 应用服务
Azure Front Door
用于 Redis 的 Azure 缓存
.NET

本文提供了实现可靠 Web 应用模式的指导。 此模式概述了如何修改(重新平台化)Web 应用以进行云迁移。 它提供了与架构良好的框架原则相一致的规范性体系结构、代码和配置指导。

为什么采用 .NET 的可靠 Web 应用模式?

可靠 Web 应用模式是一套原则和实现技术,它定义了迁移到云时应如何重新平台化 Web 应用。 它重点关注在云中取得成功所需的最低限度代码更新。 以下指南以参考实现为例,介绍了虚构公司 Relecloud 的平台重构历程,为你的历程提供业务背景。 在实现 .NET 的可靠 Web 应用模式之前,Relecloud 有一个采用 ASP.NET 框架的单体内部票务 Web 应用。

提示

GitHub 徽标有一个可靠 Web 应用模式的参考实现(示例)。 它代表了一家名为 Relecloud 的虚构公司实现可靠 Web 应用的最终状态。 它是一个生产级 Web 应用,具有本文讨论的所有代码、体系结构和配置更新。 部署并使用参考实现来指导你实现可靠 Web 应用模式。

如何实现可靠 Web 应用模式

本文包括实现可靠 Web 应用模式的体系结构、代码和配置指导。 使用以下链接导航到所需的具体指导:

  • 业务背景:将此指导与业务背景相匹配,并学习如何确定推动重新平台化决策的近期和长期目标。
  • 体系结构指导:了解如何选择正确的云服务并设计符合业务要求的体系结构。
  • 代码指导:实现三种设计模式来提高云中 Web 应用的可靠性和性能效率:重试、断路器和缓存端模式
  • 配置指导:配置身份验证和授权、托管标识、权限化环境、基础结构即代码和监控。

▪ 业务上下文

Web 应用重新平台化的第一步是确定业务目标。 应为 Web 应用程序设定近期目标,如服务水平目标和成本优化目标,以及未来目标。 这些目标会影响对云服务和云中 Web 应用体系结构的选择。 为 Web 应用定义目标 SLO,例如 99.9% 的正常运行时间。 计算影响 Web 应用可用性的所有服务的复合 SLA

例如,Relecloud 对销售预测持乐观态度,预计 Web 应用票证需求会增加。 为了满足这一需求,他们明确了 Web 应用程序的目标:

  • 应用低成本、高价值的代码更改
  • 服务级别目标 (SLO) 达到 99.9%
  • 采用 DevOps 做法
  • 创建成本优化的环境
  • 提高可靠性和安全性

为了实现这些目标,Relecloud 的本地基础结构显然不是一款经济高效的解决方案。 因此,他们决定将 Web 应用程序迁移到 Azure,认为这才是实现当下和未来目标最经济高效的方法。

体系结构指南

可靠 Web 应用模式有几个基本的体系结构要素。 需要使用 DNS 来管理终结点解析,需要 Web 应用程序防火墙来阻止恶意 HTTP 流量,还需要负载均衡器来保护和路由入站用户请求。 应用程序平台托管 Web 应用代码,并通过虚拟网络中的专用终结点来调用所有后端服务。 应用程序性能监控工具可捕获指标和日志,以了解 Web 应用。

显示了可靠 Web 应用模式基本体系结构要素的示意图。

图 1. 可靠 Web 应用模式的基本体系结构要素。

设计体系结构

设计基础结构以支持恢复指标,如恢复时间目标 (RTO) 和恢复点目标 (RPO)。 RTO 会影响可用性,并且必须支持你的 SLO。 确定恢复点目标 (RPO),并配置数据冗余以满足 RPO。

  • 选择基础设施的可靠性。 确定满足可用性需求所需的可用性区域和区域数量。 添加可用性区域和区域,直到复合 SLA 符合你的 SLO。 可靠 Web 应用模式支持多个区域,以实现主动-主动或主动-被动配置。 例如,参考实现采用主动-被动配置,以满足 99.9% 的 SLO 要求。

    对于多区域 Web 应用,可根据业务需要配置负载均衡器,将流量路由到第二个区域,以支持主动-主动或主动-被动配置。 这两个区域需要相同的服务,只是其中一个区域有一个连接各区域的中心虚拟网络。 采用中心辐射型网络拓扑来集中和共享资源,如网络防火墙。 如果有虚拟机,请在中心虚拟网络中添加一台堡垒主机,以便安全地管理虚拟机(请参阅图 2)。

    显示了可靠 Web 应用模式与第二个区域和中心辐射型拓扑的示意图。

    图 2. 可靠 Web 应用模式与第二个区域和中心辐射型拓扑。

  • 选择网络拓扑。 根据 Web 和网络要求选择正确的网络拓扑。 如果计划拥有多个虚拟网络,请使用中心辐射型网络拓扑。 它通过与本地和虚拟网络的混合连接选项,提供成本、管理和安全方面的优势。

挑选正确的 Azure 服务

将 Web 应用转移到云中时,应选择符合业务要求并与本地 Web 应用当前功能相匹配的 Azure 服务。 这种一致性有助于尽可能减少重新架构的工作。 例如,使用可以保持相同数据库引擎并支持现有中间件和框架的服务。 以下各部分提供了为 Web 应用选择合适的 Azure 服务的指导。

例如,在迁移到云端之前,Relecloud 的票证 Web 应用是一个本地整体式 ASP.NET 应用。 它在两台虚拟机上运行,并具有 Microsoft SQL Server 数据库。 Web 应用面临可伸缩性和功能部署方面的常见挑战。 基于这一出发点及其业务目标,SLO 推动他们做出了服务选择。

  • 应用程序平台:使用 Azure 应用程序服务作为应用程序平台。 Relecloud 出于以下原因选择了 Azure 应用程序服务作为应用程序平台:

    • 高级服务级别协议 (SLA):它具有高 SLA,可满足 99.9% 的生产环境 SLO 。
    • 降低管理开销:它是一个完全托管的解决方案,用于处理缩放、运行状况检查和负载均衡。
    • .NET 支持:它支持编写应用程序所用的 .NET 版本。
    • 容器化功能:Web 应用可以在云上聚合而无需容器化,但应用程序平台也支持容器化,而无需更改 Azure 服务。
    • 自动缩放:Web 应用可以根据用户流量和配置来设置自动缩放。 该平台还支持扩大或缩小规模,以适应不同的托管要求。
  • 标识管理:使用 Microsoft Entra ID 作为标识和访问管理解决方案。 Relecloud 出于以下原因选择了 Microsoft Entra ID

    • 身份验证和授权:这些应用程序需要对呼叫中心员工进行身份验证和授权。
    • 可缩放:可缩放以支持更大的场景。
    • 用户标识控制:呼叫中心员工可以使用其现有的企业标识。
    • 授权协议支持:支持将 OAuth 2.0 用于托管标识。
  • 数据库:使用可保持相同数据库引擎的服务。 使用数据存储决策树。 Relecloud 的 Web 应用使用本地的 SQL Server。 因此,他们希望使用现有的数据库架构、存储的过程和函数。 Azure 上提供了多个 SQL 产品,但 Relecloud 出于以下原因选择了 Azure SQL Database

    • 可靠性:常规用途层提供高 SLA 和多区域冗余。 它可以支持高用户负载。
    • 降低管理开销:提供托管 SQL 数据库实例。
    • 迁移支持:支持从本地 SQL Server 迁移数据库。
    • 与本地配置保持一致:支持现有的存储过程、函数和视图。
    • 复原能力:支持备份和时间点还原。
    • 专业知识和最少的修改:SQL 数据库利用内部专业知识,只需少量工作即可采用。
  • 应用程序性能监视:使用 Application Insights 来分析应用程序的遥测数据。 Relecloud 出于以下原因选择使用 Application Insights:

    • 与 Azure Monitor 集成:提供与 Azure Monitor 的最佳集成。
    • 异常情况检测:自动检测性能异常情况。
    • 故障排除:帮助诊断正在运行的应用中的问题。
    • 监视:收集有关用户如何使用应用的信息,并可轻松跟踪自定义事件。
    • 可见性差距:本地解决方案没有应用程序性能监视解决方案。 Application Insights 提供与应用程序平台和代码的轻松集成。
  • 缓存:选择是否在 Web 应用体系结构中添加缓存。 Azure Cache for Redis 是 Azure 的主要缓存解决方案。 它是一个基于 Redis 软件的托管内存中数据存储。 Relecloud 的 Web 应用负载主要偏向于查看音乐会和演出地点的详细信息,因此它添加了针对 Redis 的 Azure 缓存,原因如下:

    • 降低管理开销:这是一项完全托管的服务。
    • 速度和量:具有高数据吞吐量和低延迟读取,适用于经常访问、慢速变化的数据。
    • 多样化的可支持性:供 Web 应用的所有实例使用的统一缓存位置。
    • 外部数据存储:本地应用程序服务器执行 VM 本地缓存。 此设置未卸载频繁使用的数据,并且无法使数据失效。
    • 非粘性会话:外部化会话状态支持非粘性会话。
  • 负载均衡器:使用 PaaS 解决方案的 Web 应用程序应使用 Azure Front Door、Azure 应用程序网关或根据 Web 应用体系结构和要求同时使用这两者。 使用负载均衡器决策树来选取正确的负载均衡器。 Relecloud 需要一个 7 层负载均衡器,该负载均衡器可以跨多个区域路由流量。 Relecloud 需要一个多区域 Web 应用来实现 99.9% 的 SLO。 Relecloud 出于以下原因选择了 Azure Front Door

    • 全局负载均衡:一个 7 层负载均衡器,可以跨多个区域路由流量。
    • Web 应用程序防火墙:以本机方式与 Azure Web 应用程序防火墙集成。
    • 路由灵活性:允许应用程序团队配置入口需求,以支持未来应用程序中的更改。
    • 流量加速:使用任意广播访问最近的 Azure 接入点,并找到通往 Web 应用的最快途径。
    • 自定义域:支持具有灵活域验证的自定义域名。
    • 运行状况探测:应用程序需要智能运行状况探测监视。 Azure Front Door 会根据这些探测响应来确定用于路由客户端请求的最佳源。
    • 监视支持:支持内置报告,并为 Front Door 和安全模式提供一体式仪表板。 可以配置与 Azure Monitor 集成的警报。 它允许应用程序记录每个请求和失败的运行状况探测。
    • DDoS 保护:具有内置的 3-4 层 DDoS 保护。
    • 内容分发网络:将 Relecloud 定位为使用内容分发网络。 内容分发网络提供站点加速。
  • Web 应用程序防火墙:使用Azure Web 应用程序防火墙提供集中防护,防止常见的网络攻击和漏洞。 Relecloud 出于以下原因使用了 Azure Web 应用程序防火墙:

    • 全局保护:提供改进的全局 Web 应用保护,而不会降低性能。
    • 僵尸网络防护:团队可以监控和配置设置,以解决与僵尸网络有关的安全问题。
    • 与本地保持一致:本地解决方案在 IT 部门管理的 Web 应用程序防火墙后面运行。
    • 易于使用:Web 应用程序防火墙与 Azure Front Door 集成。
  • 配置存储:选择是否将应用配置存储添加到 Web 应用。 Azure 应用程序配置是一种服务,用于集中管理应用程序设置和功能标志。 查看应用程序配置最佳做法,以确定此服务是否适合你的应用。 Relecloud 希望将基于文件的配置替换为与应用程序平台和代码集成的中央配置存储。 他们出于以下原因向体系结构添加了应用配置:

    • 灵活性:支持功能标志。 功能标志允许用户在生产环境中选择加入和退出早期预览功能,而无需重新部署应用。
    • 支持 Git 管道:配置数据必须以 Git 存储库为准。 更新中央配置存储中的数据所需的管道。
    • 支持托管标识:支持托管标识,以简化并帮助保护与配置存储的连接。
  • 机密管理器:如果要在 Azure 中管理机密,请使用 Azure Key Vault。 可以使用 ConfigurationBuilder 对象在 .NET 应用中合并 Key Vault。 Relecloud 的本地 Web 应用将机密存储在代码配置文件中,但更好的安全做法是将机密存储在支持 RBAC 和审核控制的位置。 尽管托管标识是连接到 Azure 资源的首选解决方案,但 Relecloud 具有管理所需的应用程序机密。 Relecloud 出于以下原因使用了密钥保管库:

    • 加密:支持静态加密和传输中加密。
    • 托管标识支持:应用程序服务可以使用托管标识来访问机密存储。
    • 监视和日志记录:有助于审核访问,并在存储的机密发生更改时生成警报。
    • 集成:提供与 Azure 配置存储(应用配置)和 Web 托管平台(应用程序服务)的原生集成。
  • 存储解决方案:查看 Azure 存储选项,以便根据要求选取正确的存储解决方案。 在本地,Relecloud 的 Web 应用已将磁盘存储装载到每个 Web 服务器上,但是团队希望使用外部数据存储解决方案。 Relecloud 出于以下原因选择了 Azure Blob 存储

    • 安全访问:Web 应用可以通过匿名访问避免使用终结点访问公开到公共 Internet 的存储。
    • 加密:加密静态数据和传输中的数据。
    • 复原能力:支持区域冗余存储 (ZRS)。 区域冗余存储跨主要区域中的三个 Azure 可用性区域同步复制数据。 每个可用性区域都是一个独立的物理位置,具有独立的电源、冷却系统和网络。 此配置应使票证图像具备丢失复原能力。
  • 终结点安全:使用 Azure 专用链接通过虚拟网络中的专用终结点来访问平台即服务解决方案。 虚拟网络与服务之间的流量将遍历 Microsoft 主干网络。 Relecloud 出于以下原因选择了专用链接:

    • 增强安全通信:它允许应用程序以专用方式访问 Azure 平台上的服务,并减少数据存储的网络占用,以帮助防止数据泄露。
    • 最少的工作量:专用终结点支持 Web 应用平台和 Web 应用使用的数据库平台。 这两个平台都生成了现有本地配置的镜像,以尽量减少更改。
  • 网络安全:使用 Azure 防火墙在网络层面控制入站和出站流量。 使用 Azure Bastion 可安全地连接到虚拟机,而无需公开 RDP/SSH 端口。 Relecloud 采用中心辐射型网络拓扑,并希望将共享网络安全服务置于中心。 Azure 防火墙通过检查支路的所有出站流量来提高网络安全,从而提高安全性。 Relecloud 需要 Azure Bastion 才能从 DevOps 子网中的跳板机进行安全部署。

代码指导

要将 Web 应用成功转移到云中,则需要使用重试模式、断路器模式和缓存端设计模式来更新 Web 应用代码。

显示了基本可靠 Web 应用体系结构中设计模式角色的示意图。

图 3. 设计模式的角色。

每种设计模式都能提供工作负荷设计方面的优势,这些优势与“架构良好的框架”的一个或多个支柱相一致。 以下是应该实现的模式概览:

  1. 重试模式:重试模式通过重试可能间歇性失败的操作来处理暂时性故障。 在对其他 Azure 服务的所有向外呼叫中实现此模式。

  2. 断路器模式:断路器模式可防止应用程序重试非暂时性操作。 在对其他 Azure 服务的所有向外调用中执行此模式。

  3. 缓存端模式:缓存端模式比数据存储更频繁地向缓存添加数据和从缓存检索数据。 在请求数据库时实现此模式。

设计模式 可靠性 (RE) 安全性 (SE) 成本优化 (CO) 卓越运营 (OE) 性能效率 (PE) 支持 WAF 原则
重试模式 RE:07
断路器模式 RE:03
RE:07
PE:07
PE:11
缓存端模式 RE:05
PE:08
PE:12

实现重试模式

在应用程序代码中添加重试模式,以解决临时服务中断问题。 这些中断被称为暂时性故障。 暂时性故障通常会在几秒钟内自行解决。 重试模式允许重新发送失败的请求。 它还允许配置请求延迟和失败前的尝试次数。

  • 使用内置重试机制 使用大多数 Azure 服务都有的内置重试机制来加快实现。 例如,参考实现使用 Entity Framework Core 中的连接复原,在向 Azure SQL 数据库发出请求时应用重试模式(请参阅以下代码)。

    services.AddDbContextPool<ConcertDataContext>(options => options.UseSqlServer(sqlDatabaseConnectionString,
        sqlServerOptionsAction: sqlOptions =>
        {
            sqlOptions.EnableRetryOnFailure(
            maxRetryCount: 5,
            maxRetryDelay: TimeSpan.FromSeconds(3),
            errorNumbersToAdd: null);
        }));
    
  • 使用重试编程库。 对于 HTTP 通信,请集成标准复原库,如 PollyMicrosoft.Extensions.Http.Resilience。 这些库提供全面的重试机制,对于管理与外部 Web 服务的通信至关重要。 例如,每次代码构造调用 IConcertSearchService 对象的对象时,参考实现都会使用 Polly 来强制执行重试模式(请参阅以下代码)。

    private void AddConcertSearchService(IServiceCollection services)
    {
        var baseUri = Configuration["App:RelecloudApi:BaseUri"];
        if (string.IsNullOrWhiteSpace(baseUri))
        {
            services.AddScoped<IConcertSearchService, MockConcertSearchService>();
        }
        else
        {
            services.AddHttpClient<IConcertSearchService, RelecloudApiConcertSearchService>(httpClient =>
            {
                httpClient.BaseAddress = new Uri(baseUri);
                httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
                httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, "Relecloud.Web");
            })
            .AddPolicyHandler(GetRetryPolicy())
            .AddPolicyHandler(GetCircuitBreakerPolicy());
        }
    }
    
    private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromMilliseconds(500), retryCount: 3);
        return HttpPolicyExtensions
          .HandleTransientHttpError()
          .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
          .WaitAndRetryAsync(delay);
    }
    

实现断路器模式

使用断路器模式来处理非暂时性故障的服务中断。 断路器模式可防止应用程序持续尝试访问无响应服务。 它会释放应用程序并避免浪费 CPU 周期,以便应用程序为最终用户保留其性能完整性。

例如,参考实现会将断路器模式应用于对 API 的所有请求。 它使用 HandleTransientHttpError 逻辑来检测可以安全重试的 HTTP 请求,但会限制在指定时间内聚合的故障总数(请参阅以下代码)。

private static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
        .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
}

实现缓存端模式

缓存端模式添加到 Web 应用中,以改进内存中的数据管理。 该模式为应用程序分配处理数据请求的责任,并确保缓存与持久性存储(例如数据库)之间的一致性。 它缩短了响应时间,提高了吞吐量,并减少了对更多缩放的需求。 它还减少了主数据存储的负载,改善了可靠性和成本优化。 要实现缓存端模式,请遵循以下建议:

  • 将应用程序配置为使用缓存。 生产应用应使用分布式 Redis 缓存,因为它能减少数据库查询,从而提高性能,而且它还能启用非粘性会话,这样负载均衡器就能平均分配流量。 例如,参考实现使用分布式 Redis 缓存。 AddAzureCacheForRedis 方法将应用程序配置为使用用于 Redis 的 Azure 缓存(请参阅以下代码)。

    private void AddAzureCacheForRedis(IServiceCollection services)
    {
        if (!string.IsNullOrWhiteSpace(Configuration["App:RedisCache:ConnectionString"]))
        {
            services.AddStackExchangeRedisCache(options =>
            {
                options.Configuration = Configuration["App:RedisCache:ConnectionString"];
            });
        }
        else
        {
            services.AddDistributedMemoryCache();
        }
    }
    
  • 缓存高需求数据。 在高需求数据上应用缓存端模式,以提高其有效性。 使用 Azure Monitor 跟踪数据库的 CPU、内存和存储。 这些指标有助于确定在应用“缓存端”模式后,是否可以使用更小的数据库 SKU。 例如,参考实现缓存了支持“音乐会预告”页面的高需求数据。 GetUpcomingConcertsAsync 方法会将数据从 SQL 数据库拉入 Redis 缓存,然后用最新的音乐会数据填充缓存(请参阅以下代码)。

    public async Task<ICollection<Concert>> GetUpcomingConcertsAsync(int count)
    {
        IList<Concert>? concerts;
        var concertsJson = await this.cache.GetStringAsync(CacheKeys.UpcomingConcerts);
        if (concertsJson != null)
        {
            // There is cached data. Deserialize the JSON data.
            concerts = JsonSerializer.Deserialize<IList<Concert>>(concertsJson);
        }
        else
        {
            // There's nothing in the cache. Retrieve data 
            // from the repository and cache it for one hour.
            concerts = await this.database.Concerts.AsNoTracking()
                .Where(c => c.StartTime > DateTimeOffset.UtcNow && c.IsVisible)
                .OrderBy(c => c.StartTime)
                .Take(count)
                .ToListAsync();
            concertsJson = JsonSerializer.Serialize(concerts);
            var cacheOptions = new DistributedCacheEntryOptions {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
            };
            await this.cache.SetStringAsync(CacheKeys.UpcomingConcerts, concertsJson, cacheOptions);
        }
        return concerts ?? new List<Concert>();
    }
    
  • 使缓存数据保持最新。 计划定期缓存更新,以便与最新的数据库更改保持同步。 根据数据波动性和用户需求确定最佳刷新率。 这种做法可确保应用程序使用缓存端模式提供快速访问和最新信息。 例如,参考实现只会缓存一小时的数据,并在数据发生变化时使用 CreateConcertAsync 方法来清除缓存键(请参阅以下代码)。

    public async Task<CreateResult> CreateConcertAsync(Concert newConcert)
    {
        database.Add(newConcert);
        await this.database.SaveChangesAsync();
        this.cache.Remove(CacheKeys.UpcomingConcerts);
        return CreateResult.SuccessResult(newConcert.Id);
    }
    
  • 确保数据一致性。 实施各种机制以在任何数据库写入操作后立即更新缓存。 使用事件驱动的更新或专用数据管理类来确保缓存一致性。 始终将缓存与数据库修改保持同步是缓存端模式的核心。 例如,参考实现使用 UpdateConcertAsync 方法来保持缓存中数据的一致性(请参阅以下代码)。

    public async Task<UpdateResult> UpdateConcertAsync(Concert existingConcert), 
    {
       database.Update(existingConcert);
       await database.SaveChangesAsync();
       this.cache.Remove(CacheKeys.UpcomingConcerts);
       return UpdateResult.SuccessResult();
    }
    

配置指南

以下各部分将为实现配置更新提供指导。 每个部分都与“架构良好的框架”的一个或多个支柱相一致。

配置 可靠性 (RE) 安全性 (SE) 成本优化 (CO) 卓越运营 (OE) 性能效率 (PE) 支持 WAF 原则
配置用户身份验证和授权 SE:05
OE:10
实现托管标识 SE:05
OE:10
适当规模的环境 CO:05
CO:06
实现自动缩减 RE:06
CO:12
PE:05
自动执行资源部署 OE:05
实现监视 OE:07
PE:04

配置用户身份验证和授权

在将 Web 应用程序迁移到 Azure 时,请配置用户身份验证和授权机制。 遵循以下建议:

  • 使用标识平台。 使用 Microsoft标识平台设置 Web 应用身份验证。 此平台支持使用单个 Microsoft Entra 目录、来自不同组织的多个 Microsoft Entra 目录以及Microsoft标识或社交帐户的应用程序。

  • 创建应用注册。 Microsoft Entra ID 需要在主租户中注册应用程序。 应用程序注册可确保有权访问 Web 应用的用户在主租户中具有标识。

  • 使用平台功能。 通过使用平台功能来验证用户身份和访问数据,最大限度地减少对自定义验证代码的需求。 例如,应用程序服务提供内置的身份验证支持,因此只需在 Web 应用中编写少量代码或根本无需编写代码,就能让用户登录和访问数据。

  • 在应用程序中强制实现授权。 使用基于角色的访问控制 (RBAC),为应用程序角色分配最少的权限。 为不同的用户操作定义特定的角色,以避免重叠并确保清晰明了。 将用户映射到适当的角色,确保他们只能访问必要的资源和操作。

  • 更倾向于临时访问仓库。 使用临时权限来防止未经授权的访问和违规行为,如共享访问签名 (SAS)。 使用用户委派 SAS 在授予临时访问权限时最大程度地提高安全性。 它是唯一使用 Microsoft Entra ID 凭据的 SAS,而无需永久存储帐户密钥。

  • 在 Azure 中强制实现授权。 使用 Azure RBAC 为用户标识分配最低权限。 Azure RBAC 决定标识可以访问哪些 Azure 资源、可以对这些资源做什么以及可以访问哪些区域。

  • 避免永久提升权限。 使用 Microsoft Entra Privileged Identity Management 为特权操作授予即时访问权限。 例如,开发人员通常需要管理员级别的访问权限来创建/删除数据库、修改表架构和更改用户权限。 通过即时访问,用户标识可获得执行特权任务的临时权限。

实现托管标识

对支持托管标识的所有 Azure 服务使用托管标识。 托管身份允许 Azure 资源(工作负荷标识)验证其他 Azure 服务并与之交互,而无需管理凭据。 混合系统和旧式系统可以保留本地身份验证解决方案,以简化迁移,但应尽快过渡到托管标识。 要实现托管标识,请遵循以下建议:

  • 选取正确的托管标识类型。 如果有两个或更多 Azure 资源需要同一组权限,请优先使用用户分配的托管标识。 此设置比为每个资源创建系统分配的托管标识并为所有资源分配相同的权限更为有效。 否则,请使用系统分配的托管标识。

  • 配置最低权限。 使用 Azure RBAC 仅授予对操作至关重要的权限,例如数据库中的 CRUD 操作或访问机密。 工作负荷标识具有永久权限,因此无法为工作负荷标识提供实时或短期权限。 如果 Azure RBAC 未涵盖特定方案,则可使用 Azure 服务级别访问策略对 Azure RBAC 进行补充。

  • 保护其余机密。 将其余机密存储在 Azure 密钥保管库中。 在应用程序启动时(而不是在每个 HTTP 请求期间)从密钥保管库加载机密。 HTTP 请求中的高频访问可能会超过密钥保管库事务限制。 将应用程序配置存储在 Azure 应用配置中。

例如,参考实现在 SQL 数据库连接字符串中使用了 Authentication 参数,因此应用程序服务可以使用托管标识连接到 SQL 数据库Server=tcp:my-sql-server.database.windows.net,1433;Initial Catalog=my-sql-database;Authentication=Active Directory Default。 它使用 DefaultAzureCredential 允许 Web API 使用托管标识连接到密钥保管库(请参阅以下代码)。

    builder.Configuration.AddAzureAppConfiguration(options =>
    {
         options
            .Connect(new Uri(builder.Configuration["Api:AppConfig:Uri"]), new DefaultAzureCredential())
            .ConfigureKeyVault(kv =>
            {
                // Some of the values coming from Azure App Configuration
                // are stored in Key Vault. Use the managed identity
                // of this host for the authentication.
                kv.SetCredential(new DefaultAzureCredential());
            });
    });

适当规模的环境

使用能满足每个环境需求的 Azure 服务性能层 (SKU),而无需过多的数量。 要合理调整环境规模,请遵循以下建议:

  • 估算成本。 可使用 Azure 定价计算器估算每个环境的成本。

  • 优化生产环境的成本。 生产环境需要满足生产所需的服务级别协议 (SLA)、功能和规模的 SKU。 持续监控资源使用情况,并根据实际性能需求调整 SKU。

  • 优化预生产环境的成本。 预生产环境应使用成本较低的资源,禁用不需要的服务,并应用 Azure 开发/测试定价等折扣。 确保预生产环境与生产环境完全类似,以避免引入风险。 这种平衡既能确保测试的有效性,又不会产生不必要的费用。

  • 使用基础结构即代码 (IaC) 来定义 SKU。 实现 IaC,以便根据环境动态选择和部署正确的 SKU。 此方法可增强一致性,同时简化管理。

例如,参考实现使用 Bicep 参数将更昂贵的层 (SKU) 部署到生产环境中。

    var redisCacheSkuName = isProd ? 'Standard' : 'Basic'
    var redisCacheFamilyName = isProd ? 'C' : 'C'
    var redisCacheCapacity = isProd ? 1 : 0

实现自动缩减

自动缩放可确保 Web 应用保持弹性、响应迅速,并能高效处理动态工作负荷。 要实现自动缩放,请遵循以下建议:

  • 自动横向扩展。使用 Azure 自动缩放在生产环境中自动进行水平缩放。 配置自动缩放规则,根据关键性能指标进行横向扩展,让应用程序能够处理不同的负荷。

  • 优化缩放触发器。 如果不熟悉应用程序的缩放要求,请首先将 CPU 利用率作为初始缩放触发器。 优化缩放触发器,使其包括 RAM、网络吞吐量和磁盘 I/O 等其他指标。 目的是匹配 Web 应用程序的行为,以提高性能。

  • 提供横向扩展缓冲区。 设置缩放阈值,以便在达到最大容量前触发。 例如,配置在 CPU 使用率达到 85% 时进行缩放,而不是等到 CPU 使用率达到 100%。 这种积极主动的方法有助于保持性能和避免潜在的瓶颈。

自动执行资源部署

使用自动化在所有环境中部署和更新 Azure 资源和代码。 遵循以下建议:

  • 使用基础结构即代码。 通过持续集成和持续交付 (CI/CD) 管道来部署基础结构即代码。 Azure 为每个 Azure 资源预制了 Bicep、ARM (JSON) 和 Terraform 模板

  • 使用持续集成/持续部署 (CI/CD) 管道。 使用 CI/CD 管道将代码从源代码控制部署到各种环境,如测试、暂存和生产环境。 如果使用的是适用于 GitHub 项目的 Azure DevOps 或 GitHub Actions,则使用 Azure Pipelines。

  • 集成单元测试 在部署到应用程序服务之前,在管道中设置所有单元测试的执行和传递优先级。 合并代码质量和覆盖率工具(如 SonarQube)以实现全面的测试覆盖率。

  • 采用模拟框架。 对于涉及外部终结点的测试,请使用模拟框架。 这些框架可创建模拟终结点。 它们消除了配置真实外部终结点的需要,并可确保跨环境的统一测试条件。

  • 执行安全扫描。 使用静态应用程序安全测试 (SAST) 在源代码中查找安全漏洞和编码错误。 此外,执行软件组合分析 (SCA),检查第三方库和组件是否存在安全风险。 执行这些分析的工具可以轻松集成到 GitHub 和 Azure DevOps 中。

实现监视

实现应用程序和平台监控,从而提高 Web 应用的卓越运行和性能效率。 要实现监控,请遵循以下建议:

  • 收集应用程序遥测。 使用 Azure Application Insights 中的 autoinstrumentation 来收集应用程序遥测数据(如请求吞吐量、平均请求持续时间、错误和依赖性监控),而无需更改代码。

    参考实现使用 NuGet 软件包 Microsoft.ApplicationInsights.AspNetCore 中的 AddApplicationInsightsTelemetry 来启用 遥测收集请参阅以下代码)。

    public void ConfigureServices(IServiceCollection services)
    {
       ...
       services.AddApplicationInsightsTelemetry(Configuration["App:Api:ApplicationInsights:ConnectionString"]);
       ...
    }
    
  • 创建自定义应用程序指标。自定义遥测使用基于代码的检测。 将 Application Insights SDK 添加到代码并使用 Application Insights API。

    参考实现会收集 cart 活动相关事件的遥测数据。 this.telemetryClient.TrackEvent 计算添加到购物车中的票证。 它提供事件名称 (AddToCart) 并指定包含 concertIdcount 的字典)(请参阅以下代码)。

    this.telemetryClient.TrackEvent("AddToCart", new Dictionary<string, string> {
        { "ConcertId", concertId.ToString() },
        { "Count", count.ToString() }
    });
    
  • 监控平台。 为所有支持的服务启用诊断程序,并将诊断程序发送到与应用程序日志相同的目标,以便进行关联。 Azure 服务会自动创建平台日志,但只有在启用诊断功能时才会存储这些日志。 为每个支持诊断的服务启用诊断设置。

部署参考实现

参考实现指导开发人员完成从本地 ASP.NET 应用程序到 Azure 的模拟迁移,并强调了在最初采用阶段的必要更改。 本示例使用了虚构公司 Relecloud 的音乐会票务应用程序,该公司通过内部 Web 应用程序来销售门票。 Relecloud 为其 Web 应用程序设定了以下目标:

  • 实现低成本、高价值的代码更改
  • 达到 99.9% 的服务级别目标 (SLO)
  • 采用 DevOps 做法
  • 创建成本优化的环境
  • 增强可靠性和安全性

Relecloud 认为,要实现这些目标,他们的内部基础结构并非具有成本效益的解决方案。 他们决定将 CAMS Web 应用程序迁移到 Azure,认为这才是实现当下和未来目标最经济高效的方法。 以下体系结构代表了 Relecloud 的可靠 Web 应用模式实现的最终状态。

显示了参考实现的体系结构的示意图。图 3. 参考实现的体系结构。下载此体系结构的 Visio 文件