在Kubernetes中实现gRPC流量负载均衡

news2024/9/22 1:18:45

在尝试将gRPC服务部署到Kubernetes集群中时,一些用户(包括我)面临的挑战之一是实现适当的负载均衡。在深入了解如何平衡gRPC的方式之前,我们首先需要回答一个问题,即为什么需要平衡流量,如果Kubernetes已经完成了这项工作。

本文关注于Kubernetes和Golang。

为什么在Kubernetes中无法适当地平衡gRPC流量?

之所以难以平衡gRPC流量的主要原因是人们将gRPC视为HTTP,这就是问题的根源。设计上它们是不同的,虽然HTTP为每个请求创建和关闭连接,但gRPC使用HTTP2协议,在长时间的TCP连接上运行,使得平衡更加困难,因为多个请求通过同一个连接进行多路复用。然而,这并不是配置gRPC服务在Kubernetes中出现平衡问题的唯一原因,以下是一些常见的错误配置:

  • 错误的gRPC客户端配置
  • 错误的Kubernetes服务配置

错误的gRPC客户端配置

设置gRPC客户端时常见的情况是选择默认配置,这对于1-1连接类型完全有效,但对于生产环境来说并不如我们所希望的有效。这背后的原因是因为默认的gRPC客户端提供了使用简单的IP/DNS记录连接的可能性,这只会创建一个与目标服务的连接。

因此,需要为与多个服务器建立连接进行不同的设置,将连接类型从1-1转换为1-N。

默认设置

func main(){
  conn, err := grpc.Dial("my-domain:50051", grpc.WithInsecure())
  if err != nil {
    log.Fatalf("error connecting with gRPC server: %v", err)
  }
  
  defer conn.Close()
  cli := test.NewTestServiceClient(conn)
  rs, err := cli.DoSomething(context.Background(), ...)
  .
  .
  .
}

新的设置

func main() {
  address := fmt.Sprintf("%s:///%s", "dns", "my-domain:50051")
  conn, err := grpc.Dial(address,
  grpc.WithInsecure(),grpc.WithBalancerName(roundrobin.Name))
  if err != nil {
    log.Fatalf("did not connect: %v", err)
  }
  
  defer conn.Close()
  .
  .
  .
}

这里有两个重要的更改需要注意:

  • 地址: 最终解析的地址将类似于 dns:///my-domain:50051,之所以使用这种格式是因为Dial函数允许我们使用由Scheme://Authority/Endpoint组成的目标,而在我们的情况下,我跳过了Authority。因此,首先我添加了dns作为方案,因为我希望解析一个域并持续观察其更改,解析器选项有透传(默认)、dns和手动,更多详情请参阅这里。
  • 负载均衡器选项: 如果我们的客户端现在连接到多个服务器,那么我们的gRPC客户端可以根据所选择的负载均衡算法平衡请求。

总结一下,我们的gRPC客户端现在能够创建不同的连接,前提是域名解析为多个A或AAAA记录,而且不仅如此,现在还能够将请求均匀地分配到不同的服务器。

现在让我们看看如何让它与Kubernetes一起工作的缺失部分。

错误的Kubernetes服务配置

在Kubernetes中创建服务非常简单,我们只需要定义服务名称、端口和选择器,以便服务可以动态地将Pod分组并自动平衡请求,如下所示:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - name: grpc
      protocol: TCP
      port: 50051
      targetPort: 50051

那么,对于先前的设置,问题在于默认的Kubernetes服务只创建了一个DNS记录,链接到单个IP。因此,当您执行类似 nslookup my-service.{namespace}.svc.cluster.local 的操作时,返回的是一个单个IP,这使得在常见的gRPC实现中连接图看起来像这样:

img

例如,使用默认的Kubernetes服务的连接图:

绿线表示与客户端的活动连接,黄色表示未活动的Pod。客户端与Kubernetes服务创建了持久连接,同时服务也与其中一个Pod创建了连接,但这并不意味着服务与其余的Pod没有连接。

让我们使用一个无头服务来解决这个问题:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
 clusterIP: None **this is the key***
 selector:
    app: my-app
  ports:
    - name: grpc
      protocol: TCP
      port: 50051
      targetPort: 50051

创建了无头服务后,nslookup看起来有些不同,现在它返回与之关联的记录(将Pod的IP分组到服务中),从而使gRPC客户端更好地了解需要连接的服务器数量。

现在您已经看到了gRPC客户端的配置,您必须知道为什么Kubernetes服务返回与一组Pod关联的IP非常重要。原因是客户端可以看到所有需要建立连接的服务器。在这一点上,您可能已经意识到了一个注意事项,即平衡的责任现在在客户端部分,而不在Kubernetes的一侧。我们现在需要从Kubernetes那里得到的主要任务是保持与服务关联的Pod列表的最新状态。

img

例如,在具有无头Kubernetes服务的连接图中,可以看到连接发生了一些变化,现在我们不通过Kubernetes服务来访问Pod,而是使用Kubernetes服务来检索与域名关联的Pod列表,然后直接与Pod建立连接。但是不要因为直接连接到Pod而感到惊慌,因为我们在客户端中设置了DNS解析器类型,该解析器将持续监视与无头服务的更改,并将与可用的Pod保持最新的连接。

为什么不使用服务网格?

如果可以的话,请使用服务网格,因为在服务网格中,所有这些设置都是透明的,而且最重要的是它是与编程语言无关的。关键区别在于服务网格利用了Sidecar模式和控制平面来编排入站和出站流量,还可以看到所有网络和流量类型(HTTP、TCP等),从而能够正确平衡请求。简而言之,如果您不使用服务网格,那么您需要直接从每个客户端连接到多个服务器,或者连接到一个L7代理来帮助平衡请求。

附加信息

尽管先前的设置可以工作,但我在尝试在alpine Linux映像中进行Pod轮换或扩展时重新平衡连接时遇到了问题。经过一些研究,我意识到我并不是唯一遇到这种问题的人,可以查看这里和这里的一些相关的GitHub问题。这就是为什么我决定创建自己的解析器的原因,您可以在这里查看我创建的自定义解析器,我创建的自定义解析器非常基础,但现在可以正常工作,gRPC客户端现在可以再次监听域名的更改,我还为该库添加了一个可配置的监听器,它每隔一段时间查找域名并更新提供给gRPC连接管理器的IP集合,如果您想贡献,欢迎加入。

另一方面,因为我想深入了解,所以我决定创建自己的gRPC代理(我也学到了很多东西),利用了gRPC的http2基础,我可以创建一个代理,而无需更改proto负载消息或甚至不知道proto文件的定义(还使用了前面提到的自定义解析器)。

最后,我想说的是,如果您的gRPC客户端需要与许多服务器连接,我强烈建议使用代理作为平衡的机制,因为将这个机制放在主应用程序中将增加复杂性和资源消耗,尝试保持许多打开的连接并重新平衡它们,想象一下,如果最终的平衡在应用程序中,您将有一个与N个服务器连接的实例(1-N),但是使用代理,您将有一个与M个代理连接到N个服务器的实例(1-M-N),其中M<N,因为每个代理实例可以处理与不同服务器的许多连接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1075533.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【技能树笔记】网络篇——练习题解析(五)

目录 前言 一、应用层的作用 1.1 应用层的作用 二、HTTP协议 2.1 HTTP协议 三、FTP协议 3.1 FTP协议 四、DNS协议 4.1 DNS协议 五、DHCP协议 5.1 DHCP协议 六、邮件协议 6.1 电子邮件协议 总结 前言 本篇文章给出了CSDN网络技能树中的部分练习题解析&#xff0c…

检验检疫系统(LIS)源码:C# + MVC + SQLserver + Redis

LIS系统源码技术特点&#xff1a;采用.Net Core新的技术框架、DEV报表、前端js封装、分布式文件存储、分布式缓存等&#xff0c;支持LIS独立部署&#xff0c;Docker部署等多种方式。 技术架构&#xff1a;ASP.NET CORE 3.1 MVC SQLserver Redis等 开发语言&#xff1a;C…

每日一题——数据分析题

2023年上海市高等学校信息技术水平考试模拟题&#xff1a;数据分析题 读取csv文件这里可以使用pd.read_csv() import pandas as pd# 读取CSV文件 fileroad r素材\city_GDP.csv data pd.read_csv(fileroad, encodinggbk)# 输入地区城市名 city input("请输入城市&#…

SpringBoot之实现Web消息实时消息推送

文章目录 1 实时消息推送1.1 消息推送1.2 准备sql1.3 短轮询1.4 长轮询1.4.1 简介1.4.2 代码示例 1.5 iframe流1.6 SSE1.6.1 简介1.6.2 与 WebSocket 区别1.6.3 代码示例 1.7 MQTT1.7.1 简介1.7.2 为什么要用 MQTT协议 1.8 WebSocket 1 实时消息推送 1.1 消息推送 推送的场景…

[NCTF2019]SQLi regexp 盲注

/robots.txt 访问一下 $black_list "/limit|by|substr|mid|,|admin|benchmark|like|or|char|union|substring|select|greatest|%00|\|| |in|<|>|-|\.|\(\)|#|and|if|database|users|where|table|concat|insert|join|having|sleep/i";If $_POST[passwd] admi…

Vue的MVVM模型

MVVM模型代表三个东西&#xff1a; 1. M : 模型&#xff08;model&#xff09; --> data中的数据 2. V : 视图&#xff08;view) --> 模版代码 3. VM : 视图模型&#xff08;viewmodel&#xff09; --> Vue实例 我们用代码来展示上图中的关系&#xff1a; 也就…

激光除草距离我们实际的农业生活还有多远,结合近期所见所感基于yolov8开发构建田间作物杂草检测识别系统

最近的中秋、国庆双节相信很多朋友都会回家和家人团聚&#xff0c;我也一样&#xff0c;我的家乡是比较小众偏远的乡村&#xff0c;童年给我的最大的记忆就是干不完的农活&#xff0c;而这农活中很大一部分就是蹲在田间地头去人力拔草、撒肥料等&#xff0c;人力终究是很慢效率…

【Linux】 vi / vim 使用

天天用vim 或者vi 。看着大佬用的很6 。我们却用的很少。今天咱们一起系统学习一下。 vi / vim 发展史 vi 是一款由加州大学伯克利分校&#xff0c;Bill Joy研究开发的文本编辑器。 vim Vim是一个类似于Vi的高度可定制的文本编辑器&#xff0c;在Vi的基础上改进和增加了很多…

京东商品链接获取京东商品详情数据接口(用 Python 实现京东商品信息抓取)

在网页抓取方面&#xff0c;可以使用 Python、Java 等编程语言编写程序&#xff0c;通过模拟 HTTP 请求&#xff0c;获取京东网站上的商品页面。在数据提取方面&#xff0c;可以使用正则表达式、XPath 等方式从 HTML 代码中提取出有用的信息。值得注意的是&#xff0c;京东网站…

iTunes是什么意思?iTunes详细使用教程来了!

很多使用苹果设备的小伙伴听说过iTunes&#xff0c;但却很少接触这个软件。就像小编一样&#xff0c;一开始感觉自己根本用不上iTunes。但是当了解到iTunes的知识与使用技巧后&#xff0c;小编才意识到它有多好用。那么&#xff0c;itunes是什么意思&#xff1f;iTunes到底有什…

python爬虫:多线程收集/验证IP从而搭建有效IP代理池

目录 一、前言 二、IP池的实现 1. 收集代理IP 2. 验证代理IP可用性 3. 搭建IP代理池 三、多线程实现 四、代理IP的使用 五、总结 一、前言 在网络爬虫中&#xff0c;IP代理池的作用非常重要。网络爬虫需要大量的IP地址来发送请求&#xff0c;同时为了降低被封禁的风险…

四、WebGPU Storage Buffers 存储缓冲区

四、WebGPU Storage Buffers 存储缓冲区 存储缓冲区 storage buffers 在许多方面 uniform buffers 缓冲区相似。如果我们所做的只是在JavaScript中将UNIFORM改为STORAGE&#xff0c;WGSL 中的 var 改为 var<storage,read>&#xff0c;上一节的示例代码同样可以运行并达到…

React +ts + babel+webpack

babel babel/preset-typescript 专门处理ts "babel/cli": "^7.17.6", "babel/core": "^7.17.8", "babel/preset-env": "^7.16.11", "babel/preset-react": "^7.16.7", "babel/preset…

vue3学习(二)--- ref和reactive

文章目录 ref1.1 ref将基础类型和对象类型数据转为响应式1.2 ref()获取id元素1.3 isRef reactive1.1 reactive()将引用类型数据转为响应式数据&#xff0c;基本类型无效1.2 ref和reactive的联系 toRef 和 toRefs1.1 如果原始对象是非响应式的就不会更新视图 数据是会变的 ref …

Redis高可用技术 二:主从复制、哨兵、Cluster集群

文章目录 1. 主从复制1.1 简介1.2 主从复制的作用1.3 主从复制的特性1.4 主从复制的工作原理1.4.1 全量复制1.4.2 增量复制 1.5 Redis主从同步策略 2. 搭建Redis主从复制2.1 前置准备2.2 配置master节点2.3 配置slave1-2节点2.4 主从复制结果验证 3. 哨兵模式3.1 简介3.2 哨兵的…

若依微服务部署,裸服务部署、docker部署、k8s部署

目录 前言windows 部署若依-微服务版本浏览器验证docker部署若依-微服务版本浏览器验证k8s部署若依-微服务版本浏览器验证总结 前言 环境&#xff1a;centos7、Win10 若依是一个合适新手部署练习的开源的微服务项目&#xff0c;本篇讲解Windows部署若依微服务、docker部署若依…

【鼠标右键菜单添加用VSCode打开文件或文件夹】

鼠标右键菜单添加用VSCode打开文件或文件夹 演示效果如下&#xff1a; 右击文件 或右击文件夹 或在文件夹内空白处右击 方法一&#xff1a;重装软件 重装软件&#xff0c;安装时勾选如图所示方框&#xff08;如果登录的有账号保存有配置信息可以选择重装软件&#xff0c…

YOLOv7暴力涨点:Gold-YOLO,遥遥领先,超越所有YOLO | 华为诺亚NeurIPS23

💡💡💡本文独家改进:提出了全新的信息聚集-分发(Gather-and-Distribute Mechanism)GD机制,Gold-YOLO,替换yolov7 head部分 实现暴力涨点 Gold-YOLO | 亲测在多个数据集能够实现大幅涨点,适用各个场景的涨点 收录: YOLOv7高阶自研专栏介绍: http://t.csdnim…

49位主播带货破亿,单品直播销量100万+,9月的黑马都是谁?

9月&#xff0c;抖音电商开始为下半年重要的营销节点做出筹备&#xff0c;不仅发起抖音中秋好礼季&#xff0c;还抢先发布双11品牌玩法攻略&#xff0c;活跃平台的消费氛围。 那么&#xff0c;9月有哪些主播表现突出&#xff0c;哪些商品在畅销&#xff0c;哪些达人的粉丝数飙升…

【数据结构与算法】如何对快速排序进行细节优化以及实现非递归版本的快速排序?

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;国庆长假结束了&#xff0c;无论是工作还是学习都该回到正轨上来了&#xff0c;从今天开始恢复正常的更新频率&#xff0c;今天为大家带来的内容…