瓜农老梁

一个想分享点干货的家伙,微信公众号「瓜农老梁」

0%

引言

通过对Nacos配置中心源码阅读,将其核心原理归纳提炼。包含:客户端逻辑和服务端逻辑。

配置中心客户端逻辑

1.客户端流程概览

客户端整体流程可以进一步简化为:

  • 客户端通过长轮询的方式比较配置内容md5变更

  • 长轮询通过从阻塞队列不断获取元素判断是否立即执行

  • 阻塞队列无元素等待5秒执行

2.Listener注册逻辑

客户端Listener注册逻辑可以进一步简化为:

  • 客户端缓存了CacheData
  • 阻塞队列中添加了元素new Object()

3.配置变更检测逻辑

客户端长轮询逻辑,可以进一步简化为:

  • 客户端收到服务端推送的变更事件后发起MD5校验
  • 客户端主动向服务端发起MD5校验

4.阻塞队列添加时机

阅读全文 »

引言

在上文分析中客户端会有长轮询,不断轮询阻塞队列「listenExecutebell」去比较客户端和服务端配置内容md5是否一致,不一致则通知我们注册的Listener完成回调。当阻塞队里有元素时会立即执行,没有元素等待5秒钟执行。那都在什么时候往队列中添加元素的从而触发立即执行。

内容提要

长轮询回顾

  • 监听配置内容的变更通过长轮询实现
  • 具体实现为轮询阻塞队列

阻塞队列添加时机

  • 客户端添加Listener时添加
  • 客户端删除Listener时添加
  • 服务端通知内容变更时添加
  • 建立gRPC连接时添加

服务端变更发布流程

  • 通过配置中心发起变更请求
  • 配置变更内容被写入数据库
  • 向本节点连接的Client发送变更通知
  • 向集群中的其他节点发送变更通知

向Client发送变更通知

  • 首先构建DumpTask
  • 处理DumpTask时发布ConfigDumpEvent事件
  • 处理ConfigDumpEvent时发布LocalDataChangeEvent事件
  • 处理LocalDataChangeEvent事件:@1 先从缓存中拿出注册Client列表 @2 根据client从缓存获取gRPC连接 @3 构建ConfigChangeNotifyRequest @4 通过gRPC向客户端发送变更通知
  • 客户端处理变更通知,客户端接到请求后向阻塞队列添加new Object元素,详细客户端的长轮询流程见上篇

向其他节点发送变更通知

  • 通过gRPC向集群中其他节点发送变更通知
  • 集群节点收到变更通知后再下发给连接自己的Client
阅读全文 »

1
2
3
4
title: Mesh8# Envoy原理提点与常用命令
categories: Mesh
tags: Mesh
date: 2021-12-12 11:55:01

基本概念

Istio的核心组件,作为sideCar与应用部署在一个Pod中,作为代理流量的进出均需经过Envoy所在的容器,除了代理外还可根据规则进行流量治理、监控等功能。

Upstream Host: 上游主机,接受envoy的连接和请求并返回响应

Downstream Host: 下游主机,向envoy发起请求并接受响应

Enovy Mesh: 由一组Envoy组成的拓扑网络

**Listener: ** 监听器负责监听数据端口,接受下游的连接和请求,下游主机通过Listener连接Envoy

**Cluster: ** 集群管理后端服务服务的连接池、服务的健康检查、服务熔断等

Filter: 支持多种过滤器Listener Filter、Network Filter、L7 Filter等,组成filter链条,执行不同的流量治理逻辑。

协议支持:

L3/L4网络代理,支持TCP、HTTP代理和TLS认证

L7代理,支持Buffer、限流等高级功能

L7路由,支持通过路径、权限、请求内容、运行时间等参数重定向路由请求

在HTTP模式下支持HTTP1.1和HTTP/2,同时支持基于HTTP/2的gRPC

线程模型

一个Envoy进程包括一个Server主线程和一个GuardDog守护线程

Server主线程:负责管理Access Log以及解析上游主机的DNS。Access Log根据配置信息访问来处理Enovy访问记录,DNS解析将统一配置的域名解析成IP并缓存在本地DNS缓存中。

一个Envoy进程可以配置多个Listener,推荐配置一个,每个Listener可创建若干线程默认为核数,每个线程对应一个Worker。

一旦某个客户端连接进入Envoy中的某个线程,则连接断开之前的逻辑都在该线程内处理。例如:处理Client请求对应的TCP filter,解析协议和重新编码,与上游主机建立连接并处理返回数据等。

内存管理

内存管理分为变量管理和Buffer管理:

  • 变量管理:C++运行过程中创建的实例
  • Buffer管理:数据接收、编解码等过程中临时存储数据的Buffer,通过malloc分配

流量控制

  • 如果上游主机处理过慢会在buffer积压,通过设置上下水位的阈值来控制
  • 通过Envoy设置全局连接数来限制

主要模块

  • Network模块 抽象Socket提供读写功能
  • Network Filter模块 过滤数据流量Listener Filter、Read Filter、Write Filter
  • L7 protocol模块包含HTTP、HTTP/2、gRPC
  • L7 filters模块与HTTP相关的认证、限流、路由等
  • Server Manager模块管理Worker管理、启动管理、配置管理和日志等
  • L7 Connection Manager模块包括建立连接、复用连接等功能
  • Cluster Manger模块集群管理模块包括hosts管理、负载均衡、健康检查等
阅读全文 »

1
2
3
4
title: Mesh1# istio安装与部署
categories: ServiceMesh
tags: ServiceMesh
date: 2021-09-21 11:55:01

引言

Istio作为service mesh控制面的实施标准,先部署起来。然而会有一个坑要注意,否则无法访问到页面。这个坑是个示例的bug,已被人提了issue,我也被坑了一把。

一、准备工作

1.安装Docker

通过命令行或者直接下载,由于网络原因我直接下载安装 ,下载地址:

1
https://hub.docker.com/editions/community/docker-ce-desktop-mac

2.驱动安装

1
curl -LO https://storage.googleapis.com/minikube/releases/latest/docker-machine-driver-hyperkit
1
chmod +x docker-machine-driver-hyperkit
1
sudo mv docker-machine-driver-hyperkit /usr/local/bin/
1
sudo chown root:wheel /usr/local/bin/docker-machine-driver-hyperkit
1
sudo chmod u+s /usr/local/bin/docker-machine-driver-hyperkit

3.安装minikube

1
curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/

验证版本

1
2
$ minikube version
minikube version: v1.22.0
阅读全文 »

1
2
3
4
title: Mesh2# 第三方注册中心集成istio
categories: Mesh
tags: Mesh
date: 2021-09-21 11:55:01

引言

公司往往有自己的注册中心,有的使用Nacos、zookeeper等,还有自研的。这些在istio体系外的注册中心需要融入网格体系,让注册中心以及配置中心事件通知到istio,进而通过istio下发到数据面去。

第三方注册中心集成到istio通常有三种做法:

  • 方式一 修改源码实现serviceregistry.Instance接口适配到service controller

  • 方式二 通过自定义MCP Server,例如:Nacos提供了这部分实现

  • 方式三 将第三方注册中心事件封装成istio的ServiceEntry和WorkloadEntry资源写入Kubernetes的api server

    istiod收到监听后完成转换

方式一需要修改istio源码,重度耦合后续升级istio比较困难;方式二看着比较简单却调试比较困难给开发造成障碍;方式三从业界实践来看采用的最为广泛。

本文将分析第三种方式如何集成istio的,在此之前需要先走查下kubernetes的大体架构。

一、k8s架构简述

架构与概念

kube-apiserver: 与Kubernetes资源交互的入口,可以通过kubectl或者client-go其他语言类库进行访问

kube-scheduler: 负责资源调度与计算,将Pod按照特定策略分发到计算节点

etcd: 键值存储数据库,保存Kubernetes集群相关数据

kube-controller-manager: 运行一系列列控制器的组件,比如:节点控制器、任务控制器、端点控制器等

kubelete: 运行在计算节点中,通过监听控制面接受指令,在节点内执行操作

kube-proxy: 运行在计算节点的网络代理,负责Pod内外的网络通信代理

Pods创建流程

  • 通过kubectl或者client-go类库向kube-apiserver发起创建请求
  • kube-apiserver将请求信息持久化在etcd数据库
  • kube-scheduler检测到请求后,计算Pod应该分配到哪个Node,并将分配策略写入etcd数据库
  • Kubelet检测到etcd的分配策略后,执行该策略调用docker相关api创建container

二、第三方注册中心集成

架构图

转换流程:

从注册中心(Zookeeper)获取变更事件,将其转换为ServiceEntry写入kube-apiserver;Istiod(Pilot)通过监听kube-apiserver收到ServiceEntry后经过转换通过xDS下发给数据面。

阅读全文 »

1
2
3
4
title: Mesh7# wasm扩展Envoy使用详解
categories: Mesh
tags: Mesh
date: 2021-11-07 11:55:01

一、wasm工作原理

我们想要网格的服务发现、路由、熔断降级、负载均衡,这些流量治理都在数据面Envoy中执行才行。Envoy也提供的Filter机制来做这些功能,通常有以下方式:

  • 通过C++代码自定义filter重新编译Envoy
  • 使用Lua脚本扩展filter
  • 使用wasm扩展Envoy

第一种C++编译学习成本过高,维护也困难,第二种适合用于实现简单功能可以,第三种是重点发展方向通过其他语言编写filter,通过wasm编译运行嵌入在Envoy中运行,通过可移植的二进制指令实现,以下特性:

  • 动态加载到Envoy中执行
  • 无需修改Envoy代码容易维护
  • 支持较多开发语言比如tinygo
  • 进程级隔离在VM沙箱运行部影响Envoy进程

流量进过Envoy示意图:

阅读全文 »

1
2
3
4
title: Mesh3# Envoy代理转发与xDS映射关系
categories: Mesh
tags: Mesh
date: 2021-10-15 11:55:01

引言

Envoy作为Istio默认数据面代理,它的工作流程是怎么样的?本文通过示例运行,走查其运行流程,以及xDS协议映射。

xDS

xDS 协议是“X Discovery Service”的简写,这里的“X”表示它不是指具体的某个协议,是一组基于不同数据源的服务发现协议的总称,包括 CDS、LDS、EDS、RDS等。在Istio架构中,基于xDS协议提供了标准的控制面规范,并以此向数据面传递服务信息和治理规则。在Envoy中,xDS被称为数据平面 API,并且担任控制平面Pilot和数据平面Envoy的通信协议。

CDS 是 Cluster Discovery Service的缩写,Envoy使用它在进行路由的时候发现上游Cluster。Envoy通常会优雅地添加、更新和删除 Cluster。有了 CDS 协议,Envoy在初次启动的时候不一定要感知拓扑里所有的上游Cluster。在做路由 HTTP 请求的时候通过在 HTTP 请求头里添加 Cluster信息实现请求转发。

EDS 即Endpoint Discovery Service 的缩写。在Envoy术语中,Endpoint即Cluster的成员。Envoy 通过 EDS API可以更加智能地动态获取上游Endpoint。

LDS 即Listener Discovery Service的缩写。基于此,Envoy 可以在运行时发现所有的Listener,包括 L3 和 L4 filter 等所有的 filter 栈,并由此执行各种代理工作,如认证、TCP 代理和 HTTP 代理等。添加 LDS 使得 Envoy 的任何配置都可以动态执行。

RDS 即 Router Discovery Service 的缩写,用于 Envoy 在运行时为 HTTP 连接管理 filter 获取完整的路由配置,比如 HTTP 头部修改等。并且路由配置会被优雅地写入而无需影响已有的请求。当 RDS 和 EDS、CDS 共同使用时,可以帮助构建一个复杂的路由拓扑蓝绿发布等。

ADS EDS,CDS 等每个独立的服务都对应了不同的 gRPC 服务名称。对于需要控制不同类型资源抵达 Envoy 顺序的需求,可以使用聚合发现服务,即 Aggregated xDS,它可以通过单一的 gRPC 服务流支持所有的资源类型,借助于有序的配置分发,从而解决资源更新顺序的问题。

1
备注:上述概念摘自 https://www.servicemesher.com/istio-handbook/ecosystem/xds.html
阅读全文 »

1
2
3
4
title: Mesh5# Istio服务模型与流量治理要点
categories: Mesh
tags: Mesh
date: 2021-10-16 11:55:01

Istio服务模型

服务(Service)与版本(Version):Istio中的服务在kubernetes中以service形式存在,可定义不同的服务版本。通过Deployment创建工作负载,通过Service关联这些负载,域名或者虚拟IP访问后端Pod。

服务实例(ServiceInstance): 一个服务可以包含一组实例,在Kubernetes中用Endpoints实现,一组域名或者IP地址。

服务(Service)示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
name: helloworld
labels:
app: helloworld
service: helloworld
spec:
ports:
- port: 5000
name: http
selector:
app: helloworld

备注:创建一个名称为helloworld的Service,指向“app: helloworld”的Pods,Kubernetes会自动创建一个和Service同名的Endpoints对象,Selector会持续跟踪映射属于helloworld的Pods。

工作负载示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-v1
labels:
app: helloworld
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: helloworld
version: v1
template:
metadata:
labels:
app: helloworld
version: v1
spec:
containers:
- name: helloworld
image: docker.io/istio/examples-helloworld-v1
resources:
requests:
cpu: "100m"
imagePullPolicy: IfNotPresent #Always
ports:
- containerPort: 5000

备注:以Deployment方式创建工作负载,关联的版本与镜像。

阅读全文 »

现象反馈

业务同学反馈有个服务在部署容器后不间断收到积压告警,该服务对积压敏感,影响派单的时效性。原来部署到ECS上的服务没有积压情况,准备往容器迁移。下面是业务同学做的排除测试,另外容器当前在J/K可用区部署,而MQ集群部署在B/G/F区。

  • 回退到原ECS部署积压消失
  • 在原可用区申请扩容ECS未出现积压
  • 在新的可用区J/K申请ECS出现积压

备注: 很明显该积压与可用区有关系。

阅读全文 »

引言

Nacos注册中心的主要流程基本上撸完了,下面开始撸配置中心。本文从示例入手走查了客户端的初始化流程,Listener的注册逻辑和执行逻辑。

内容提要

示例

  • 通过示例构建ConfigService、注册了Listener分析其流程

Client初始化概览

  • 支持多种获取server地址方式(本地、endpoint)
  • 支持多种namespace设置(本地、阿里云)
  • 支持超时时间、重试时间等参数设置
  • 支持用户名和密码验证
  • 长轮询会从BlockingQueue中获取元素,队列有元素立即执行executeConfigListen,队列无元素阻塞5秒钟执行executeConfigListen()

Listener注册逻辑

  • client添加Listener后会在cacheMap中缓存CacheData
  • cacheMap中key由「dataId+group+tenant」拼接而成
  • 每个CacheData会绑定注册的Listener列表
  • 每个CacheData会绑定taskId,3000个不同的CacheData对应一个taskId
  • 设置isSyncWithServer=false表示 cache md5 data不是来自server同步
  • BlockingQueue中添加new Object() 供长轮询判断立即执行使用

配置变更执行逻辑

  • 执行逻辑由executeConfigListen方法实现
  • 当CacheData从Server同步后,会校验md5是否变更了,变更则回调注册的Listener完成通知
  • 注册Listener后会构建与server的RPC通道rpcClient
  • 向server发起变更查询请求configChangeListenRequest
  • Server端通过比较缓存的md5值,返回client变更的key列表
  • Client通过变更的key列表向server发起配置查询请求ConfigQueryRequest
  • 获取变更内容,并回调注册的Listener完成通知
  • 回调注册的Listener是通过线程池异步执行Runnble Job实现的
阅读全文 »