服务网格 ASM 负载均衡算法全面解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
云解析 DNS,旗舰版 1个月
简介: 在本文中,笔者将解析服务网格的多种负载均衡算法的实现原理和使用场景,为服务网格负载均衡算法的选择提供参考。

【阅读原文】戳:服务网格 ASM 负载均衡算法全面解析


引言:当今,分布式微服务是毫无争议地主流架构模式,微服务将复杂大型系统拆分成多个松耦合的微型系统,以此使得大型系统的迭代更为灵活,容错率也变得更高。对于其中的单一微服务,支持横向扩容,以根据业务情况灵活地调整系统中每个服务所使用的资源。要正确地调用多副本服务,有两个基础的问题需要解决:服务发现和负载均衡。K8s作为当前业界广泛采用的部署平台,通过服务(Service)非入侵式地解决了服务发现和负载均衡的问题,但是,面对日益复杂的业务场景,K8s服务仍然存在一些不尽人意之处,例如,其负载均衡算法仅支持随机,而对于一些特定场景,随机算法的表现较差,且没有调优余地。因此,越来越多地微服务运维团队选择了服务网格作为网络基础设施,其在K8s服务的基础上,提供了比K8s更强大的负载均衡能力。在本文中,笔者将解析服务网格的多种负载均衡算法的实现原理和使用场景,为服务网格负载均衡算法的选择提供参考。

 

 

服务网格负载均衡的优势

 

K8s Service通过iptables规则,实现了连接级别的负载均衡,这种负载均衡不需要入侵应用,而是直接从网络层面将流量做DNAT,实现了负载均衡。但K8s的负载均衡存在一些显著的缺陷,首先,由于其使用iptables规则实现负载均衡,这使得K8s的负载均衡算法的时间复杂度是O(n),因此在集群规模逐渐膨胀时,负载均衡的性能可能受到显著影响。其次,其只支持随机负载均衡算法,这种算法并不能严格地将请求均匀地分布到后端实例上。由于其基于iptables规则实现的缘故,因为其工作在3层(IP层),因此只能做到4层,即TCP连接级别的负载均衡,而当今大多数主流应用使用HTTP/GRPC协议进行通信,基于iptables规则实现的K8s服务负载均衡注定无法实现请求级别的路由。而服务网格控制平面从K8s集群ApiServer获得工作负载和服务信息,并将这些信息下发至SIdecar,借助其7层能力,支持对HTTP/GRPC请求实现请求级负载均衡,同时支持了适用于多种场景的负载均衡算法,用户可以根据业务特性,为特定服务选择适用的负载均衡算法,以更高效地利用系统资源。


 


 

 

负载均衡算法

 

服务网格ASM提供了多种负载均衡算法,其中包括与社区Istio兼容的RANDOM、ROUND_ROBIN,LEAST_REQUEST以及ASM特有的PEAK_EWMA。在本文接下来的内容中,笔者将逐一分析每种负载均衡算法的特点及其适用场景。

 

 

RANDOM和ROUND_ROBIN

 

服务网格ASM提供了经典的随机(RANDOM)和轮询(ROUND_ROBIN)算法,对于随机负载均衡,与K8s服务不同的是,由于ASM Sidecar工作在7层,因此对于HTTP、GRPC协议的通信可以提供请求级别的随机负载均衡。随机负载均衡由于随机计算本身的不稳定性,不能保证请求完全均匀地分布到后端。此时若工作负载的性能和请求产生的计算量都比较平均,则可以考虑使用轮询算法进行负载均衡,轮询算法可以完全平均地将请求分布到后端工作负载。这两种算法被广泛使用在各种分布式系统的负载均衡中,它们足够简单直观,且被几乎所有负载均衡软硬件支持,但是,在实践中却存在一些场景,使得这两种算法的表现不尽人意。

 

 

RANDOM和ROUND_ROBIN的不足之处

 

由于RANDOM和ROUND_ROBIN两种负载均衡算法均是完全不考虑后端服务状态,仅使用本地状态计算得出负载均衡结果的算法,因此,后端服务状态变化完全不会影响到负载均衡计算的结果,从而导致做出不够理想的负载均衡决策,例如以下这些场景:

 

1.后端处理能力不均衡

 

在后端处理能力不均衡时,完全均衡地分布请求反而会影响服务质量,由于后端负载能力不平均,处理能力弱的后端会更早过载,而负载均衡算法仍然以平均分配的方式将请求分配至后端,这些被分配至已过载的后端的请求会加剧已过载后端的压力,进一步恶化服务的延迟,影响服务的整体表现,例如下图例子中,Server-1的处理能力更强,还能够接受新的请求,而Server-2已经满载,但随机或者轮询负载均衡算法仍然将新请求打到Server-2,而不是尚有余力的Server-1,这将导致这个请求的返回实践因Server-2的满载而显著延长。


 


 

2.请求计算量不平均

 

即使后端的处理能力类似,同类请求对后端造成的压力也可能存在显著的差异,一个典型的例子是LLM应用,同样是几个单词的prompt,“write me a novel”和“what is the time”两个prompt在后端计算量上的差异可谓天壤之别,因此返回时间可能也存在显著差异,例如下图的例子中,Server-1和Server-2正在处理的请求虽然都是2个,但是,Server-2处理了一个大计算量的请求,导致其已经满载,然而随机或者轮询负载均衡算法仍然将新请求打到Server-2,而不是尚有余力的Server-1,这将导致这个请求的返回实践因Server-2的满载而显著延长。

 

即使是在请求计算量大致均等的场景下,错误的请求也可能导致类似的问题,例如客户端发送了错误的请求,服务端可能在校验失败后不做任何处理就立即返回,就会间接导致请求计算量不平均的问题。

 


 


 

如何避免RANDOM和ROUND_ROBIN的不足

 

RANDOM和ROUND_ROBIN存在的缺陷本质上还是因为负载均衡无法感知后端的状态所致,理论上来说,最优的负载均衡决策应该结合后端实时状态和请求的潜在计算量做出,然而,这两者在真正的负载均衡实现中都存在很大的挑战,那么是否存在对后端状态间接感知的方式呢?笔者将在下面的篇幅中介绍ASM支持的另外两种负载均衡算法。

 

 

LEAST_REQUEST

 

LEAST_REQUEST算法在用户未显式指定时被作为ASM默认提供的负载均衡算法,该负载均衡器会记录当前每个后端上未完成的请求数量,处理能力更强的后端往往会因请求更快完成而更快地消耗这个计数,负载均衡器借助这个现象来间接感知后端的压力状态,并总是将请求路由到未完成请求数最少的后端。

 

下面,我们来通过例子直观地感受一下LEAST_REQUEST的优势,首先以前面提到过的后端处理能力不均衡场景为例,在T1时刻,负载均衡向Server-1和Server-2分别分配了3个请求,由于Server-1的并发处理能力更大,到T2时刻时,Server-1已经完成了对全部3个请求的处理,而Server-2仍然有一个请求未完成处理,由于LEAST_REQUEST总是会向进行中请求最少的后端分配请求,因此,新到达的请求被分配至负载更低的Server-1。


 


 

我们再来看看请求计算量不均的场景,这个场景下同样的请求对后端的计算复杂度差异显著,下图例子中,在T1时刻,Server-1有2个进行中的请求,Server-2只有一个,但Server-2处理的是一个大计算量请求(例如一个复杂的Prompt)。到T2时刻,Server-1的2个请求都已经返回,而Server-2尚未完成一个复杂请求的计算和返回。此时,如果负载均衡器收到了新的请求,由于Server-1的进行中请求数量少于Server-2,新请求会被分配至Server-1。


 

 


结合上述两个例子,我们可以直观地看到LEAST_REQUEST负载均衡算法在应对更加复杂场景时相对于RANDOM和ROUND_ROBIN算法表现出了更强的自适应能力,通过进行中请求数的变化实时地感知后端地状态以做出更优的负载均衡决策,降低请求平均延迟,提升整个系统的利用率。这样的特性非常适合对于同类请求计算量差异的AI场景。

 

LEAST_REQUEST能在绝大多数情况下提供较好的负载均衡表现,但是LESAT_REQUEST是任何情况下的最优解吗?我们不妨思考这样一种情况,当后端因为异常出现报错时,由于错误请求没有被实际处理,往往会立即返回,LEAST_REQUEST负载均衡器中记录的报错节点的进行中的请求数量会快速降低,该后端在该评价体系下被误认为是“高性能”的,因此更多的请求被分配到发生错误的后端,进而造成系统的整体错误率提升。显然,这个缺陷是由于LEAST_REQUEST评估体系无法感知错误率的存在导致的,那么是否有更好的选择呢,接下来,笔者将介绍ASM提供的PEAK_EWMA负载均衡器。

 

 

PEAK_EWMA

 

 

ASM在1.21版本提供了PEAK_EWMA负载均衡器,该负载均衡器会ASM结合端点在过去一段时间内的响应时间、错误率,静态权重等因子计算出端点的负载均衡权重计算节点的分值,在一段时间内响应时间越短、错误率越低,静态权重越高,则该节点的分值越高,更容易被负载均衡选中。相比于LEAST_REQUEST,PEAK_EWMA的感知范围更全面,延迟升高、错误率上升都会造成该后端的权重降低,更少的请求被路由到这个后端,从而尽可能地提升服务整体地延迟和成功率表现。

 

这里我们提供了一个PEAK_EWMA负载均衡器的端到端实例,该示例使用simple-server应用作为服务端,该应用可通过启动参数指定,模拟不同状态码返回、随机延迟范围等多种服务端响应行为。在本例中,我们将部署两个Deployment,其中名为simple-server-503的deployment使用--mode=503启动参数设置为固定返回503,模拟一个收到请求立即返回503状态码的后端;名为simple-server-normal的deployment则使用--mode=normal--delayMin=50--delayMax=200模拟一个正常返回200的后端,其处理延迟在50-200之间波动。


 

 

 


前提条件

 

已创建ASM 1.21或以上版本的实例

已创建K8s 1.21或以上版本的集群,并已将K8s集群添加至ASM实例

 

 

部署示例

 

在K8s集群中通过以下yaml部署simple-server应用和服务,以及用于发起测试流量的sleep应用:


apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: simple-server
  name: simple-server-normal
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-server
  template:
    metadata:
      labels:
        app: simple-server
    spec:
      containers:
      - args:
        - --mode
        - normal
        image: registry-cn-hangzhou.ack.aliyuncs.com/test-public/simple-server:v1.0.0.2-gae1f6f9-aliyun
        imagePullPolicy: IfNotPresent
        name: simple-server
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: simple-server
  name: simple-server-503
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-server
  template:
    metadata:
      labels:
        app: simple-server
    spec:
      containers:
      - args:
        - --mode
        - "503"
        image: registry-cn-hangzhou.ack.aliyuncs.com/test-public/simple-server:v1.0.0.2-gae1f6f9-aliyun
        imagePullPolicy: IfNotPresent
        name: simple-server
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: simple-server
  name: simple-server
  namespace: default
spec:
  ports:
  - name: http
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: simple-server
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sleep
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      terminationGracePeriodSeconds: 0
      serviceAccountName: sleep
      containers:
      - name: sleep
        image: registry-cn-hongkong.ack.aliyuncs.com/test/curl:asm-sleep
        command: ["/bin/sleep", "infinity"]
        imagePullPolicy: IfNotPresent

 


为了避免ASM默认存在的重试机制(默认重试2次)对验证结果造成影响,我们通过为simple-server应用VirtualService来显式地关闭重试。使用ASM实例的kubeconfig,应用以下VirtualService:

 

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: simple-server
  namespace: default
spec:
  hosts:
    - simple-server.default.svc.cluster.local
  http:
    - name: default
      retries:
        attempts: 0 # 关闭重试
      route:
        - destination:
            host: simple-server.default.svc.cluster.local

 


使用K8s集群kubeconfig,执行以下命令来发起测试:

 

$ kubectl exec -it deploy/sleep -c sleep -- sh -c 'for i in $(seq 1 10); do curl -s -o /dev/null -w "%{http_code}\n" simple-server:8080/hello; done'
200
200
200
503
503
503
503
503
200
503

 


可以看到,由于simple-server服务下有一个后端是固定返回503的,默认的LEAST_REQUEST并不能很好地在负载均衡阶段避开返回错误的节点。接下来我们来看看ASM的PEAK_EWMA负载均衡器的表现,在ASM实例中应用以下DestinationRule,为simple-server服务启用PEAK_EWMA负载均衡器:

 

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: simple-server
  namespace: default
spec:
  host: simple-server.default.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      simple: PEAK_EWMA
---

 


使用同样的命令再次执行测试:

 

$ kubectl exec -it deploy/sleep -c sleep -- sh -c 'for i in $(seq 1 10); do curl -s -o /dev/null -w "%{http_code}\n" simple-server:8080/hello; done'
200
503
200
200
200
200
200
200
200
200

 


可以看到,在返回了一个503后,后续全部都是200,这是因为PEAK_EWMA负载均衡器在后端返回503后,导致错误率上升,从而降低了该断点的分值,因此在之后的一段时间内,该后端将在分值比较时低于正常的后端,因此不会被选择到。但是,过一段时间后,这种影响因为移动平均的缘故又被消除,从而使得负载均衡器可以重新选择到该后端。我们在前文提到过,PEAK_EWMA算法不仅可以感知后端的错误率,同时还可以感知后端延迟变化,尽可能将请求路由给延迟更低的端点,关于这一点ASM官方文档提供了一个例子,欢迎各位读者前往体验。

 

 

总结

 

负载均衡是一个现代分布式应用绕不开的一个重要话题,每一种负载均衡都有其适用的场景,根据业务场景正确地选择负载均衡算法能为应用带来更好的表现。阿里云服务网格ASM团队在结合了大量客户实际场景后,于1.21版本推出了PEAK_EWMA负载均衡算法,该负载均衡算法在传统的负载均衡算法上更进一步,提供了综合后端延迟、错误率、静态权重的负载均衡选择能力,让负载均衡能够动态适应应用状态的变化,做出更优的负载均衡决策,从而提升应用的全局表现。



我们是阿里巴巴云计算和大数据技术幕后的核心技术输出者。

欢迎关注 “阿里云基础设施”同名微博知乎

获取关于我们的更多信息~

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
12天前
|
负载均衡 算法 Java
Spring Cloud全解析:负载均衡算法
本文介绍了负载均衡的两种方式:集中式负载均衡和进程内负载均衡,以及常见的负载均衡算法,包括轮询、随机、源地址哈希、加权轮询、加权随机和最小连接数等方法,帮助读者更好地理解和应用负载均衡技术。
|
18天前
|
机器学习/深度学习 数据采集 存储
一文读懂蒙特卡洛算法:从概率模拟到机器学习模型优化的全方位解析
蒙特卡洛方法起源于1945年科学家斯坦尼斯劳·乌拉姆对纸牌游戏中概率问题的思考,与约翰·冯·诺依曼共同奠定了该方法的理论基础。该方法通过模拟大量随机场景来近似复杂问题的解,因命名灵感源自蒙特卡洛赌场。如今,蒙特卡洛方法广泛应用于机器学习领域,尤其在超参数调优、贝叶斯滤波等方面表现出色。通过随机采样超参数空间,蒙特卡洛方法能够高效地找到优质组合,适用于处理高维度、非线性问题。本文通过实例展示了蒙特卡洛方法在估算圆周率π和优化机器学习模型中的应用,并对比了其与网格搜索方法的性能。
117 1
|
21天前
|
负载均衡 监控 算法
揭秘负载均衡的五大算法秘籍:让你的服务器轻松应对亿万流量,不再崩溃!
【8月更文挑战第31天】在互联网快速发展的今天,高可用性和可扩展性成为企业关注的重点。负载均衡作为关键技术,通过高效分配网络流量提升系统处理能力。本文介绍了轮询、加权轮询、最少连接及IP哈希等常见负载均衡算法及其应用场景,并提供Nginx配置示例。此外,还探讨了如何根据业务需求选择合适算法、配置服务器权重、实现高可用方案、监控性能及定期维护等最佳实践,助力系统优化与用户体验提升。
47 2
|
26天前
|
算法 JavaScript 前端开发
国标非对称加密:RSA算法、非对称特征、js还原、jsencrypt和rsa模块解析
国标非对称加密:RSA算法、非对称特征、js还原、jsencrypt和rsa模块解析
105 1
|
27天前
|
缓存 算法 前端开发
深入理解缓存淘汰策略:LRU和LFU算法的解析与应用
【8月更文挑战第25天】在计算机科学领域,高效管理资源对于提升系统性能至关重要。内存缓存作为一种加速数据读取的有效方法,其管理策略直接影响整体性能。本文重点介绍两种常用的缓存淘汰算法:LRU(最近最少使用)和LFU(最不经常使用)。LRU算法依据数据最近是否被访问来进行淘汰决策;而LFU算法则根据数据的访问频率做出判断。这两种算法各有特点,适用于不同的应用场景。通过深入分析这两种算法的原理、实现方式及适用场景,本文旨在帮助开发者更好地理解缓存管理机制,从而在实际应用中作出更合理的选择,有效提升系统性能和用户体验。
57 1
|
1月前
|
机器学习/深度学习 算法 TensorFlow
【深度学习】深度学习语音识别算法的详细解析
深度学习语音识别算法是一种基于人工神经网络的语音识别技术,其核心在于利用深度神经网络(Deep Neural Network,DNN)自动从语音信号中学习有意义的特征,并生成高效的语音识别模型。以下是对深度学习语音识别算法的详细解析
50 5
|
1月前
|
JavaScript 算法 前端开发
"揭秘Vue.js的高效渲染秘诀:深度解析Diff算法如何让前端开发快人一步"
【8月更文挑战第20天】Vue.js是一款备受欢迎的前端框架,以其声明式的响应式数据绑定和组件化开发著称。在Vue中,Diff算法是核心之一,它高效计算虚拟DOM更新时所需的最小实际DOM变更,确保界面快速准确更新。算法通过比较新旧虚拟DOM树的同层级节点,递归检查子节点,并利用`key`属性优化列表更新。虽然存在局限性,如难以处理跨层级节点移动,但Diff算法仍是Vue高效更新机制的关键,帮助开发者构建高性能Web应用。
40 1
|
1月前
|
机器学习/深度学习 自然语言处理 负载均衡
揭秘混合专家(MoE)模型的神秘面纱:算法、系统和应用三大视角全面解析,带你领略深度学习领域的前沿技术!
【8月更文挑战第19天】在深度学习领域,混合专家(Mixture of Experts, MoE)模型通过整合多个小型专家网络的输出以实现高性能。从算法视角,MoE利用门控网络分配输入至专家网络,并通过组合机制集成输出。系统视角下,MoE需考虑并行化、通信开销及负载均衡等优化策略。在应用层面,MoE已成功应用于Google的BERT模型、Facebook的推荐系统及Microsoft的语音识别系统等多个场景。这是一种强有力的工具,能够解决复杂问题并提升效率。
47 2
|
26天前
|
存储 负载均衡 监控
自适应负载均衡算法原理和实现
自适应负载均衡算法原理和实现
|
4月前
|
Oracle 关系型数据库
oracle asm 磁盘显示offline
oracle asm 磁盘显示offline
232 2

推荐镜像

更多