使用 go-control-plane 自定义服务网格控制面

写在前面

阅读本文需要最起码了解envoy相关的概念

本文只是一个类似于demo的测试,只为了学习istio,更好的理解istio中的控制面和数据面(pilot -> proxy)是如何交互的,下图的蓝色虚线
在这里插入图片描述
先说go-control-plane是什么,它是一个用于实现xDS APIgolang库,xDS APIEnvoy用于动态配置的协议。我们实现了go-control-plane就可以做到

  1. 动态配置管理:允许控制面动态更新数据面代理的配置
  2. 支持多种 xDS 资源
  3. 缓存和版本管理:提供快照缓存机制,管理配置的版本和更新

使用go-control-plane我们可以创建一个自定义控制面来管理·Envoy`,从而实现动态的服务发现、负载均衡和路由等等…

xds则是一系列服务发现的总称

  • lds 监听器服务发现
  • rds 路由服务发现
  • cds 集群服务发现
  • eds 端点服务发现
  • ads 聚合服务发现

等等等等,还有一些其他的服务发现,本文不涉及就没有说到,如果不理解这些概念,建议先去官网了解一下 https://www.envoyproxy.io

实现 go-control-plane

功能描述

在这里插入图片描述
上文是envoy代理的架构,程序中的逻辑我使用倒叙的方式描述

  • 创建endpoint地址是www.envoyproxy.io端口是443
  • 创建cluster叫做xds_demo_cluster它的端点就是上面创建的
  • 创建路由在filter_chins下的http_connection_manager中名称叫做xds_demo_route,没有任何的路由规则,路由的cluster名称(请求转发的目的地)叫做xds_demo_cluster
  • 最后创建listener名称是listener_xds_demo监听的地址是0.0.0.0:12000

整体就是当我们访问localhost:12000的时候会将我们的请求转发到www.envoyproxy.io
如果运行过默认的envoy的用户可能就会发现我程序中下发的配置就是默认运行envoy时的配置,只不过默认运行envoy静态配置文件的方式就是所有的配置都写在envoy.yaml中,而本文是动态的方式。

envoy有多种运行方式本文不做赘述

功能实现

package mainimport ("context""log""net""time"cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"routerv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"http_connection_manager "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"clusterservice "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3"discoverygrpc "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"endpointservice "github.com/envoyproxy/go-control-plane/envoy/service/endpoint/v3"listenerservice "github.com/envoyproxy/go-control-plane/envoy/service/listener/v3"routeservice "github.com/envoyproxy/go-control-plane/envoy/service/route/v3""github.com/envoyproxy/go-control-plane/pkg/cache/types""github.com/envoyproxy/go-control-plane/pkg/cache/v3""github.com/envoyproxy/go-control-plane/pkg/resource/v3""github.com/envoyproxy/go-control-plane/pkg/server/v3""google.golang.org/grpc""google.golang.org/protobuf/types/known/anypb""google.golang.org/protobuf/types/known/durationpb"
)func main() {ctx := context.Background()lis, err := net.Listen("tcp", ":18000")if err != nil {log.Fatalf("Failed to listen: %v", err)}sc := cache.NewSnapshotCache(true, cache.IDHash{}, nil)srv := server.NewServer(ctx, sc, nil)// new grpc servergs := grpc.NewServer()clusterservice.RegisterClusterDiscoveryServiceServer(gs, srv)endpointservice.RegisterEndpointDiscoveryServiceServer(gs, srv)listenerservice.RegisterListenerDiscoveryServiceServer(gs, srv)routeservice.RegisterRouteDiscoveryServiceServer(gs, srv)discoverygrpc.RegisterAggregatedDiscoveryServiceServer(gs, srv)err = setSnapshot(ctx, "xds-node-id", sc)if err != nil {log.Fatalf("set snapshot error: %v", err)} else {log.Println("set snapshot success")}log.Println("Starting control plane server...")if err := gs.Serve(lis); err != nil {log.Fatalf("Failed to serve: %v", err)}
}func setSnapshot(ctx context.Context, nodeId string, sc cache.SnapshotCache) error {clusterName := "xds_demo_cluster"manager := buildHttpManager(clusterName)fcs := buildFilterChain(manager)serviceListener := buildListener("0.0.0.0", 12000, fcs)serviceEndpoint := buildEndpoint()serviceCluster := buildCluster(clusterName, serviceEndpoint)rs := map[resource.Type][]types.Resource{resource.ClusterType:  {serviceCluster},resource.EndpointType: {serviceEndpoint},resource.ListenerType: {serviceListener},resource.RouteType:    {manager},}snapshot, err := cache.NewSnapshot("1", rs)if err != nil {log.Fatalf("new snapshot error: %v", err)}return sc.SetSnapshot(ctx, nodeId, snapshot)
}func buildFilterChain(manager *http_connection_manager.HttpConnectionManager) []*listener.FilterChain {managerPB, err := anypb.New(manager)if err != nil {log.Fatalf("Failed to marshal HttpConnectionManager: %v", err)}fcs := make([]*listener.FilterChain, 0, 0)fcs = append(fcs, &listener.FilterChain{Filters: []*listener.Filter{{Name: "envoy.filters.network.http_connection_manager",ConfigType: &listener.Filter_TypedConfig{TypedConfig: managerPB,},},},})return fcs
}func buildHttpManager(clusterName string) *http_connection_manager.HttpConnectionManager {xdsRoute := &route.RouteConfiguration{Name: "xds_demo_route",VirtualHosts: []*route.VirtualHost{{Name:    "xds_demo_service",Domains: []string{"*"},Routes: []*route.Route{{Match: &route.RouteMatch{PathSpecifier: &route.RouteMatch_Prefix{Prefix: "/",},},Action: &route.Route_Route{Route: &route.RouteAction{HostRewriteSpecifier: &route.RouteAction_HostRewriteLiteral{HostRewriteLiteral: "www.envoyproxy.io",},// 集群要去下文一致ClusterSpecifier: &route.RouteAction_Cluster{Cluster: clusterName,},},},},},},},}routerConfig, _ := anypb.New(&routerv3.Router{})// http 链接管理器manager := &http_connection_manager.HttpConnectionManager{StatPrefix: "ingress_http",RouteSpecifier: &http_connection_manager.HttpConnectionManager_RouteConfig{RouteConfig: xdsRoute,},HttpFilters: []*http_connection_manager.HttpFilter{{Name: "envoy.filters.http.router",ConfigType: &http_connection_manager.HttpFilter_TypedConfig{TypedConfig: routerConfig,},},},SchemeHeaderTransformation: &corev3.SchemeHeaderTransformation{Transformation: &corev3.SchemeHeaderTransformation_SchemeToOverwrite{SchemeToOverwrite: "https",},},}return manager
}func buildEndpoint() *endpoint.LbEndpoint {epTarget := &endpoint.LbEndpoint{HostIdentifier: &endpoint.LbEndpoint_Endpoint{Endpoint: &endpoint.Endpoint{Address: &corev3.Address{Address: &corev3.Address_SocketAddress{SocketAddress: &corev3.SocketAddress{Address: "www.envoyproxy.io",PortSpecifier: &corev3.SocketAddress_PortValue{PortValue: 443,},},},},},},}return epTarget
}func buildCluster(clusterName string, ep *endpoint.LbEndpoint) *cluster.Cluster {serviceCluster := &cluster.Cluster{Name:           clusterName,ConnectTimeout: durationpb.New(time.Second * 3),ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS,},DnsLookupFamily: cluster.Cluster_V4_ONLY,LbPolicy:        cluster.Cluster_ROUND_ROBIN,LoadAssignment: &endpoint.ClusterLoadAssignment{ClusterName: clusterName,Endpoints: []*endpoint.LocalityLbEndpoints{{LbEndpoints: []*endpoint.LbEndpoint{ep},},},},TransportSocket: &corev3.TransportSocket{Name:       "envoy.transport_sockets.tls",ConfigType: nil,},}us := &tlsv3.UpstreamTlsContext{Sni: "www.envoyproxy.io",}tlsConfig, _ := anypb.New(us)serviceCluster.TransportSocket.ConfigType = &corev3.TransportSocket_TypedConfig{TypedConfig: tlsConfig,}return serviceCluster
}func buildListener(ip string, port uint32, fcs []*listener.FilterChain) *listener.Listener {return &listener.Listener{Name: "listener_xds_demo",Address: &corev3.Address{Address: &corev3.Address_SocketAddress{SocketAddress: &corev3.SocketAddress{Address: ip,PortSpecifier: &corev3.SocketAddress_PortValue{PortValue: port,},},},},// 过滤器链FilterChains: fcs,}
}
  • cache.NewSnapshotCache返回一个SnapshotCachego-control-plane中的一个核心组件,用于管理和存储Envoy所需的 xDS资源的快照,并且向Envoy实例推送更新
  • server.NewServer创建xDS服务实例
  • sc.SetSnapshot用于将生成的配置快照设置到指定的节点上,是动态配置和更新Envoy代理的行为,入参有一个id 是下文envoy引导配置中的id

截止到现在我们就可以启动这个服务,我们要记住当前服务监听的地址,因为在envoy.yaml中会需要使用到的

为 envoy 设置引导配置

引导配置(bootstrap configuration),引导配置文件通常指定控制面地址(如xDS服务器地址)、监听器、集群、管理接口等基本信息。

node:id: xds-node-idcluster: xds-node-clusterdynamic_resources:ads_config:api_type: GRPCgrpc_services:- envoy_grpc:cluster_name: xds_clustercds_config:ads: {}lds_config:ads: {}static_resources:clusters:- name: xds_clusterconnect_timeout: 1stype: STRICT_DNSlb_policy: ROUND_ROBINhttp2_protocol_options: {}load_assignment:cluster_name: xds_clusterendpoints:- lb_endpoints:- endpoint:address:socket_address:address: 127.0.0.1port_value: 18000admin:access_log_path: /dev/nulladdress:socket_address:address: 0.0.0.0port_value: 9901

解释

  1. static_resources
    1. 定义静态集群xds_cluster,用于与xDS服务器通信。这里的xDS服务器运行在127.0.0.1:18000就是我们上面服务监听的地址
  2. dynamic_resources
    1. ads_config: 配置聚合发现服务(ADS)来动态获取配置,使用gRPC服务与xds_cluster进行通信
    2. cds_configlds_config分别表示使用ADS来获取配置
  3. admin
    1. 定义管理接口,监听0.0.0.0:9901,用于查看Envoy的状态和配置
  4. node
    1. id 节点的唯一标识符,用于在xDS服务器中区分不同的Envoy实例
    2. cluster 节点所属的集群名称。

然后启动envoy,从输出的日志中我们可以看到通过控制面下发的配置,数据面已经加载成功了。

[2024-06-27 07:47:53.524][1][info][main] [source/server/server.cc:977] starting main dispatch loop
[2024-06-27 07:47:53.526][1][info][upstream] [source/common/upstream/cds_api_helper.cc:32] cds: add 1 cluster(s), remove 1 cluster(s)
[2024-06-27 07:47:53.527][1][info][upstream] [source/common/upstream/cds_api_helper.cc:71] cds: added/updated 1 cluster(s), skipped 0 unmodified cluster(s)
[2024-06-27 07:47:53.544][1][info][upstream] [source/common/upstream/cluster_manager_impl.cc:240] cm init: all clusters initialized
[2024-06-27 07:47:53.544][1][info][main] [source/server/server.cc:957] all clusters initialized. initializing init manager
[2024-06-27 07:47:53.546][1][info][upstream] [source/common/listener_manager/lds_api.cc:106] lds: add/update listener 'listener_xds_demo'
[2024-06-27 07:47:53.546][1][info][config] [source/common/listener_manager/listener_manager_impl.cc:930] all dependencies initialized. starting workers

下一步我们可以访问一下监听的地址,可以看到成功转发到了envoy的官方网站。

~ $ curl http://localhost:12000 | grep title% Total    % Received % Xferd  Average Speed   Time    Time     Time  CurrentDload  Upload   Total   Spent    Left  Speed19 15571   19  3098    0     0   5286      0  0:00:02 --:--:--  0:00:02  5286    <title>Envoy proxy - home</title>
100 15571  100 15571    0     0  21685      0 --:--:-- --:--:-- --:--:-- 21686

我们还可以通过 ~ $ curl http://localhost:9901/config_dump 来查看envoy的实时配置

写在最后

动态配置的方式是在内存中加载配置,不会更新到静态的文件中。

更高级、复杂的用法可以参考istio;具体来说pilot watch集群中的服务、端点、配置等资源的变化。当检测到这些资源的变化时,pilot会生成新的配置,并通过xDS API将更新推送到相应的Envoy实例,从而实现动态配置和管理服务网格中的流量控制和路由规则。这样可以确保 Envoy始终具有最新的服务发现信息和路由配置。

源码目录 https://github.com/istio/istio/tree/master/pilot

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

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

相关文章

MySQL高级-MVCC-原理分析(RC级别)

文章目录 1、RC隔离级别下&#xff0c;在事务中每一次执行快照读时生成ReadView2、先来看第一次快照读具体的读取过程&#xff1a;3、再来看第二次快照读具体的读取过程: 1、RC隔离级别下&#xff0c;在事务中每一次执行快照读时生成ReadView 我们就来分析事务5中&#xff0c;两…

柯桥法语学习|学点黑话!法语中的「钱」可不止“argent”

法语中有哪些关于钱的“黑话”&#xff1f;一起来和法语君看一下吧&#xff01; bl 之所以繁杂&#xff0c;是因为这些词在诞生之初&#xff0c;不止涉及一个故事&#xff0c;而是一大堆小轶事&#xff0c;以“bl”指钱的起源如迷宫般复杂。 根据Trsor de la langue frana15857…

VMware17安装Ubuntu20版本-保姆级别

首先需要安装好VMware和Ubuntu20的镜像&#xff0c;在网上搜索Ubuntu镜像下载即可&#xff0c;最后选择国内镜像站下载&#xff0c;这样更快点&#xff0c;然后打开VMware。 1.创建虚拟机&#xff1a; 2.选择自定义&#xff1a; 3.默认&#xff0c;继续下一步&#xff1a; 4.选…

kubuadm 方式部署 k8s 集群

准备三台机器 主机名IP地址CPU/内存角色K8S版本Docker版本k8s231192.168.99.2312C4Gmaster1.23.1720.10.24k8s232192.168.99.2322C4Gwoker1.23.1720.10.24k8s233192.168.99.2332C4Gwoker1.23.1720.10.24 需要在K8S集群各节点上面安装docker&#xff0c;如未安装则参考 …

江协科技51单片机学习- p21 LED点阵屏(8*8)

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

ASP.NET CORE应用针对IIS有哪两种部署模式?

一、ASP.NET CORE Core Module IIS其实也是按照管道的方式来处理请求的&#xff0c;但是IIS管道和ASP.NET CORE中间件管道有本质的不同。对于部署在IIS中的Web应用来说&#xff0c;从最初接收到请求到最终将响应发出去&#xff0c;这段处理流程被细分为一系列固定的步骤&#x…

Python pip install模块时C++编译环境问题

pip install模块时C编译环境问题 在接触和使用python后&#xff0c;常常会通过pip install命令安装第三方模块&#xff0c;大多数模块可以直接安装&#xff0c;但许多新同学仍会遇见某些模块需要实时编译后才能安装&#xff0c;如报错信息大概是缺乏C编译环境&#xff0c;本文则…

MTK7628+MT7612 加PA定频数据

1、硬件型号TR726A5G121-DPA PC9.02.0017。如下所示&#xff1a; 2、WIFI5.8 AC模式 42&#xff08;5120MHz&#xff09;信道&#xff0c;80带宽 3、WIFI5.8 AC模式 38&#xff08;5190MHz&#xff09;信道&#xff0c;40带宽 4、WIFI5.8 AC模式 36&#xff08;5180 MHz&…

双指针-移动 0(easy)

目录 一、问题描述 二、解题思路 三、代码实现 四、刷题链接 一、问题描述 二、解题思路 1.开辟一个新数组&#xff0c;初始时元素都为0 2.设置两个指针&#xff0c;指针p1遍历旧数组&#xff0c;指针p2指向新数组的待修改位置 3.当p1指向非零元素时&#xff0c;将该元素…

Ubuntu20.04离线安装Docker

链接&#xff1a;https://pan.baidu.com/s/10OLClTHSIJY-_nbldcoFFQ 提取码&#xff1a;x4nt --来自百度网盘超级会员V4的分享 1.下载3个docker离线安装包&#xff0c;下载网址&#xff1a; https://download.docker.com/linux/ubuntu/dists/xenial/pool/stable/amd64/ 2.把…

【阅读论文】-- IDmvis:面向1型糖尿病治疗决策支持的时序事件序列可视化

IDMVis: Temporal Event Sequence Visualization for Type 1 Diabetes Treatment Decision Support 摘要1 引言2 1 型糖尿病的背景3 相关工作3.1 时间事件序列可视化3.2 电子健康记录可视化3.3 1 型糖尿病可视化3.4 任务分析与抽象 4 数据抽象5 层次化任务抽象5.1 临床医生工作…

Python逻辑控制语句 之 判断语句--if else结构

1.if else 的介绍 if else &#xff1a;如果 ... 否则 .... 2.if else 的语法 if 判断条件: 判断条件成立&#xff0c;执行的代码 else: 判断条件不成立&#xff0c;执行的代码 &#xff08;1&#xff09;else 是关键字, 后⾯需要 冒号 &#xff08;2&#xff09;存在冒号…

Unity扩展编辑器功能的特性

1.添加分组标题 用于在Unity的Inspector视图中为属性或变量组创建一个自定义的标题或头部&#xff0c;有助于在Inspector中组织和分类不同的属性&#xff0c;使其更易于阅读和管理。 [Header("Common Properties")] public float MouseSensitivity 5; public float…

15 个适用于企业的生成式 AI 用例

作者&#xff1a;来自 Elastic Jennifer Klinger 关于生成式人工智能及其能做什么&#xff08;和不能做什么&#xff09;有很多讨论。生成式人工智能&#xff08;例如大型语言模型 - LLMs&#xff09;利用从大量训练数据中学习到的模式和结构来创建原创内容&#xff0c;而无需存…

解决“Duplicate keys detected: ‘ ‘.This may cause an update error.”问题

问题原因 出现“Duplicate keys detected”的错误&#xff0c;通常表示在v-for指令中使的:key绑定值有重复。 如果前端是静态数据&#xff0c;一般能自我避免:key绑定值有重复。如果前端是绑定的动态数据&#xff0c;那么需要另外提供一个唯一的键。 在这个例子中&#xff0c…

Spring+Vue项目部署

目录 一、需要的资源 二、步骤 1.首先要拥有一个服务器 2.项目准备 vue&#xff1a; 打包: 3.服务器装环境 文件上传 设置application.yml覆盖 添加启动和停止脚本 ​编辑 安装jdk1.8 安装nginx 安装mysql 报错&#xff1a;「ERR」1273-Unknown collation: utf8m…

2024年【广东省安全员A证第四批(主要负责人)】新版试题及广东省安全员A证第四批(主要负责人)考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【广东省安全员A证第四批&#xff08;主要负责人&#xff09;】新版试题及广东省安全员A证第四批&#xff08;主要负责人&#xff09;考试试卷&#xff0c;包含广东省安全员A证第四批&#xff08;主要负责人&am…

岗位实习最终篇(汇总)——人力资源管理系统(包含DDL,DML,视图,简单/复杂查询,触发器语句和存储过程语句)

DDL CREATE TABLE users (user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 员工ID,username VARCHAR(50) NOT NULL UNIQUE COMMENT 用户名,password VARCHAR(255) NOT NULL COMMENT 密码,first_name VARCHAR(50) NOT NULL COMMENT 名,last_name VARCHAR(50) NOT NULL COMMENT…

聊聊如何制定互联网产品测试策略

提起互联网产品测试&#xff0c;给人的第一感觉那就是一个字“快”&#xff0c;相比于传统行业的软件&#xff0c;更新周期快的一个多月一个版本&#xff0c;慢的半年或一年一个大版本&#xff0c;从测试的角度出发&#xff0c;制定产品的测试策略侧重点有所不一样&#xff0c;…

OVS:网桥的状态:fail_mode模式

目录 1.创建一个普通的ovs网桥不做任何配置 2.检测fail_mode值&#xff0c;默认为空 3.创建netns并配置sto网桥的两个普通端口并配置IP信息 4.默认情况下的两个端口下挂两个虚拟机v3,v4天然通信-ping-ok 5.修改网桥的fail_mode为standalone,原来的通信没有影响 6.修改了…