作为更进一步的云计算形态,云原生正在成为云时代的技术新标准,通过重塑整个软件生命周期,成为释放云价值的最短路径。
在企业内部,将云原生基础架构作为企业内部的统一架构已成为大势所趋。与此同时,也必然带来了由各种基础平台整合带来的兼容性问题,特别是规模越大、历史沉淀越多的企业,这种“技术债务”体现得越明显。
本文分享的经验来自于阿里巴巴过去数年来在混合调度方面积累的生产实践积累,具有很强的生产实用价值。内容由浅入门,逐渐深入到调度器内幕,讲述在大规模容器调度场景下,阿里巴巴针对云原生应用设计的统一基础设施 ASI(Alibaba Serverless infrastructure)调度器如何管理阿里巴巴如此复杂、繁忙的资源调度任务;并尝试通过一些具体的案例让您得以充分理解,相信会为有类似问题的读者打开设计思路,并提供落地借鉴。通过本文,相信您将系统地理解阿里巴巴复杂任务场景下的资源混合调度。
ASI 在阿里集团内部引领着容器全面上云的实施,承担了包括阿里集团内部轻量级容器架构演进、运维体系云原生化等职责,并进一步加速促进了新兴的技术包括 Mesh、Serverless、Faas 等在阿里集团内的落地;支撑了包括淘宝、天猫、优酷、高德、饿了么、UC、考拉等几乎所有经济体内部业务、阿里云云产品众多场景及生态。
ASI 的核心基于 Kubernetes,并提供完整的云原生技术栈支持。如今的 ASI 也已成功实现与阿里云容器服务 ACK(Alibaba Cloud Container Service for Kubernetes)的会师;而 ACK 既保留了云上的各种能力,也能成功应对阿里集团复杂的业务环境。
ASI 调度器是 ASI 云原生的核心组件之一。在 ASI 云原生的发展历程中,调度器的作用至关重要。最直观的认知是:阿里巴巴规模庞大的在线电商交易容器,例如购物车、订单、淘宝详情等,每一台容器的分布,包括容器编排、单机计算资源、内存资源,均由调度器分配和调度;特别是在 双11 零点峰值场景下,少数容器编排错误都有可能给业务带来致命影响,调度器需负责把控峰值时每一台容器计算的质量,其重要性可想而知。
ASI 调度器起源于 2015 年在线电商交易容器调度,这一年最早的调度器仅涵盖在线交易 T4(阿里早期基于LXC和Linux Kernel定制的容器技术)和 Alidocker 场景,出生即责任重大,并在 2015 年扛住 双11 流量峰值中发挥作用。
ASI 调度器的演进之路也伴随着云原生发展的全过程。它经历了最早期的在线交易容器调度器、Sigma 调度器、Cerebulum 调度器、ASI 调度器;到如今我们正在建设的下一代调度器 Unified-Scheduler,它将进一步吸收并融合过去数年阿里巴巴 ODPS(伏羲)、Hippo 搜索、在线调度在各个领域的先进经验。各阶段的调度解读如下图:
在 ASI 调度器的演进过程中有非常多的挑战需要解决,主要体现在:
关于阿里云原生详细的发展历程,有兴趣的同学可以通过《一个改变世界的“箱子”》这篇文章来了解。下面,我们重点来分享 ASI 调度器是如何管理着阿里如此庞大规模、如此复杂繁忙的计算资源调度任务。
1. 调度器是什么
调度器在 ASI 众多组件中的作用非常核心。调度器是云原生容器平台调度系统内众多核心组件之一;调度器是资源交付的基石。调度器是 ASI 云原生的大脑。调度器的价值主要体现在:
更通俗地讲,调度器要做的是:
在 ASI 云原生体系中,中心调度器所在的位置如下图(其中标红的框框所示):
2. 广义的调度器
在大部分时候,业界讲到调度器指的是“中心调度器”,例如社区的 K8s kube-scheduler。但真实的调度场景复杂,每一次调度都是一个复杂又灵活的综合体协同。作业提交后,它需要中心调度器、单机调度、内核调度多层次调度共同协调,并进一步在 K8s 组件 kubelet、controller 等配合下完成执行;在线调度场景,又有着批量编排调度器;而重调度下的多次调度则确保集群总是保持最优。
ASI 广义的调度器理解为:中心调度器、单机调度、内核调度、重调度、规模化编排调度、多层调度 一体的综合体。
1)中心调度器
中心调度器负责计算每一个(或一批)作业的资源编排计算,它确保一次调度最优。中心调度器为这个具体的任务计算确定诸如集群、地域、执行节点(宿主机)等信息,更进一步细化节点上的 CPU 分配、存储、网络资源分配。
中心调度器在 K8s 生态组件协同下,管理着大部分任务的生命周期。
ASI 云原生的演进之路中,中心调度器即上文描述的 Sigma 调度器、Cerebulum 调度器、ASI 调度器等等。
2)单机调度
主要负责两类职责:
第一类职责:统筹协同单机内多 POD 的最佳运行。ASI 在接受到中心调度器的节点选择指令后,将任务调度到具体的节点上执行,单机调度即开始工作:
第二类职责:单机资源信息的采集、上报、汇聚计算,为中心调度提供决策依据。在 ASI 内,单机调度组件主要指 SLO-Agent、Kubelet 的部分增强能力;在正在建设的 Unified-Scheduler 调度内,单机调度主要指 SLO-Agent、Task-Agent、以及 Kubelet 的部分增强能力。
3)内核调度
单机调度从资源视角统筹单机内多 POD 的最佳运行,但任务的运行态实际由内核控制。这就需要内核调度。
4)重调度
中心调度器确保了每次任务的最佳调度,即一次性调度问题;但中心调度器并不能实现集群维度的全局最优,这就需要重调度。
5)规模化编排调度
规模化编排调度是阿里巴巴大规模在线调度的特有场景,自 17 年开始建设,现在已经非常成熟,并仍在持续增强中。
利用规模化编排能力,我们可以一次性调度数万、数十万容器,一次性确保集群维度所有容器的全局最佳编排。它非常巧妙地弥补了一次性中心调度的弊端,规避了大规模建站场景下需反复重调度的复杂度。
关于内核调度、重调度、规模化编排调度,我们将在下面的章节中详细展开。
6)调度分层
另一个维度,我们也会定义调度分层,包括 一层调度、二层调度、三层调度...等;Sigma 在离线混部场景甚至引入了零层调度的概念。每个调度系统对调度分层的理解和定义会不一样,并都有各自的概念。例如,在过去的 Sigma 体系内,调度分为 0 层、1 层 和 2 层调度:
Sigma 的调度分层体系的致命弊端是,各个二层调度的技术能力和投入参差不齐;例如广告的二层调度系统非常优秀,但并不是所有的二层调度对业务贴心到极致。ASI 吸取教训,将众多能力下沉至 ASI 内,并进一步标准化上层 PAAS,简化上层的同时增强上层能力。
而今天正在建设的下一代调度器概念内,也分为多层,例如:计算负载层(主要指 Workload 的调度管理)、计算调度层(如 DAG 调度、MR 调度等)、业务层(同 Sigma 2 层的概念)。
3. 调度资源类型
我尝试用正在建设的 Unified-Scheduler 调度器来让大家更好地理解。在 Unified-Scheduler 调度器内,调度着 Product 资源、Batch 资源、BE 计算资源三种分等级的资源形态。
不同调度器对分等级的资源形态有不同的定义,但本质上大同小异。为了让大家更好地理解这一精髓,我在后续的章节对 ASI 调度器也做了详细讲解。
1)Product(在线)资源
有 Quota 预算的资源,且调度器需保障其最高级别的资源可用性。典型代表是在线电商核心交易的长生命周期 POD 实例。最经典的例子就是 双11 核心链路上的购物车(Cart2)、订单(tradeplatform2)等交易核心的业务 POD。这些资源要求算力的严格保障、高优先级、实时性、响应低延时、不可被干扰等等。
举例来说,在线交易的长生命周期 POD 的存在时间很长,数天、数月、甚至数年。大部分应用研发同学申请的应用,在构建完毕后需要申请数台长生命周期实例,这些都是 Product 资源。淘宝、天猫、聚划算、高德、友盟、合一、菜鸟、国际化、闲鱼....等等众多业务研发同学申请的 POD(或容器)实例,相当大部分都是 product 资源。
Product 资源不仅仅指在线长生命周期的 POD;凡是符合上述定义的资源请求,都是 Product 资源。但并不是所有的长生命周期 POD 都是 Product 资源。例如阿里内部“Aone 实验室”用于执行 CI 构建任务的 POD,可以长生命周期存在,但它可以被低成本驱逐抢占。
2)Batch 资源
在线业务使用的 Product 资源的 Allocate 和 Usage 之间的 Gap 在一段时间内是比较稳定的,这个 Gap 和 Prod 未分配的资源就作为BE资源,售卖给针对 latency 不那么敏感和但是对资源稳定性有一定需求的业务。Batch 有 quota 预算,但是保证一段时间内(例如 10 分钟)的一定概率(例如 90%)的资源可用性。
也就是说,Product(在线)资源申请走了账面上的资源,但实际上从负载利用率指标来看可能有众多算力未被使用;此时将发挥调度器的差异化 SLO 分等级调度能力,将那些未跑满的部分,作为超发资源充分使用,售卖给 Batch 资源。
3)Best Effort(BE)资源
指没有 Quota 预算,不保障资源可用性,随时可以被压制和抢占;节点上已分配在节点的 Usage 低于一个水位的时候,调度器认为这部分 Gap 是一个“比较不稳定/不记账”的资源,那么这个 Gap 就称为 BE 资源。
我们可以这样来比方:Product、Batch 资源负责大块吃肉,BE 资源则负责消费 Product 和 Batch 不要的残渣。例如:日常开发工作中,研发需要跑很多 UT 测试任务,这类计算任务对计算资源的质量要求并不高,时间延时的容忍度也比较高,也不大好评估额度预算,针对这类场景去购买大量的 Product 或者 Batch 资源,将非常不划算;但如果使用最廉价的 BE 资源,收益将相当可观。此时,BE 资源就是 Product/Batch 运行中没有用到的资源。
很容易理解到,正是通过这种分等级的资源调度能力,从技术层面,Unified-Scheduler 调度器可以将一台物理节点的资源使用,发挥到极致。
下图是 ASI 围绕着广义调度需覆盖的职责,并对应不同资源等级诉求、以及服务的丰富业务场景,构建的调度能力总览。通过这张图,大家可以理解到 ASI 调度器的技术全貌。
1. 在线调度的业务诉求
在 ASI 云原生容器平台上,在线部分服务着交易、导购、直播、视频、本地生活、菜鸟、高德、合一、友盟、海外等数十个 BU 的各类调度场景。其中最高等级的“Product 资源”的调度占比最为庞大。
在线业务的调度与离线调度、众多 JOB 型调度相比较,有着典型的差异(描述在线场景时,大家可以想象到,离线调度的世界同样精彩)。
1)生命周期
长生命周期的特性,与一些典型的短生命周期任务调度(如 FaaS 函数计算),在任务特征上有着本质的不同,背后的技术挑战也截然不同。例如:相对短生命周期的函数计算场景的挑战是:最极致的调度效率、百毫秒的执行效率、快速的调度吞吐、POD 运行时性能等。而长生命周期 POD 带来的差异化挑战是:全局最优的调度必须依赖重调度来持续迭代优化;运行时的最佳调度必须依赖单机重调度持续优化保障。可以想象,在过去非云原生时代,众多业务不可迁移,对调度而言简直是噩梦;这意味着调度器不仅仅面对调度能力的技术问题,还需面对难度巨大的存量业务治理推动;在线应用的启动时间长,又更加剧降低了重调度的灵活度,带来更多的复杂度。
2)容器运行时
在线容器的运行时由于对业务和算力都非常敏感,因此对调度质量提出了非常苛刻的挑战。
3)应对阿里在线应用特有的复杂业务模型
4)特有的规模化运维诉求
2. 一次调度:调度基本能力
下表详细描述了在线调度最常见的调度能力:
3. 应用间编排策略
因集群节点数量有限,彼此潜在干扰的众多应用,不得已需在同一节点并存时,这时就需要应用间编排策略,来确保每一个宿主节点和每一个 POD 运行时最优。
实际的调度生产实践中,“业务稳定性”永远是排在第一位,但资源却总是有限的;我们很难平衡“资源成本最优”和“业务稳定性”。大部分情况下,应用间编排策略都能完美地解决这一平衡;通过定义应用之间(如 CPU 消耗密集型、网络消耗型、IO 密集型、峰值模型特征等)的并存策略,集群内充分打散,或同一节点并存时又有充分的策略约束保护,进而做到不同 POD 间的干扰概率最小。
更进一步,调度器在运行时辅以更多技术手段优化,例如:通过网络优先级控制、CPU 精细编排控制等策略,来尽可能规避应用间运行时的潜在影响。
应用间编排策略带来的其它挑战是:调度器在建设好本职的应用间编排能力外,还需充分理解其上运行的每一个业务运行特征。
4. CPU 精细化编排
CPU 精细编排在“在线调度领域”内是非常有意思的话题,它包括 CpuSet 调度、CpuShare 调度。其它场景的调度领域,例如离线调度领域,它并没有这么重要,甚至不可被理解;但在线交易场景下,无论是理论推断、实验室场景、还是无数次大促压测数据,都证明了精准的 CPU 调度就是这么重要。
CPU 精细编排的一句话解读是:调核,确保 CPU 核最大化、最稳定地被使用。
CPU 精细编排如此重要,以至于 ASI 在过去的数年,已经将这一规则吃透并使用到了极致。相信您在看到下表后(仅含 CpuSet 精细编排调度),您也会感叹 ASI 甚至已经将它玩出了花样。
科普:以一台 96 核(实际上我们说的都是 96 个逻辑核)的 X86 架构物理机或神龙为例,它有 2 个 Socket,每个 Socket 有 48 个物理核,每个物理核下有 2 个逻辑核。【当然,ARM 的架构又与X86不同】。
由于 CPU 架构的 L1 L2 L3 Cache 设计,最理想的分配是:同一个物理核下的 2 个逻辑核,其中一个核 分配给最核心的在线交易应用如 Carts2(购物车业务),另一个核分配给另一个不繁忙的非核心应用;这样在日常、或 双11 零点峰值时,Carts2 可以占尽便宜。这种用法,在实际的生产环境、压测演练环境中均屡试不爽。
假如我们将同一个物理核上的两个逻辑核,都同时分配给 Carts2 时,由于业务峰值的相同(尤其是同一个 POD 实例),资源的最大化使用就会大打折扣。
理论上我们也应该尽量规避两个同样都是交易核心的应用,例如 Carts2(购物车业务)、tradePlatform2(订单),使其不要去共用这两个逻辑核。但实际上在微观层面,Carts2 和 tradePlatform2 的峰值会有差异,所以实际上影响还小。尽管这样的 CPU 分配看起来有些“将就”;但物理资源总归是有限的,也只能保持这份“将就”了。
而在 numa-aware 开启时,为了最大化使用 L3 Cache 来提升计算性能,同一个 POD 的更多核,又应确保尽量规避跨 Socket。
而当使用 CPUShare 时,Request 和 Limit 如何分配,也非常有学问;CPUSet 和 CPUShare 并存时,调度将更加复杂(例如:CpuSet 容器的新扩容、或下线,潜在的诉求是整机所有 POD 的 CPU 重调度);而在新兴的 GPU 异构调度场景下,CPU 与 GPU 的并存分配也具备一定的技巧。
5. 规模化编排调度
规模化编排主要应用于建站、搬站或规模化迁移场景,例如阿里巴巴频繁的大促建站、机房迁移诉求下的超大规模搬站等。基于成本考量,我们需要在尽可能短的时间,以极少的人力成本快速创建数十万级别 POD。
多个任务依次请求的随机性和不可预见性,决定了中心调度在规模化领域存在诸多弊端。在没有规模化编排能力之前,阿里巴巴大规模站点的建设,往往需经历复杂的 “业务自扩容 -> 反复重调度” 的过程,这将耗费大量人力数周的精力。好在我们有了规模化编排调度,在小时级规模化交付效率的同时,又能确保 99% 以上的资源分配率。
1. 重调度
中心调度器做到了一次性最优调度;但与最终想要的集群维度全局调度最优,是两个完全不一样的概念。重调度也包括全局中心重调度和单机重调度。
为什么一定需要中心重调度作为一次性调度的补偿呢?我们举几个例子:
中心重调度的算法、实现上往往非常复杂。我们需要深入理解各类重调度场景并充分覆盖,定义清晰的重调度 DAG 图,动态执行并确保执行的成功率。
众多场景也需要单机重调度。例如:CPU 精细编排的 SLO 优化、基于 OQS 数据驱动的单机重调度优化等。
需要强调的是,单机重调度的执行,必须先解决安全风控的问题,规避不可控的爆炸半径。在单机侧风控能力不足前,我们建议您暂不要采用节点自治方式,而是改为严格保护控制下的中心统一触发。实际上在 K8s 域内,会出现非常多不可避免的节点自治场景(例如 pod yaml 变化时,Kubelet 将执行相应变更),过去 ASI 花费数年持续梳理每一个潜在的风控点,并迭代建设了分等级风控管理(核按钮、高危、中危等)的 Defender 系统;针对潜在风险项,执行单机侧动作前,与中心的 Defender 交互,通过安全防控规避灾难事件的发生。我们建议调度器必须同样做到严密的安全防御等级,才允许节点自治操作。
2. 内核调度
内核调度存在的背景是:一台并行运行着众多任务的繁忙宿主机,即使中心调度 & 单机调度,已共同确保其最佳资源分配(如 CPU 分配、IO 打散等),但实际运行时,多任务间不可避免地进行内核态的资源争抢,在大家熟知的在离线混部场景中竞争尤为激烈。这就需要中心调度、单机调度、内核调度 通过众多协同,例如统筹任务的各类资源优先级,并交由内核调度控制执行。
这也对应众多内核隔离技术。包括 CPU:调度优先级 BVT、Noise Clean 机制等;内存:内存回收、OOM 优先级等;网络:网络金银铜优先级、IO 等等。
在今天我们还有了安全容器。基于安全容器的 Guest Kernel 和 Host Kernel 隔离机制,我们可以更优雅地规避内核运行态的部分争抢问题。
3. 弹性调度、分时调度
弹性和分时的逻辑都是更好的资源复用,只是维度不一样。
ASI 调度器与阿里云基础设施层充分协同,利用 ECS 提供的强大弹性能力,在饿了么场景,低峰期向云归还资源,高峰期重新申请相应资源。
我们可以使用 ASI 大资源池(注:ASI 资源池的宿主资源均来自于阿里云云资源)的内置弹性 Buffer,也可以直接使用阿里云 IaaS 层的弹性技术。二者的平衡是一个很有争议的话题,也是一个比较艺术的过程。
ASI 的分时调度更是将资源复用做到了极致,并带来了巨大的成本优化。通过每天晚上大规模停用在线交易 POD 实例,释放的资源用于 ODPS 离线任务使用,每天早上离线任务退水并重新拉起在线应用。这个经典场景更是将在离线混部技术的价值发挥到最大。
分时的精髓是资源复用,以及依赖的大资源池建设管理,这是资源运营 & 调度技术 的综合。这需要调度器积累多多益善的丰富形态作业、以及多多益善的海量作业任务。
4. 垂直伸缩调度/X+1/VPA/HPA
垂直伸缩调度是一种秒级交付技术,非常完美地部分解决了突发流量问题。垂直伸缩调度也是大促零点峰值压力风险的杀手锏,通过对存量 POD 的资源垂直调整、准确可靠的 CPU 调度和洗牌算法来做到计算资源的秒级交付。垂直伸缩调度、VPA 技术一脉相承,垂直伸缩调度也是 VPA 的场景之一。
“X+1” 水平扩容调度在某种意义上也可以理解为 HPA 场景之一,只不过 “X+1” 水平扩容调度由手动触发。“X+1” 侧重于强调极致的资源交付效率,这背后是研发效率的极大提升:在线 POD“X(若干)”分钟可启动完毕提供业务服务;除应用启动外的所有其它操作,务必在 “1” 分钟内全部完成。
垂直伸缩调度与 “X+1” 水平扩容调度互为补充,共同为各类峰值保驾护航。
ASI 也正在实施更多的 VPA 和 HPA 场景。例如,我们可以通过 VPA 技术,额外提供蚂蚁春节红包更多数量的免费算力,这将是非常大的成本节约。
VPA/HPA 等调度技术更多场景的极致实施,也是阿里巴巴未来将继续追求完善的地方。
5. 分等级[差异化 SLO]的资源调度
差异化 SLO 调度是调度器的精髓之一;这节内容与上文中的【调度资源类型】章节存在一定的重复。鉴于差异化 SLO 的复杂度,所以有意将它放在本章的最后一节来讲述。
ASI 调度器内,也非常精确地定义了 SLO(服务质量目标)、QoS 和 Priority。
1)SLO
SLO 描述的是服务质量目标。ASI 通过不同的 QoS 和 Priority 来提供差异化 SLO,不同的 SLO 有不同的定价。用户可以根据不同的业务特性来决定"认购"哪类 SLO 保障的资源。如:离线的数据分析任务,则可以使用低等级的 SLO 以享受更低的价格。而对于重要业务场景则可以使用高等级的 SLO,当然价格也会更高。
2)QoS
QoS 描述了资源保障质量。K8s 社区定义的 QOS 包括 Guaranteed、Burstable、BestEffort。ASI 中定义的 QOS,与社区并没有进行完全映射(社区完全用 Request / Limit 来映射)。为了能将集团的场景(如 CPUShare,混部等)描述清晰,ASI 从另外一个维度定义了 QOS,它包括 LSE / LSR / LS / BE,清晰地划分出不同的资源保障,不同的业务根据自身的延迟敏感度可以选择不同的 QOS。
3)PriorityClass
PriorityClass 和 QoS 是两个维度的概念。PriorityClass 描述的则是任务的重要性。
资源分配策略和任务的重要性(即 PriorityClass 和 QoS)会有不同的组合,当然也需要存在一定的对应关系。例如,我们可以定义一个名为 Preemptible 的 PriorityClass,其大部分任务对应到 BestEffort 的 QoS。
各个调度系统对 PriorityClass 有不同的定义。例如:
1. 调度模拟器
调度模拟器有点类似于阿里巴巴的全链路压测系统,通过线上真实的流量回放、或模拟流量回放,在模拟环境验证调度新的能力,进而不断地锤炼各种调度算法,优化各类指标。
调度模拟器的另一个常见的用途是,是对线上疑难杂症问题做线下模拟,做到无害、高效地定位各类问题。
一定程度上,调度模拟器是全局调度最优的基础。有了调度模拟器,我们得以在模拟环境,反复锤炼各种算法、技术框架、技术链路,进而做到全局指标的优化,例如:全局分配率、不同场景下的调度性能、调度稳定性等等。
2. Elastic Scheduling Platform(ESP 平台)
为了做到全局最优的调度,围绕着调度器,ASI 构建了一套全新的 Elastic Scheduling Platform(ESP 平台),旨在围绕调度器,打造基于调度数据指导 & 核心调度能力 & 产品化调度运营 的一站式自闭环调度效能系统。
在过去,我们已经建设了诸多类似模块,例如调度 SLO 巡检、众多调度工具、不同场景的二层调度平台等;而基于 ESP 平台,集合更多的二层调度能力,带给 ASI 全局最优的调度质量,并围绕 业务稳定性、资源成本、用户效能提升,带给客户更贴心的服务。
本文尝试着系统地讲解 ASI 调度器的基本概念、原理和各种场景,并带领您走进调度器美丽精彩的世界。调度器博大精深,遗憾的是,受限于篇幅,也不得不控制篇幅,非常多的内容点到为止并未深入展开。在调度器内,还有更多更深层次的调度内幕,如异构机型调度、调度画像、公平性调度、优先级调度、腾挪调度、抢占调度、磁盘调度、Quota、CPU 归一化、GANG Scheduling、调度 Tracing、调度诊断等众多调度能力,本文均未予阐述。受限于篇幅,本文也未讲述 ASI 强大的调度框架结构及优化、调度性能优化等更多更深层次的技术内幕。
早在 2019 年,ASI 已优化 K8s 单集群至业界领先的万级节点规模,并得益于阿里云 ACK 强大的 K8s 运维体系,阿里集团内持续保有着数量众多的大规模计算集群,同时也积累了业界领先的 K8s 多集群生产实践。正是在这些大规模 K8s 集群内,ASI 基于完善的容器调度技术,持续为众多复杂的任务资源提供着计算资源算力。
在过去的数年,借助于集团全面上云的契机,阿里集团在调度领域,已实现了从 ASI 管控到阿里云容器服务 ACK的全面迁移和进化。而阿里集团内复杂、丰富、规模化的业务场景,未来也将持续输出、增强并锤炼云的技术能力。
作者:黄涛、汪萌海
原文链接
本文为阿里云原创内容,未经允许不得转载。