使用 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 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux——移动文件或目录,查找文件,which命令

移动文件或目录 作用 - mv命令用于剪切或重命名文件 格式 bash mv [选项] 源文件名称 目标文件名称 注意 - 剪切操作不同于复制操作&#xff0c;因为它会把源文件删除掉&#xff0c;只保留剪切后的文件。 - 如果在同一个目录中将某个文件剪切后还粘贴到当前目录下&#xff0c;…

onnx模型转rknn到部署

简介 最近开始用3568的板子&#xff0c;之前是在用3399&#xff0c;cpu的话3399比3568强&#xff0c;但是3568有1T的npu算力&#xff0c;所以模型移植过来用npu使用&#xff0c;之前用ncnn感觉太慢了&#xff0c;rk的npu使用没有开源&#xff0c;所以没法兼容&#xff0c;只能跑…

基于pycharm对每个工程配置python环境

目录 1 生成环境2 配置pycharm 1 生成环境 设定一个存放虚拟环境的目录&#xff0c;比如可以放在如下目录下&#xff1a; /Users/Name/PycharmProjects/env 然后生成虚拟环境&#xff0c;执行如下操作&#xff1a; python3 -m venv /Users/Name/PycharmProjects/env/agent_pr…

本周波动预警!7月将一路上涨,牛市“复苏“?低于6万美元的比特币,是熊市陷阱吗?

比特币在第三季度伊始发出了一些积极信号。随着上周末的涨势&#xff0c;BTC/USD最高一度达到63818美元&#xff0c;这让人对比特币能否重拾牛市信心满怀希望。不过&#xff0c;在冲破关键阻力位64000美元之前&#xff0c;市场参与者仍保持谨慎态度。比特币要想维系开头的牛市态…

AI系统:未来科技的驱动力

引言 人工智能&#xff08;Artificial Intelligence, AI&#xff09;是一门研究如何使计算机模拟、延伸和扩展人类智能的学科。自20世纪50年代起&#xff0c;人工智能作为一项科学研究领域开始兴起。早期的AI系统主要集中在简单的任务&#xff0c;如棋类游戏和数学证明。随着计…

KUKA机器人中断编程2—中断相关的指令

在进行中断编程时&#xff0c;涉及到多个指令&#xff0c;包括:DECL、ON、OFF、GLOBAL、BRAKE、RESUME等。 1、中断声明 事件和子程序通过INTERRUPT DECL ... WHEN .. DO .. 来定义。 语法:INTERRUPT DECL Prio WHEN 事件 DO 中断程序 例如:INTERRUPT DECL 19 WHEN $IN[1]TRU…

锁相环相位噪声仿真代码-汇总

24小时自动发货 所设计的压控振荡器输入电压为0.625V时&#xff0c;输出大致为500Mhz&#xff1b;输入电压为1.559时&#xff0c;输出电压大致为1Ghz 1.文件夹里面各个文件作用&#xff08;包括参考书PLL PHASE NOISE ANALYSIS、lee的射频微电子、以及前人留下的matlab文件还有…

MATLAB-振动问题:单自由度阻尼振动系统受迫振动

一、基本理论 二、MATLAB实现 单自由度阻尼振动系统受迫振动&#xff0c;MATLAB代码如下&#xff1a; clear; clc; close allA 1; psi 0; F0 10; D 20; Rm 0.5; M 1; omega 2; delta Rm / (2*M); omega0 sqrt(D / M); Omega sqrt(omega0^2 - delta^2); Zm Rm i *…

经典文献阅读之--iDet3D(交互式3D目标检测器)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务&#xff0c;并且需要GPU资源&#xff0c;可以考虑使用UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&…

Kafka基本原理详解

&#xff08;一&#xff09;概念理解 Apache Kafka是一种开源的分布式流处理平台&#xff0c;专为高性能、高吞吐量的实时数据处理而设计。它最初由LinkedIn公司开发&#xff0c;旨在解决其网站活动中产生的大量实时数据处理和传输问题&#xff0c;后来于2011年开源&#xff0…

2024年7月1日 (周一) 叶子游戏新闻

老板键工具来唤去: 它可以为常用程序自定义快捷键&#xff0c;实现一键唤起、一键隐藏的 Windows 工具&#xff0c;并且支持窗口动态绑定快捷键&#xff08;无需设置自动实现&#xff09;。 喜马拉雅下载工具: 字面意思 《星刃》早期概念图分享 末世破败环境推主Genki分享了《星…

ROS2在rviz2中实时显示轨迹和点

本文是将《ROS在rviz中实时显示轨迹和点》博客中rviz轨迹显示转为ROS2环境中的rviz2显示。 ros2的工作空间创建这里就不展示了。 包的创建 ros2 pkg create --build-type ament_cmake showpath --dependencies rclcpp nav_msgs geometry_msgs tf2_geometry_msgsshowpath.cpp…

公网环境使用Potplayer远程访问家中群晖NAS搭建的WebDAV听歌看电影

文章目录 前言1 使用环境要求&#xff1a;2 配置webdav3 测试局域网使用potplayer访问webdav4 内网穿透&#xff0c;映射至公网5 使用固定地址在potplayer访问webdav 前言 本文主要介绍如何在Windows设备使用potplayer播放器远程访问本地局域网的群晖NAS中的影视资源&#xff…

dedecms无法采集怎么办

dedecms无法采集解决方法&#xff1a;1、检查是否设置了正确的采集规则和路径&#xff0c;确保dedecms能够正常访问目标网站&#xff0c;并且有足够的权限进行采集操作&#xff1b;2、尝试使用代理服务器进行采集&#xff0c;或者通过破解验证码的方式绕过网站的反爬虫机制&…

Vue3学习笔记<->创建第一个vue项目(2)

新建一个项目目录 找一个盘新建一个目录&#xff0c;我这里在D盘创建一个vuedemo目录作为项目存放的目录。使用idea打开目录。   单击ieda底部的按钮“Terminal”&#xff0c;打开命令行窗口&#xff0c;如果命令行窗口当前目录不是“vuedemo”&#xff0c;就切换到“vuedem…

2024第17届中国西部(重庆)留学移民海外置业展览会

2024第17届中国西部&#xff08;重庆&#xff09;留学移民海外置业展览会 邀请函 主办单位&#xff1a; 中国西部教体医融合博览会组委会 承办单位&#xff1a;重庆中博展览有限公司 展会背景&#xff1a; 成都和重庆是中国新一线城市&#xff0c;是西部经济的核心增长极&a…

OpenStack开源虚拟化平台(一)

目录 一、OpenStack背景介绍&#xff08;一&#xff09;OpenStack是什么&#xff08;二&#xff09;OpenStack的主要服务 二、计算服务Nova&#xff08;一&#xff09;Nova组件介绍&#xff08;二&#xff09;Libvirt简介&#xff08;三&#xff09;Nova中的RabbitMQ解析 OpenS…

KUKA机器人不同运行方式

KUKA机器人有以下四种运行方式&#xff1a; 1、手动慢速运行&#xff08;T1&#xff09; 2、手动快速运行&#xff08;T2&#xff09; 3、自动运行&#xff08;AUT&#xff09; 4、外部自动运行&#xff08;AUT EXT&#xff09; 将示教器上的钥匙向右旋转&#xff0c;就会…

ruoyi—cloud 新建模块+生成代码

1.复制一个模块——修改名字 2.打开模块下的yml文件&#xff0c;修改端口号和名字 &#xff08;1&#xff09;修改一个名字 &#xff08;2&#xff09;打开yml文件 &#xff08;3&#xff09;修改端口号&#xff0c;不要重复 &#xff08;4&#xff09;改名字和模块一致 3.…

arm_uart4实验

#include "uart4.h" //UART //初始化 void hal_uart4_init() { //rcc_init //…