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

在尝试将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-appports:- name: grpcprotocol: TCPport: 50051targetPort: 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-appports:- name: grpcprotocol: TCPport: 50051targetPort: 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.mzph.cn/news/100072.shtml

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

相关文章

亘古难题——前端开发or后端开发

一、引言 前端开发 前端开发是创建WEB页面或APP等前端界面呈现给用户的过程&#xff0c;通过HTML&#xff0c;CSS及JavaScript以及衍生出来的各种技术、框架、解决方案&#xff0c;来实现互联网产品的用户界面交互。 前端开发从网页制作演变而来&#xff0c;名称上有很明显的时…

C语言-数组

C 语言支持数组数据结构&#xff0c;数组是一个由若干相同类型变量组成的有序集合。 这里的有序是指数组元素在内存中的存放方式是有序的&#xff0c;即所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素&#xff0c;最高的地址对应最后一个元素。 在 C 语言中&am…

GNU和Linux的关系、 Linux的发行版本、CentOs和RedHat的区别

GNU和Linux的关系 其实&#xff0c;我们通常称之为的"Linux"系统&#xff0c;相对更准确的名称应该称为“GNU/Linux”系统&#xff01; 一个功能完全的操作系统需要许多不同的组成部分&#xff0c;其中就包括内核及其他组件&#xff1b;而在GNU/Linux系统中的内核就…

物联网AI MicroPython传感器学习 之 MQ136硫化氢传感器

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; 一、产品简介 MQ136 是一种硫化氢检测传感器&#xff0c;感应范围为 1 - 200ppm。传感元件是 SnO2&#xff0c;它在清洁空气中的电导率较低。当存在 H₂S 气体时&#xff0c;传感器的电导率随着气体浓度的升…

LeetCode 24.两两交换链表中的结点

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 首先可以特判一下&#xff0c;如果结点数目小于等于1&#xff0c;则直接返回即可&#xff0c;因为数目小于等于1就不需要交换了。 然后我们可以创建一个虚拟的头结点&#xff0c;然…

【力扣2011】执行操作后的变量值

&#x1f451;专栏内容&#xff1a;力扣刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、题目描述二、题目分析 一、题目描述 题目链接&#xff1a;执行操作后的变量值 存在一种仅支持 4 种操作和 1 个变量 …

用IDEA操作数据库--MySQL

IDEA集成了DataGrip的操作数据库的功能 就可以省略我们下载SQLyog/Navicat/DataGrip这些图形化操作工具了 以下是IDEA的使用 输入数据库的用户和密码

陀螺仪传感器解读-Gyro Acce,1

加速度计和陀螺仪的简介 https://www.cnblogs.com/zdxgloomy/articles/4171937.html 加速度计和陀螺仪的使用指南 &#xff0c;代码部分 https://www.amobbs.com/forum.php?modviewthread&tid5510930&_dsign972b156c 模拟加速度计: 1. Accelerometer prinicple. 加…

Java基本数据类型

Java基本数据类型 1 数值型 整型数据类型 数据类型内存空间&#xff08;8位1字节&#xff09;取值范围byte(字节型&#xff09;8位&#xff08;1字节&#xff09;-128~127 &#xff08;2的8次方&#xff09;short(短整型&#xff09;16位&#xff08;2字节&#xff09;-32768~3…

算法练习11——买卖股票的最佳时机 II

122. 买卖股票的最佳时机 II 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&#xff0c;然后在 同一天 出售。 返回 你能获得…

运维知识点汇总

一.公共基础 linux常用目录 链接一 链接二 linux系统启动 链接一 链接二 LVM 链接一 磁盘挂载 链接一 文件权限 链接一 二.VLAN详解 链接 三.中间件 单体部署&#xff1a; 优点&#xff1a; &#xff08;1&#xff09;小团队成型即可完成开发-测试-上线&am…

简单实现接口自动化测试(基于python+unittest)

简介 本文通过从Postman获取基本的接口测试Code简单的接口测试入手&#xff0c;一步步调整优化接口调用&#xff0c;以及增加基本的结果判断&#xff0c;讲解Python自带的Unittest框架调用&#xff0c;期望各位可以通过本文对接口自动化测试有一个大致的了解。 引言 为什么要…

软件行业与就业(导师主讲)

在企业软件应用的整体架构体系中&#xff0c;有一部分被称为中间件&#xff0c;那么什么叫中间件&#xff1f; 中间件&#xff08;Middleware&#xff09;是指位于操作系统和应用程序之间的一层软件层&#xff0c;它提供了一组工具和服务&#xff0c;用于简化和增强企业软件应用…

Sentinel入门

文章目录 初始Sentinel雪崩问题服务保护技术对比认识Sentinel微服务整合Sentinel 限流规则快速入门流控模式关联模式链路模式 流控效果warm up排队等待 热点参数限流全局参数限流热点参数限流 隔离和降级FeignClient整合Sentinel线程隔离熔断降级慢调用异常比例、异常数 授权规…

rustlings本地开发环境配置

克隆自己的仓库 首先我们要在github上找到自己仓库并把它克隆到本地 git clone https://github.com/LearningOS/rust-rustlings-2023-autumn-******.git下载插件 rust-analyzer和Git Graph一个可以用来解析rust代码&#xff0c;另一个可以可视化管理git代码库 下载rustling…

HTML5+CSS3+JS小实例:仿优酷视频轮播图

实例:仿优酷视频轮播图 技术栈:HTML+CSS+JS 效果: 源码: 【html】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content=&quo…

网络安全(骇客)—技术学习

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟入…

深入篇【C++】总结<lambda表达式>与<包装器和bind>的使用与意义

深入篇【C】总结&#xff1c;lambda表达式&#xff1e;与&#xff1c;包装器和bind&#xff1e;的使用与意义 一.lambda表达式1.使用语法2.底层本质3.应用意义 二.包装器(适配器)1.使用语法2.解决问题①3.解决问题②4.应用场景:指令操作 三.bind (适配器)1.调整参数位置2.绑定参…

C# Windows 窗体控件中的边距和填充

可以将 Margin 属性、Left、Top、Right、Bottom 的每个方面设置为不同的值&#xff0c;也可以使用 All 属性将它们全部设置为相同的值。 在代码中设置Margin&#xff0c;元素的左边设置为5个单位、上边设置为10个单位、右边设置为15个单位和下边设置为20个单位。 TextBox myT…

【gitlab】本地项目上传gitlab

需求描述 解决方法 下面的截图是gitlab空项目的描述 上传一个本地项目按其中“Push an existing folder”命令即可。 以renren-fast项目为例 # 用git bash 下载renren-fast项目 git clone https://gitee.com/renrenio/renren-fast.git# 在renren-fast的所属目录 打开git ba…