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

news2024/11/23 9:12:41

写在前面

阅读本文需要最起码了解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 main

import (
	"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 server
	gs := 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-id
  cluster: xds-node-cluster

dynamic_resources:
  ads_config:
    api_type: GRPC
    grpc_services:
    - envoy_grpc:
        cluster_name: xds_cluster
  cds_config:
    ads: {}
  lds_config:
    ads: {}

static_resources:
  clusters:
  - name: xds_cluster
    connect_timeout: 1s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    http2_protocol_options: {}
    load_assignment:
      cluster_name: xds_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 18000

admin:
  access_log_path: /dev/null
  address:
    socket_address:
      address: 0.0.0.0
      port_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  Current
                                 Dload  Upload   Total   Spent    Left  Speed
 19 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.coloradmin.cn/o/1869375.html

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

相关文章

安宝特方案 | AR术者培养:AR眼镜如何帮助医生从“看”到“做”?

每一种新药品的上市都需要通过大量的临床试验&#xff0c;而每一种新的手术工具在普及使用之前也需要经过反复的实践和验证。医疗器械公司都面临着这样的挑战&#xff1a;如何促使保守谨慎的医生从仅仅观察新工具在手术中的应用&#xff0c;转变为在实际手术中实操这项工具。安…

简化收支记录,只留关键日期! 一键掌握财务流动,高效管理您的每一笔收支

在繁忙的生活中&#xff0c;管理个人或家庭的财务收支变得尤为重要。然而&#xff0c;传统的记账方式往往繁琐且复杂&#xff0c;让人望而却步。今天&#xff0c;我们为您推荐一款简洁易用的记账神器——晨曦记账本&#xff0c;让您轻松记录收支&#xff0c;只显示日期&#xf…

三种三相交流电动机正反转互锁电路的分析

PLC和固态继电器应用都很普及了&#xff0c;常规电磁继电器还有用武之地吗?答案是&#xff1a;有用武之地的。因为微处理器的应用使逻辑控制发生了变革&#xff0c;极大地发挥了开关功能的特性&#xff0c;但在应用中&#xff0c;它还是无法承受较大的负载&#xff0c;因此还要…

PyTorch的环境配置和安装

PyTorch环境配置及安装 初步机器学习&#xff0c;这里记录下一些学习经过&#xff0c;之后以便于自己查看&#xff0c;同时欢迎各位大佬点评&#xff0c;本节是机器计算的一个包的安装和简单验证。 安装、使用环境 Windows环境下&#xff1a;CUDA官网使用IDM下载就很快乐&am…

3 滑动窗口

滑动窗口是一种常用的数据结构和算法思想&#xff0c;广泛应用于处理数组或序列中的连续片段问题。它的核心特点是窗口的大小可以动态调整&#xff0c;但总保持一个固定大小&#xff0c;通过在序列上“滑动”来检查不同的子序列。以下是滑动窗口的一些典型应用场景&#xff1a;…

帝国cms未审核文章可视化预览效果

有时候为了让编辑更加清楚的看到别人审核之后的效果&#xff0c;同时文章有需要下一级审核才能在前端展示出来&#xff0c;今天就来展示一个未审核文章预览审核后的效果 这次给某出版社开发的时候&#xff0c;他们需要实现编辑能够预览自己发布之后的审核效果&#xff0c;所以就…

想学gis开发,java和c++那个比较好?

ava与C的应用场景不同&#xff0c;究竟选择谁&#xff0c;应该由开发者的兴趣方向来决定。 你选择Java&#xff0c;意味着以后的业务方向就是偏后台服务开发&#xff0c;如果你非得说我用java也可以写界面&#xff0c;对不起&#xff0c;别人不会。 刚好我有一些资料&#xf…

从源码到上线:直播带货系统与短视频商城APP开发全流程

很多人问小编&#xff0c;一个完整的直播带货系统和短视频商城APP是如何从源码开发到最终上线的呢&#xff1f;今天&#xff0c;笔者将详细介绍这一全过程。 一、需求分析与规划 1.市场调研与需求分析&#xff1a;首先需要进行市场调研&#xff0c;了解当前市场的需求和竞争情…

移动端 UI 风格,书写华丽篇章

移动端 UI 风格&#xff0c;书写华丽篇章

电阻代码的谐音助记口诀

整理电子信息的课设&#xff0c;发现当时的笔记&#xff0c;记录一下&#xff0c;时间过得真快啊。 01234黑棕红橙黄 56789绿蓝紫灰白 银色和金色代表误差&#xff0c; 银色百分之十 金色百分之五 可以这么理解&#xff0c;运动会奖牌&#xff0c;金牌比银牌等级高&#xff…

简过网:考公务员报班和不报班的区别大吗?

备考公务员&#xff0c;究竟是报班还是不报班呢&#xff1f;一篇文章让你看看两者之间的区别&#xff01; 报不报班&#xff0c;其实这是很多考生都会纠结的地方&#xff0c;其实小编还是建议报个班的&#xff0c;这不仅仅是因为我是做这个行业的&#xff0c;更是因为这么长时…

用Microsoft.Extensions.Hosting 管理WPF项目.

首先引入必要的包: <ItemGroup><PackageReference Include"CommunityToolkit.Mvvm" Version"8.2.2" /><PackageReference Include"Microsoft.Extensions.Hosting" Version"8.0.0" /><PackageReference Include&q…

工商银行:低息差下的挣扎

时隔四年&#xff0c;市值再度超越贵州茅台成为A股“股王”。 今天要说的就是“宇宙行”——中国工商银行 虽然茅台的信仰开始崩塌&#xff0c;但各大银行股巨头们今年也不好过。2024年一季度六大行业绩集体受挫&#xff0c;息差普遍收窄超过20个基点。其中&#xff0c;包括工…

grpc学习golang版(六、服务器流式传输)

系列文章目录 第一章 grpc基本概念与安装 第二章 grpc入门示例 第三章 proto文件数据类型 第四章 多服务示例 第五章 多proto文件示例 第六章 服务器流式传输 文章目录 一、前言二、定义proto文件三、拷贝任意文件进项目四、编写server服务端五、编写client客户端六、测试六、示…

LLM RAG with Agent

题意&#xff1a;基于代理的LLM检索增强生成 问题背景&#xff1a; I was trying the application code in the link. 我正在尝试链接中的应用程序代码。 I am using the following Llang Chain version 我正在使用以下Llang Chain版本 langchain 0.0.327 langchain-commun…

UE学习笔记--UE项目,IDE提示项目被卸载的解决方案

前言 我用的 IDE 是 Rider。 我不小心把 Intertmediate 文件夹给删掉了。 然后进入 Rider&#xff0c;报了一些错&#xff0c;然后编译也有问题。启动不了 UE。 解决办法 右键你的 uproject&#xff0c;点击 Generate visual studio project files。 让它重新生成对应的文件…

Windows11+CUDA12.0+RTX4090如何配置安装Tensorflow2-GPU环境?

1 引言 电脑配置 Windows 11 cuda 12.0 RTX4090 由于tensorflow2官网已经不支持cuda11以上的版本了&#xff0c;配置cuda和tensorflow可以通过以下步骤配置实现。 2 步骤 &#xff08;1&#xff09;创建conda环境并安装cuda和cudnn&#xff0c;以及安装tensorflow2.10 con…

ManageEngine连续荣登Gartner 2024年安全信息和事件管理魔力象限

我们很高兴地宣布&#xff0c;ManageEngine再次在Gartner的安全信息和事件管理&#xff08;SIEM&#xff09;魔力象限中榜上有名&#xff0c;这是我们连续第七年获得这一认可。 Gartner ManageEngine Log360是一款全面的SIEM解决方案&#xff0c;旨在帮助组织有效处理日志数据…

电机驱动知识点总结

文章目录 一、直流电机入门基础知识1.直流电机原理2.减速器3.电机实物接线图解 二、TB6612 模块介绍1.D103A 模块说明2.D153B 模块说明3.D24A 模块说明 三、原理图说明1.TB6612 芯片原理图介绍与控制说明2.D103A 模块原理图介绍3.D153B 原理图介 四、电机驱动问题排查和测试方法…

如何加密电脑文件夹?安全可靠的文件夹加密工具分享

如何加密电脑文件夹呢&#xff1f;很多办公的小伙伴都有这样的疑问。想要保护自己的电脑文件夹数据的安全&#xff0c;但又不知道如何下手。这时候就需要借助电脑文件加密工具进行文件安全防护了。 本文将详细讲解电脑文件夹加密的方法&#xff0c;推荐几款款安全可靠的文件夹加…