写点什么

基于 etcd 实现大规模服务治理应用实战

作者:百度Geek说
  • 2021 年 11 月 11 日
  • 本文字数:7463 字

    阅读完需:约 24 分钟

基于etcd实现大规模服务治理应用实战

导读:服务治理目前越来越被企业建设所重视,特别现在云原生,微服务等各种技术被更多的企业所应用,本文内容是百度小程序团队基于大模型服务治理实战经验的一些总结,同时结合当前较火的分布式开源 kv 产品 etcd,不仅会深入剖析 ectd 两大核心技术 Raft 与 boltdb 的实现原理,也会披露服务治理真实实践的经验,希望帮助大家在服务治理的道路上获得更多帮助。


全文 8243 字,预计阅读时间 21 分钟。

一、服务治理概念介绍

服务治理是 IT 治理的一部分,它关注服务生命周期中的相关要素,其重点环节包括服务注册和发现、服务平滑升级、流量监控、流量管控、故障定位、安全性等。


服务是需要"治理"的,但是治理是需要成本的,如果一个服务的业务逻辑简单,运行流程清晰,出现问题也能及时定位和回滚,那么该服务治理的成本可能非常低,甚至只需要人工处理就行,但是在复杂业务中,服务的提供者和服务的使用者可能分别运行在不同的进程中(甚至在不同的物理节点上),并由不同的团队开发和维护。团队协作和服务协同,都需要进行大量的协调工作。协调工作越多,复杂度越高,这样就有了服务治理的需求,通过建设统一的服务治理平台,就可以有有效的提升业务的服务治理能力,包括协同的规范化、实时监控,不断优化调用链路的效率,以及辅助降低依赖复杂度,规避风险等。在大型业务系统中,服务治理已经是技术架构中必不可缺的一部分,也是整个业务系统最重要的基础设施之一。



上图是网上流传的 netflix 的服务拓扑图,图中密密麻麻的白色小点就是 netflix 的服务节点,节点之间的连线表明服务之间有调用,节点和连线构成了复杂的服务调用链,如此庞大的应用系统必须要通过一个强力的服务治理平台来进行管理。


服务治理本质上是对服务生命周期的管控,因而服务治理平台的核心需求就是如何解决服务生命周期中的痛点问题,其包括以下几个方面:


1、注册和发现


服务调用方在调用服务之前必须要得到服务提供方的地址,也就是调用方需要通过一种方式“发现“服务提供方,这就是服务发现。而要完成服务发现,就需要将服务提供方的信息存储到某个载体,这个存储的动作即是”服务注册“,而存储的载体则称为”服务注册中心“。在任何一个服务治理平台中,"注册中心"是必不可少的一个模块。服务注册和发现是服务治理中最基础的功能,在服务生命周期中,它负责服务的初始环节。


2、流量监控


在服务注册发现之后,就是服务的调用,大量的服务调用,形成了流量。流量监控就是对众多服务间的调用关系、状态的清晰掌控。其主要包括了调用拓扑关系、调用追踪、日志、监控告警等,服务治理通过调用拓扑来整体监控服务调用关系,通过建立监控体系来快速发现、定位问题,从而在整体上感知业务系统的运行状况。在服务生命周期中,流量监控负责服务的运行态感知。


3、流量调度


在业务系统运行过程中,经常会有比如促销、秒杀、明星绯闻等热点问题,或者机房断网、断电、系统大范围升级等突发事件,带来业务系统中局部服务的流量突增突降,这样就需要对服务的流量进行调度和管理。流量管理包括两个方面:从微观的单个服务来说,就是服务调用过程的管理,包括在何时采用何种均衡负载策略、路由策略以及熔断限流策略,这些策略统称为调用策略;从宏观上来说,就是流量分发的管理,可以根据某些流量特征和流量占比进行灰度发布、蓝绿发布等,这些称为流量分发策略。服务调用策略、流量分发策略,都需要通过流量监控收集的调用数据进行分析,从而制定出决策,然后在服务治理平台上落地。流量调度负责服务的运行态管理。


4、服务控制


流量调度的策略如何在服务的提供方和调用方生效,可以重启生效,也可以在运行态实时生效,这就是看服务治理平台对服务的控制力度,服务治理平台在充分建设服务治理能力后,能实时把服务治理的策略向服务进行分发并立即生效。


5、服务安全


每个服务都承载自身的业务职责,一些业务敏感的服务,需要对其他服务的访问进行认证与鉴权,也就是安全问题。


本文把拥有成千上万的服务称之为大型应用系统,该系统的特征是大量的服务、大量的服务实例、以及海量的服务调用,服务治理平台在管理这类业务系统的服务时,需要面对以下巨大的挑战:


1.  高可靠性


大型业务系统,海量的服务调用,错综复杂的调用关系,对服务的可靠性要求很高,很多基层的服务都要求 99.99%的可靠性,因而维护这些服务的服务治理平台,其可靠性的要求也非常高,而要达到这么高的可靠性,服务治理平台本身也需要做到多级部署、多地热备、降级隔离、平滑上线等方案。


2.  高性能


在保证可靠性的前提下,服务治理还必须有很高的性能,比如在监控数据中,快速准确的感知到某个服务的出现了单点故障,从而能够将流量分发到该服务的其他进程上去。如果业务系统的服务数不多,调用量不高,那么监控数据量也不会很大,服务的单点故障很容易就能查到,但是在实时的海量调用数据中,一些常规的查询手段要花费大量的时间,等感知到单点故障时,可能已经造成了不可挽回的业务损失。所以性能是考量服务治理平台治理能力的一项重要指标,如何保证高性能,高速的存储、多级缓存、线性部署都是必不可少的。


3.  高扩展


高扩展包含两个方面:大型应用系统的服务,可能是由多个团队在开发运维,其水平和技术能力也是参差不齐的,因而服务治理平台需要提供兼容和扩展的能力,通过扩展性,尽可能的把不同的服务治理起来;同时,在业务系统服务量增长时,服务治理平台也应该具备同步扩展的能力,来保证其高可靠和高性能。


面对海量服务的治理挑战,服务治理平台也需要有一个强大好用的存储工具来应对,etcd 就是一个不错的选择。

二、etcd 介绍

2.1 etcd 发展背景与相关竞品介绍

2013 年 CoreOS 创业团队在构建一款开源,轻量级的操作系统 ContainerLinux 时,为了应对用户服务多副本之间协调的问题,自研开发的一款用于配置共享和服务发现的高可用 KV 分布式存储组件——ETCD。


下面我们也针对 Zookeeper 和 Consul 两个选型做了一下对比:


·       ZooKeeper


ZooKeeper 从高可用性,数据一致性,功能这三个方面而言是完全符合需求的,但 CoreOS 还是坚持自研 etcd 的原因总结有以下两点:


1.  ZooKeeper 不支持通过 API 安全地变更成员,需要人工修改节点配置并重启进程.如果操作有误,有可能导致脑裂等线上故障,同时 CoreOS 对适配云环境,集群规模的平滑调整,运行时配置的在线变更都是有期望目标的,这方面 ZooKeeper 的维护成本比较高。


  1. 高负载读写性能,ZooKeeper 在大规模的实例连接情况下性能表现并不佳。


etcd 名字是由“/etc”文件夹和”d”分布式系统组成。“/etc”文件夹是用来存储单系统配置数据的,而“etcd”用于存储大规模分布式系统的配置数据,etcd 集群可提供高稳定性,高可靠性,高伸缩性和高性能的分布式 KV 存储服务。etcd 是基于复制状态机实现的,由 Raft 一致性模块,日志模块,基于 boltdb 持久化存储的状态机组成,可应用于分布式系统的配置管理,服务发现,分布式一致性等等。


ZooKeeper 与 etcd 一样,可解决分布式系统一致性和元数据存储等问题,但是 etcd 相较于 ZooKeeper 有以下几点优势:


1.  动态集群成员关系重新配置


2.  高负载下稳定的读写能力


3.  多版本并发控制数据模型


4.  可靠的键监控


5.  Lease(租约)原语将连接和会话分离


6.  分布式锁保证 API 安全性


7.  ZooKeeper 使用自己的 RPC 协议,使用受限;而 etcd 客户端协议是基于 gRPC 的,可支持多种语言。


·       Consul


Consul 与 etcd 解决的是不同的问题,etcd 用于分布式一致性 KV 存储,而 Consul 侧重于端到端的服务发现,它提供了内置的健康检查,失败检测和 DNS 服务等等,另外 Consul 通过 RESTfulHTTPAPIs 提供 KV 存储能力.但是当 KV 使用量达到百万级时,会出现高延迟和内存压力等问题。


一致性算法方面,etcd、Consul 基于 Raft 算法实现数据复制,ZooKeeper 则是基于 Zab 算法实现。Raft 算法由 Leader 选举,日志同步,安全性组成,而 Zab 协议则由 Leader 选举、发现、同步、广播组成。


分布式 CAP 方面,etcd、Consul 和 ZooKeeper 都是 CP 系统,发生网络分区时,无法写入新数据。


下表是针对三者的关键能力做了一下对比分析:


2.2 etcd 核心技术介绍

基于 Raft 协议实现数据高可用和强一致性

早期数据存储服务引入多副本复制技术方案来解决单点问题,但是无论是主从复制还是去中性化复制,都存在一定的缺陷。主从复制运维困难,且一致性与可用性难以兼顾;去中心化复制,存在各种写入冲突问题需要业务处理。而分布式一致性算法,正是解决多副本复制存在问题的关键。分布式一致性算法,又称为共识算法,最早是基于复制状态机背景下提出来的。Paxos 作为第一个共识算法,过于复杂,不容易理解,难以在工程上落地。斯坦福大学的 Diego 提出的 Raft 算法,通过将问题拆解为三个子问题,易于理解,降低了工程落地难度。这三个子问题是:Leader 选举,日志复制,安全性。

Leader 选举

etcd(版本 3.4+)中 Raft 协议定义集群节点有 4 种状态:Leader、Follower、Candidate、PreCandidate。


正常情况下,Leader 节点会按照心跳间隔时间,定时广播心跳消息给 Follower 节点,以维持 Leader 身份。Follower 收到后回复心跳应答包消息给 Leader。Leader 都会带有一个任期号(term),任期表示从一次选举开始,赢得选举的节点在该任期内担当 Leader。任期号单调递增,在 Raft 算法中充当逻辑时钟,用于比较各个节点数据新旧,识别过期 Leader 等等。


当 Leader 节点异常时,Follower 节点会接收 Leader 的心跳消息超时,当超时时间大于竞选超时时间后,会进入 PreCandidate 状态,不自增任期号,仅发起预投票(民意调查,防止由于节点数据远远落后于其他节点而发起无效选举),获得大多数节点认可后,进入 Candidate 状态.进入 Candidate 状态的节点,会等待一个随机时间,然后发起选举流程,自增任期号,投票给自己,并向其他节点发送竞选投票信息。


当节点 B 收到节点 A 竞选消息后,有 2 种情况:


1.  节点 B 判断节点 A 的数据至少和自己一样新,节点 A 任期号大于节点 B 任期号,并且节点 B 未投票给其他候选者,即可投票给节点 A,节点 A 获得集群大多数节点支持,可成为新 Leader。


2.  如果节点 B 也发起了选举,并投票给自己,那么它将拒绝投票给节点 A。此时若没有节点可以得到大多数投票支持,则只能等待竞选超时,开启新一轮选举。


日志复制

Raft 日志结构如下图所示:



Raft 日志由有序索引的一个个条目组成,每个日志条目包含了任期号和提案内容.Leader 通过维护两个字段来追踪各个 Follower 的进度信息.一个是 NextIndex,表示 Leader 发送给该 Follower 节点的下一个日志条目索引;另一个是 MatchIndex,表示该 Follower 节点已复制的最大日志条目索引。


本文以 Client 提交“hello=world”提案,至接收到响应的整个流程为例,简单介绍 etcd 日志复制流程:


1.  当 Leader 接收到 Client 提交的提案信息后,生成日志条目,同时遍历各个 Follower 的日志进度,生成对各个 Follower 追加日志的 RPC 消息;


2.  通过网络模块将追加日志的 RPC 消息广播给各个 Follower;


3.  Follower 接收到追加日志消息并持久化之后,回复 Leader 已复制最大日志条目索引,即 MatchIndex;


4.  Leader 接收到 Follower 应答后,更新对应 Follower 的 MatchIndex;


5.  Leader 根据各个 Follower 提交的 MatchIndex 信息,计算出日志条目已提交索引位置,该位置代表日志条目被一半以上节点持久化;


6.  Leader 通过心跳告知各个 Follower 已提交日志索引位置;


7.  当 Client 的提案,被标识为已提交后,Leader 回复 Client 该提案通过。


通过以上流程,Leader 同步日志条目给各个 Follower,保证 etcd 集群的数据一致性。

安全性

etcd 通过给选举和日志复制增加了一系列规则,来保证 Raft 算法的安全性。


选举规则:


1.  一个任期号,只能有一个 Leader 被选举,Leader 选举需要集群一半以上节点支持;


2.  节点收到选举投票时,如果候选者最新日志条目的任期号小于自己,拒绝投票,任期号相同但是日志比自己短,同样拒绝投票。


日志复制规则:


1.  Leader 完全特性,如果某个日志条目在某个任期号中已被提交,则这个日志条目必然出现在更大任期号的所有 Leader 中;


2.  只附加原则,Leader 只能追加日志条目,不能删除已持久化的日志条目;


3.  日志匹配特性,Leader 发送日志追加信息时,会带上前一个日志条目的索引位置(用 P 表示)和任期号,Follower 接收到 Leader 的日志追加信息后,会校验索引位置 P 的任期号与 Leader 是否一致,一致才能追加。

boltdb 存储技术

ectd 的另一个核心技术是 boltdb 存储,提供高效的 b+树的检索能力,同时支持事务操作,他是支撑 etcd 高性能读写的关键能力之一。


boltdb 的实现参见了 LMDB(LightningMemory-MappedDatabase)设计思路,基于高效快速的内存映射数据库方案.基于 B+树的结构设计。数据文件设计上 bolt 使用一个单独的内存映射的文件,实现一个写入时拷贝的 B+树,这能让读取更快。而且,BoltDB 的载入时间很快,特别是在从 crash 恢复的时候,因为它不需要去通过读 log(其实它压根也没有)去找到上次成功的事务,它仅仅从两个 B+树的根节点读取 ID。

文件存储设计

由于采用了单文件映射存储,所以 bolt 对文件按指定长度进行分块处理,每块存储不同的内容类型。默认使用 4096 字节的长度进行分块。每一块的开头有单独的 pageid(int64)标识。


文件块的类型有以下几种:


数据文件全景结构


说明:


  • metapage 固定在 page0 与 page1 位置

  • Pagesize 大小固定在 4096 字节

  • bolt 的文件写入采用了本地序(小端序)的模式,比如 16 进制 0x0827(2087)写入的内容为 2708000000000000


单文件方案的优势就是不需要做文件的合并删除等操作,只需要在原文件上追加扩展长度就可以了。

查询设计

boltdb 提供了非常高效的查询能力,可以先看一下它的对象设计:



从对象设计上,boltdb 在加载时,会先 loadmeta 数据进内存,然后根据 bucket,来定位数据块所在的位置,然后再根据 key 的值,来定位 branchnode 的位置,然后定位到叶子值节点。


我们以查询为例,来讲解一下,下面是一个基本的查询示例代码:


tx, err := db.Begin(true) // 开启事务   if err != nil {       return   }   b := tx.Bucket([]byte("MyBucket")) // 根据名称查询bucket   v := b.Get([]byte("answer20")) // 根据key进行查询    fmt.Println(string(v))   tx.Commit()
复制代码


对应上面的代码,下面的序列图,可以更详细的了解一次查询的操作流程:



上面最关键的代码就是 search 方法,下面是主要的代码片断,已添加了注释说明方便阅读。


func (c *Cursor) search(key []byte, pgid pgid) {    p, n := c.bucket.pageNode(pgid)    if p != nil && (p.flags&(branchPageFlag|leafPageFlag)) == 0 {        panic(fmt.Sprintf("invalid page type: %d: %x", p.id, p.flags))    }    // 把当前查询节点(page,node)压入栈    e := elemRef{page: p, node: n}    c.stack = append(c.stack, e)     // If we're on a leaf page/node then find the specific node.    if e.isLeaf() {        c.nsearch(key)        return    }    // if node cached seach by node's inodes field    if n != nil {        c.searchNode(key, n)        return    }    // recursively to load branch page and call search child node again    c.searchPage(key, p)}
复制代码

三、百度基于 etcd 打造大规模服务治理建设思路

3.1 具体的挑战

天路是百度小程序团队开发打造的面向大型业务服务治理需求的一套解决方案,其目标之一就是打造成百度的服务治理规范样板。天路由注册中心、可视化管理平台、SDK 框架、统一网关、tianlu-mesher 五个部分组成,目前已经接入了 150+产品线,实例数已达数十万级别。随着接入平台的团队数增多、以及服务实例的快速增长,大量团队间如何轻松的协作以及实现大规模服务治理平台的高可用、高性能一直是天路持续面临的挑战。

3.2 整体架构建设思路与方案

天路作为一个服务治理平台,核心理念是为所有的服务提供便捷的调用,统一的服务监控管理,简化服务的开发和维护成本。我们从以下不同的方面思考基于 etcd 打造大规模服务治理平台:高可用、高性能、高扩展、易用性。


·       高可用


  • 考虑到 etcd 跨机房调用的高网络延时,我们采用单机房部署,同时我们也实现了主备集群切换的方案,解决在单机房实例全部宕机的情况下,etcd 集群不可用的问题。

  • 为了降低平台对 etcd 的强依赖,我们做了 etcd 降级使用缓存的方案。在监控到 etcd 集群的性能无法支持当前平台的时候,使用缓存存储实例数据,能够让运维人员在恢复 etcd 之前,系统不受影响正常运行;etcd 恢复之后,切换回 etcd 集群继续工作。


·       高性能


  • etcd 集群的 kv 查询性能很高,qps 能达到 10000 以上。为了解决在极端并发下的性能问题,注册中心采用多级缓存提升查询效率,降低对 etcd 的访问压力。

  • 服务间调用使用直连的方式,请求不需要经过注册中心进行转发,调用基本没有时间损耗。


·       高扩展


  • 考虑到将来服务实例数达到百万级别,我们需要考虑架构的高扩展性。


·       易用性


  • 用户通过可视化的管理平台可以查看已注册的服务,也可通过管理平台实时更新服务治理策略的配置,实时调整服务治理策略。

  • 将调用日志接入 trace 平台,用户可通过 traceId 在 trace 平台查到整个调用链的记录,便于出错时进行快速的问题定位。

  • 多语言 SDK,支持多种 rpc 技术,包括百度自研的 rpc 技术 brpc(https://github.com/baidu/Jprotobuf-rpc-socket【java/go sdk】)和 http jsonrpc 协议等


架构方案图

3.3 关键的指标与运维目标

此外针对更好的实施服务治理平台的运维,还需要以下的关键考核指标与运维要求。


关键指标:


·       可用性达 99.99 以上;


·       平响 100ms 以下。


运维目标:


1、故障发现早


·       配置监控告警,包括注册中心实例健康、etcd 平响、内存和 cpu 监控。


2、故障处理快


·       自动处理:通过 noah 的回调机制,自动处理一些故障,提高处理速度。


·       手动处理:值班机制。

四、总结

服务治理目前越来越被企业建设所重视,特别现在云原生,微服务等各种技术被更多的企业所应用,但是要真正在应用好,融合好,还是有非常多的挑战,除了一套成熟的服务治理产品外,包括团队整体对服务治理的认知,技术经验的深淀,遵循服务化的设计能力水平的能力等,都会影响到最终的实施效果。本文也仅在服务治理产品选型上给大家一些启发,希望在服务治理的道路上帮大家走得更好更稳。


推荐阅读:


|百度爱番番数据分析体系的架构与实践


|托管页前端异常监控与治理实战


|Flux架构思想在度咔App中的实践


---------- END ----------


百度 Geek 说


百度官方技术公众号上线啦!


技术干货 · 行业资讯 · 线上沙龙 · 行业大会


招聘信息 · 内推信息 · 技术书籍 · 百度周边


欢迎各位同学关注

发布于: 2021 年 11 月 11 日阅读数: 21
用户头像

百度Geek说

关注

百度官方技术账号 2021.01.22 加入

关注我们,带你了解更多百度技术干货。

评论

发布
暂无评论
基于etcd实现大规模服务治理应用实战