Envoy控制面实践

news/2024/11/2 2:19:18/

简介

Envoy 是一款由 Lyft 开源的,使用 C++ 编写的 L7 代理和通信总线,目前是 CNCF 旗下的开源项目且已经毕业,代码托管在 GitHub 上,它也是 Istio 服务网格中默认的数据平面。关于 Envoy 的详情请阅读 Envoy 中文文档。Envoy 本身无法构成一个完整的 Service Mesh,但是它可以作为 service mesh 中的应用间流量的代理,负责 service mesh 中的数据层。

Envoy架构中的一些重要概念:

  • Downstream:下游主机,指连接到Envoy的主机,这些主机用来发送请求并接受响应。

  • Upstream:上游主机,指接收来自Envoy连接和请求的主机,并返回响应。

  • Listener:服务或程序的监听器, Envoy暴露一个或多个监听器监听下游主机的请求,当监听到请求时,通过Filter Chain把对请求的处理全部抽象为Filter, 例如ReadFilter、WriteFilter、HttpFilter等。

  • Cluster:服务提供集群,指Envoy连接的一组逻辑相同的上游主机。Envoy通过服务发现功能来发现集群内的成员,通过负载均衡功能将流量路由到集群的各个成员。

  • xDS:xDS中的x是一个代词,类似云计算里的XaaS可以指代IaaS、PaaS、SaaS等。DS为Discovery Service,即发现服务的意思。xDS包括CDS(cluster discovery service)、RDS(route discovery service)、EDS(endpoint discovery service)、ADS(aggregated discovery service),其中ADS称为聚合的发现服务,是对CDS、RDS、LDS、EDS服务的统一封装,解决CDS、RDS、LDS、EDS信息更新顺序依赖的问题,从而保证以一定的顺序同步各类配置信息。以上Endpoint、Cluster、Route的概念介绍如下:

  • Endpoint:一个具体的“应用实例”,类似于Kubernetes中的一个Pod;

  • Cluster:可以理解“应用集群”,对应提供相同服务的一个或多个Endpoint, 类似Kubernetes中Service概念,即一个Service提供多个相同服务的Pod;

  • Route:当我们做金丝雀发布部署时,同一个服务会有多个版本,这时需要Route规则规定请求如何路由到其中的某个版本上。

Envoy正常的工作流程为Host A(下游主机)发送请求至上游主机(Host B、Host C、Host D等),Envoy通过Listener监听到有下游主机的请求,收到请求后的Envoy将所有请求流量劫持至Envoy内部,并将请求内容抽象为Filter Chains路由至某个上游主机中从而实现路由转发及负载均衡能力。

xDS

Envoy为了实现流量代理能力通常需要一个统一的配置文件来记录信息以便启动时加载,在Envoy中启动配置文件有静态配置和动态配置两种方式。静态配置是将配置信息写入文件中,启动时直接加载,动态配置通过xDS实现一个Envoy的服务端(可以理解为以API接口对外实现服务发现能力)。

xDS模块的功能是通过Envoy API V1(基于HTTP)或V2(基于gRPC)实现一个服务端将配置信息暴露给上游主机,等待上游主机的拉取。

xDS API 在envoy中被称为 Data plane API 。其代码保存在 https://github.com/envoyproxy/envoy/tree/master/api/envoy/api/v2,用户可以根据proto文件自行生成相对应语言的GRPC代码文件。

Envoy 官方提供了两份 xDS Server 的实现,分别是:

  • go-control-plane 基于Golang 的 xDS Server 实现代码

  • java-control-plane 基于 Java 的 xDS Server 实现代码

另外,官方还把 api 的定义代码从 Envoy 的源码库中提取出来,放在了 https://github.com/envoyproxy/data-plane-api

实践

要实现一个简单的控制面,一般就是基于xDS api来管理动态配置的能力,如下几步

1. 实现callback

type callbacks struct {test.CallbackssimpleCache cache.SnapshotCachehash        cache.NodeHashdatasource  service.Datasourceconfig      *Config
}func (c *callbacks) OnStreamRequest(id int64, request *discovery.DiscoveryRequest) error {nodeId := c.hash.ID(request.Node)logrus.Debugf("node: %s on stream request version_info: %s resource_names: %s type_url: %s response_nonce: %s error: %+v", nodeId, request.VersionInfo, request.ResourceNames, request.TypeUrl, request.ResponseNonce, request.ErrorDetail)if !containsString(c.simpleCache.GetStatusKeys(), nodeId) {c.SetSnapshot(nodeId)}return c.Callbacks.OnStreamRequest(id, request)
}

根据业务需要,实现相应的回调方法。

2. 创建grpc服务

server := server.NewServer(ctx, simpleCache, cb)var grpcOptions []grpc.ServerOption
grpcOptions = append(grpcOptions,grpc.MaxConcurrentStreams(grpcMaxConcurrentStreams),grpc.KeepaliveParams(keepalive.ServerParameters{Time:    grpcKeepaliveTime,Timeout: grpcKeepaliveTimeout,}),grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{MinTime:             grpcKeepaliveMinTime,PermitWithoutStream: true,}),
)
grpcServer := grpc.NewServer(grpcOptions...)lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {logrus.Fatal(err)
}discoverygrpc.RegisterAggregatedDiscoveryServiceServer(grpcServer, server)
endpointservice.RegisterEndpointDiscoveryServiceServer(grpcServer, server)
clusterservice.RegisterClusterDiscoveryServiceServer(grpcServer, server)
routeservice.RegisterRouteDiscoveryServiceServer(grpcServer, server)
listenerservice.RegisterListenerDiscoveryServiceServer(grpcServer, server)
secretservice.RegisterSecretDiscoveryServiceServer(grpcServer, server)
runtimeservice.RegisterRuntimeDiscoveryServiceServer(grpcServer, server)logrus.Infof("management server listening on %d\n", port)if err = grpcServer.Serve(lis); err != nil {logrus.Println(err)}

上面的流程大致就是:

  • 构造一个业务server实例,将回调实例传入

  • 构造一个grpc服务器实例

  • 向grpc注册业务服务,也就是各个xDS api的实现服务

  • 启动一个grpc服务器

3. 构造快照

snap, _ := cache.NewSnapshot(time.Now().Format(time.RFC3339),map[resource.Type][]types.Resource{resource.ClusterType:  clusters,resource.RouteType:    {makeRoute(RouteName, routes)},resource.ListenerType: {makeHTTPListener(ListenerName, RouteName, listenerPort)},},
)

根据xDS 数据格式,构造所需的业务数据,也就是最后存储在envoy中的动态配置

4. 返回快照

if err := snapshot.Consistent(); err != nil {logrus.Errorf("snapshot inconsistency: %+v\n%+v", snapshot, err)return
}if err := c.simpleCache.SetSnapshot(context.Background(), nodeId, snapshot); err != nil {logrus.Errorf("snapshot error %q for %+v", err, snapshot)return
}

将业务数据动态注入envoy配置中


http://www.ppmy.cn/news/60484.html

相关文章

PyTorch:深度学习框架的优雅演进与设计理念

❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️ 👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…

JavaScript (七) -- JavaScript 事件(需要了解的事件的运用)

目录 具体方法: 1. onmouseover事件的使用(鼠标悬浮事件) onmouseover事件的代码演示:

人脉社交社群运营系统源码

人脉社交社群运营需要注意以下几个方面: 社群定位:要明确社群的目标人群、服务内容和特点,以便吸引到符合社群需求的用户。 内容策划:需要提供丰富、有趣、有价值的内容,如文章、图片、视频等,以增…

协程实现原理

大家好,我是易安!今天我们来探讨一个问题,Go 协程的实现原理。此“协程”非彼”携程“。 线程实现模型 讲协程之前,我们先看下线程的模型。 实现线程主要有三种方式:轻量级进程和内核线程一对一相互映射实现的1:1线程…

『python爬虫』异常错误:request状态码是200,但是使用full xpath路径解析返回得到是空列表(保姆级图文)

目录 xpath 与 full xpath错因分析解决办法总结 欢迎关注 『python爬虫』 专栏,持续更新中 欢迎关注 『python爬虫』 专栏,持续更新中 xpath 与 full xpath xpath:相对路径full xpath :绝对路径 错因分析 可能是因为某些网页有特…

STM32F4_十进制和BCD码的转换

目录 前言 1. BCD码 2. BCD码和十进制转换的算法 前言 最近在学习STM32单片机(不仅仅是32)的RTC实时时钟系统的过程中,需要配置时钟的时间、日期;这些都需要实现BCD码和十进制之间进行转换。这里和大家一起学习BCD码和十进制之…

怎么成为一名架构师?架构师第一步。基层开发人员逆袭成为架构师真的很难吗?

文章目录 写在前面一、企业需要什么样的架构师1、从招聘软件上了解2、架构师的主要职责与能力 二、成为一名架构师很难吗1、架构师的定义2、当前大部分开发团队的现状3、为什么要有架构师4、技术人员如何自我突破 三、晨钟暮鼓的几句话 写在前面 一个团队中,每个人…

Chapter2:时域分析法(上)

第二章:时域分析法 Exercise2.1 已知系统的特征方程为: s 3 + 20 s 2 + 9 s + 100 = 0 s^3+20s^2+9s+100=0 s