Kubernetes核心组件-ETCD详解

Posted 巨子嘉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kubernetes核心组件-ETCD详解相关的知识,希望对你有一定的参考价值。

1.Kubernetes核心组件-ETCD详解

Kubernetes是典型的主从分布式架构,由集中式管理节点(Master Node),分布式的工作节点(Worker Node)组成以及辅助工具组成。其中ETCD是管理节点的核心组件,主要负责集群状态集中式存储,功能架构与Zookeeper类似。本篇主要是详细讲解ETCD架构及核心技术。


1.1.ETCD发展与演进

ETCD是用Go语言编写,通过Raft一致性算法,实现的一个高可用的分布式键值(key-value)数据库,核心里程碑如下:

2013年6月,由CoreOS团队于发布的开源。

2014年6月,作为Kubernetes核心元数据的存储服务一起发布,自此ETCD社区得到飞速发展。

2015年2月,发布了第一个正式稳定版本2.0,重构了Raft一致性算法,提供了用户树形数据视图,支持每秒超过1000次的写入性能。

2017年1月,发布了3.1版本,提供了一套全新的API,同时提供了gRPC接口,通过gRPC的proxy扩展并极大地提高ETCD的读取性能,支持每秒超过10000次的写入。

2018年11月,项目进入CNCF的孵化项目。

2019年8月,发布了3.4版本,该版本由Google、Alibaba等公司联合打造,进一步改进etcd的性能及稳定性。

2021年7月,发布了3.5版本,支持Go Module版本号语义及模块化,提升了性能及稳定性,增强了集群运维能力。

ETCD经过长时间的持续演进已趋于成熟,性能及稳定顶得到了极大的提升。当前针对kubernetes集群中List Pod等expensive request导致OOM等不稳定的问题,以及Range Stream,QoS特性,计划在ETCD 3.6版本实现,拭目以待。


1.2.ETCD架构及功能

用户读请求会经由HTTP Server,转发给Store进行具体的事务处理;如果涉及到节点的修改请求,则交给Raft模块进行状态的变更,日志记录,同步提交给集群其他节点并等待确认,最后完成数据的提交并再次同步。ETCD整体架构主要分为四个模块:

HTTP  Server:用于处理用户发送的API请求,以及其它ETCD节点同步与心跳信息的请求。

Store:用于处理ETCD支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等等,是ETCD对用户提供的大多数API功能的具体实现。

Raft:Raft强一致性算法的具体实现,是ETCD的核心。

WAL:Write Ahead Log(预写式日志),是ETCD的数据存储方式。除了在内存中存有所有数据的状态以及节点的索引外,ETCD就通过WAL进行持久化存储。WAL中所有的数据提交前都会事先记录日志。Entry表示存储的具体日志内容。Snapshot是为了防止数据过多而进行的状态快照;

通常一个ETCD集群是由3个或者5个节点组成,多个节点之间通过Raft一致性算法完成协同;其中会有一个节点作为Leader主节点,负责数据的同步与分发。当Leader主节点出现故障时会自动选举另一节点为Leader主节点。

ETCD架构中,有一个非常关键的概念quorum,quorum的定义是(n+1)/2,即超过集群中半数节点的临界值;只要确保quorum数量的节点正常工作, 那么整个集群就能正常工作并对外提供服务;

为了保障部分节点故障之后,依然能继续提供服务,就需要解决一个非常复杂分布式一致性的问题。ETCD是采用Raft一致性算法来实现,通过一套数据同步机制,在Leader切换后能够重新同步所有提交的数据,保证整个集群数据一致。

客户端在多个节点中,任意选择其中的一个就可以完成数据的读写,内部的状态及数据协同由ETCD自身完成。ETCD内部的机制比较复杂,但是面向用户提供的接口非常简单,可以通过客户端或者通过HTTP的方式连接集群并操作数据,可以大致将接口分为了5组:

Put(key,value)/Delete(key):Put向集群中写入数据,Delete删除集群中的数据。

Get(key)/Get(keyFrom,keyEnd):支持两种查询方式,指定单个key的查询与指定的一个key的范围的查询。

Watch(key)/Watch(keyPrefix):支持Watch机制实现增量数据更新监听,支持指定单个key与一个key前缀,实际应用中的建议使用key前缀。

 Transactions(if/then/else ops).Commit():支持简单的事务,可以通过指定一组条件满足时执行某些动作,当条件不成立的时候执行另一组操作。

Leases: Grant/Revoke/KeepAlive:支持租约机制,通常情况下在分布式系统中需要去检测一个节点是否存活的场景,就需要租约机制。


1.3.ETCD核心技术与原理1.3.1.从Paxos到Raft,实现分布式一致性

Paxos算法是1990年Leslie提出的,是一个经典且完备的分布式一致性算法。其目标是在不同的节点间,对一个key的取值达成共识(同一个值)。在Paxos中一个决策过程(Round、Phase)分为两个阶段:

准备阶段 phase1:Proposer向超过半数(n/2+1)的Acceptor发起prepare消息(发送编号);如果Acceptor收到一个编号为N的Prepare请求,且N大于该Acceptor已经响应过的所有Prepare请求的编号,那么就将N作为响应反馈给Proposer,同时该Acceptor承诺不再接受任何编号小于N的提案,否则拒绝返回。

投票阶段 phase2:如果超过半数Acceptor回复promise,Proposer向Acceptor发送accept消息。Acceptor检查accept消息是否符合规则,只要该Acceptor没有对编号大于N的Prepare请求做出过响应,它就接受该提案。

在实际发展中,Paxos算法也演化出了一系列的变种:PBFT算法是一种共识算法,较高效地解决了拜占庭将军问题;Multi-Paxos算法优化了prepare阶段的效率,同时允许多个Leader并发提议;以及FastPaxos、EPaxos等,这些演变是针对某些问题进行的优化,内核思想还是依托于Paxos思想。

Paxos从被提出一直是分布式一致性算法的标准协议,但是它不易理解并且实现起来非常复杂,以至于到目前为止都没有一个完整的实现方案,很多实现都是都是Paxos-likeRaft算法是斯坦福大学2017年提出,Raft通过分治思想把算法流程分为三个子问题,分别是选举(Leader election)、日志复制(Log replication)、安全性(Safety)

(一)选举(Leader election):Raft定义集群节点有4种状态,分别是Leader、Follower、Candidate、PreCandidate。正常情况下,Leader节点会按照心跳间隔时间,定时广播心跳消息给Follower节点,以维持Leader身份。Follower收到后回复消息给Leader。Leader都会带有一个任期号(term),用于比较各个节点数据新旧,识别过期Leader等。当Leader节点异常时,Follower节点会接收Leader的心跳消息超时,当超时时间大于竞选超时时间后,会进入PreCandidate状态,不自增任期号仅发起预投票,获得大多数节点认可后,进入Candidate状态并等待一个随机时间,然后发起选举流程,自增任期号投票给自己,并向其他节点发送竞选投票信息。当节点收到其他节点的竞选消息后,首先判断竞选节点的数据及任期号大于本节点,并且在本节点未发起选举给自己投,则可以投票给竞选节点,否则拒绝投票。

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

以“hello=world”为例,从Client提交提案到接收响应的整个流程为例,展示Raft日志全流程,通过一下8步Leader同步日志条目给各个Follower,保证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该提案通过。

(三)安全性(Safety):通过为选举及日志复制增加一系列规则,来保证Raft算法的安全性:

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

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

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

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

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

ETCD使用Raft协议来维护集群内各个节点状态的一致性,多个分布式节点相互通信构成整体对外服务,每个节点都存储了完整的数据,并且通过Raft协议保证每个节点维护的数据是一致的。每个ETCD节点都维护了一个状态机,并且任意时刻至多存在一个有效的主节点。主节点处理所有来自客户端写操作,通过Raft协议保证写操作对状态机的改动会可靠的同步到其他节点。

1.3.2.boltdb为基础 ,实现高效KV存储引擎 

ETCD是使用boltdb来持久化存储KV核心的元数据,boltdb整个数据库就一个db文件,基于B+树的索引,读效率高效且稳定;读事务可多个并发,写事务只能串行,并且开销较大不能并发,只能靠批量操作来缓解性能问题。

(一)page文件块:boltdb以4k定长为存储单元划分空间,即page文件块,所有其他对象都是以此为基础扩展抽象。

boltdb存储的管理单元是page,每个page都由header+ data组成,page的类型有以下几种:

meta page:元数据信息。包含存储B+树root buck位置,可用块的数量freelist,当前最大块的offset等。分为metaA与metaB, 用来控制进行中的事务与已完成的事务(写事务只有一个进行中).  根据meta中的txid的值的大小来判断当前生效的是哪个meta。(txid会根据事务操作递增)

bucket page:存储bucket名称数据,bucket名称也是采用B+树结构存储。根root bucket的pageid由meat的root字段指定

branch page:存储分支数据内容。分支数据结构中只有key信息,没有value信息。指向的都是下一级节点的pageid信息

leaf page:存储叶子数据内容。

freelist page:Freelist用于管理当前可用的pageid列表。由Meta元数据信息中的freelist字段来定位所在的pageid位置。

(二)B+树索引:boltdb是采用了变异的B+树索引:节点分支个数不固定;叶子节点不相互感知,不保证所有的叶子节点在同一层。但是索引查找和数据组织形式都是标准B+树。

其中Bucket本质上是命名空间,是一些key/value的集合,不同的Bucket可以有同名的key/value ;node是B+树节点的抽象封装,page是磁盘物理的概念node则是逻辑上的抽象;在boltdb中,Bucket是可以嵌套的,boltdb天生就有一个Bucket,这个是自动生成的,由meta指向,不是用户创建的,后续创建的Bucket都是这个Bucket的sub bucket。

   boltdb实现事务的方式非常简单,就是绝对不覆盖更新数据。其中meta是通过两个互为备份的page页轮转写实现的,每次都写新的地方,最后更改路径引用,具体步骤如下:。

1.通过key查询到位于B+树的那个page;

2.把这个page读出来,构建node节点,更新node的内容;

3.把node的内容写到空闲的的page,并且一层层往上;

4.最终更新meta索引内容;

1.3.3.MVCC多版本&Watch机制

ETCD通过term标识集群Leader的任期,当集群发生Leader切换,term的值就会+1。通过revision标识全局数据的版本,当数据发生变更(创建、修改、删除),revision都会+1。在相同集群多Leader任期时,revision依然会保持全局单调递增,使得集群任意一次的修改都对应一个唯一的revision,以此来实现数据的MVCC多版本及数据的Watch机制


(一)MVCC多版本:如上图,在同一个Leader任期之内,所有的修改操作对应的term值始终都等于2,而revision则保持单调递增。当重启集群之后,所有的修改操作对应的term值都变成了3。在新的Leader任期内,所有的term值都等于3,且不会发生变化,而对应的revision值同样保持单调递增。从一个更大的维度去看,可以发现在term=2和term=3的两个Leader任期之间,数据对应的revision值依旧保持了全局单调递增。

(二)Watch机制:在clientv3库中,Watch特性被抽象成Watch、Close、RequestProgress三个简单API提供给开发者使用,屏蔽了client与gRPCWatchServer交互的复杂细节,实现了一个client支持多个gRPCStream,一个gRPCStream支持多个watcher,显著降低了你的开发复杂度。同时当watch连接的节点故障,clientv3库支持自动重连到健康节点,并使用之前已接收的最大版本号创建新的watcher,避免旧事件回放等。


1.4.总结

ETCD是在ZooKeeper与doozer之后的项目,深度借鉴了前两个项目的精华,同时也解决了前两个项目的问题。使用场景都相差不大,主要是分布式协同协调场景,包含集群监控,集群选主,服务发现,消息分布与订阅,负载均衡,分布式通知与协调,分布式锁及分布式队列等。

同时ETCD在kubernetes生态体系的带领下,快速发展至稳定,无论是整体架构设计,性能,稳定性及用户体验都是远远超越了ZooKeeper与doozer;不仅在kubernetes生态有大量产品集成依赖,非kubernetes体系的也有越来越多的产品集成使用。


kubernetes 核心组件之 etcd

文章目录

etcd

Etcd是CoreOS基于Raft协议开发的分布式key-value存储,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)。

etcd 发展历程

2013 年,一个叫做作 CoreOS 的创业团队认为,所有的安全问题,都可以归结为软件更新能力的问题,如果可以构建一个自动更新的服务器(自动化),那么系统的安全问题将迎刃而解。为了达到这个目的,必须将应用程序打包成一个独立单元,达到基本操作系统和应用程序隔离的目的,CoreOS 通过容器技术来达到目的。同时,还共了集群化的管理方案,让用户管理集群就像管理单机一样方便。
对于管理集群,CoreOS 团队面临的第一个问题就是,如何保证集群的高可用?换句话说,CoreOS 团队希望在重启任意一个集群中的某个节点时,其他的应用程序不会因此而宕机。因此,需要运行多个副本。那多个副本,就需要进行协调服务。CoreOS 首先在开源社区去寻找优秀的、相关的开源协调服务项目,它们希望这个开源项目满足 5 个目标:

  • 高可用
    协调服务作为集群的控制面板,它的背后保存了各个服务的部署信息。如果出现故障,可能会导致集群无法变更、服务副本数量无法协调,从而影响用户体验,例如数据加载过慢(节点少,流量大)。

  • 低容量
    只保存存储关键元数据,集群、节点的配置信息。

  • 可维护性
    在分布式系统出现 bug 时,都会出现新增、替换节点的场景,这些都需要通过协调服务进行节点的变更,如果人工去操作这些节点,那可能会出现认为操作失误导致的宕机,能否通过 API 来约定协调服务需要进行的操作,这会是更平滑的一种方式。

  • 数据一致性
    高可用的前提下,不能出现单点故障,那多节点(多个副本)又会出现数据一致性的问题。

  • 能够监听节点并且可以进行增删改查的基本操作
    节点出现异常或变更时,相比控制端定时去轮询检查,一个“pull”的拉取操作,变成一个“push”操作,将事件变为推送操作,可以避免协调服务中不必要的性能开销。

有了理想目标后,CoreOS 开始寻找这样的协调服务了,不过很遗憾,在 2013 年并没有,因此 CoreOS 决定自己造轮子,那接下来就是为了实现这个目标而技术选型了。
在解决数据一致性的问题上,CoreOS 选择引入一个共识算法 Raft。该算法是易于理解的,它将一致性问题拆解成了 Leader 选举、日志同步、安全性三个相对独立的子问题。
再来看看低容量的问题,CoreOS 参考了 ZooKeeper 的数据模型与 API,使用基于目录的层级模式,而 API 相比 ZooKeeper 来说,使用了更加简单的 REST API,来对节点进行增删改查以及监听操作。
对于在存储引擎上的节点,etcd 选择的是简单内存树,将节点的数据结构进行了精简,节点中包含路径、值、孩子节点信息。这是一个典型的低容量设计。
最后是可维护性,Raft 算法提供了成员变更算法,基于此来监听成员的在线、变更状态。
基于以上的技术选型,CoreOS 团队在 2013 年发布了基于 Go 编写的 etcd 的第一个版本 beta v0.1 。
2014 年 6 月,Google 的 Kubernetes 项目横空出世,它选择了 etcd 作为服务协调的基石,因为 etcd 的目标与 Kubernetes 所需要的特性不谋而合,例如:高可用、Watch 机制。Kubernetes 项目使用 etcd,除了技术因素,也与当时的商业竞争有关,CoreOS 是 Kubernetes 容器生态圈的核心成员之一。在 Kubernetes 的带领下,etcd 也进入了快速发展的时代。因此在 2015 年 1 月,etcd 的 v2.0 版本正式发布,此时它被广泛应用于配置存储、服务发现、主备选举等场景。不过此时也出现了若干性能、功能不足的问题,如下:

  • 功能局限问题
    首先, etcd v2.0 不支持范围查询和分页。在 Kubernetes 集群规模增大后,Pod、Event 等资源可能出现上千个,不支持分页会出现 expensive request,引发性能问题。其次是不支持多 key 事务,在实际场景中,我们往往需要同时更新多个 key。

  • 性能瓶颈问题
    HTTP/1.x 协议的瓶颈引发了 etcd 的性能瓶颈,因为 HTTP/1.x 协议没有压缩机制,批量拉取较多的 Pod 时容易导致 Server 和 etcd 出现丢包、CPU 高负载、内存溢出问题。

  • 内存开销问题
    前面我们知道了,etcd 使用了一颗内存树来保存节点,在数据场景较大时,配置项较多,将会存储大量的数据,导致内存靠小较大,这影响了系统的稳定性。

  • Watch 机制可靠性问题
    Kubernetes 非常依赖 etcd Watch 机制,然而 etcd v2.0 是内存型,不支持保存节点的历史版本,只在内存中使用滑动窗口保存了最近的变更时间,当 etcd server 写请求较多、网络波动时,很容易出现事件丢失,因此触发 client 数据的全量拉取,产生大量的 expensive request。

面对这些问题,CoreOS 团队积极听取了开源社区的声音并积极改进,面对功能局限中提到的分页、范围查询查询不支持,那么它就去支持;面对内存开销大的问题,那它就通过引入 B-tree、boltdb 实现一个 MVCC 数据库,将数据模型从层次性改为扁平型,基于 boltdb 实现持久化存储,降低 etcd 的内存占用;对于性能瓶颈,它引入了 gRPC,通过 protobuf 定义消息,解码性能得到了大幅的提高,并通过 HTTP/2.0 多路复用机制,减少了大量 watcher 等场景下的连接数。基于这些解决方案,在 2016 年 6 月,etcd v3.0 正式发布了。

如今,etcd已然成为了云原生时代首选的元数据存储产品


etcd 工作原理

etcd集群本身是一个分布式系统,由多个节点相互通信构成整体对外服务,每个节点都存储了完整的数据,并且通过Raft协议保证每个节点维护的数据是一致的,在ETCD集群中任意时刻至多存在一个有效的主节点,由主节点处理所有来自客户端写操作,通过Raft协议保证写操作对状态机的改动会可靠的同步到其他节点,Raft协议如下图所示:

Raft协议主要分为三个部分:选举,复制日志,安全性。


选举

Raft协议是用于维护一组服务节点数据一致性的协议。这一组服务节点构成一个集群,并且有一个主节点来对外提供服务。当集群初始化,或者主节点挂掉后,面临一个选举问题。集群中每个节点,任意时刻处于Leader、Follower、Candidate这三个角色之一,选举特点如下:

  • 当集群初始化时候,每个节点都是Follower角色。
  • 集群中存在至多1个有效的主节点,通过心跳与其他节点同步数据;
  • 当Follower在一定时间内没有收到来自主节点的心跳,会将自己角色改变为Candidate,并发起一次选举投票。
  • 当收到包括自己在内超过半数节点赞成后,选举成功。
  • 当收到票数不足半数选举失败,或者选举超时。
  • 若本轮未选出主节点,将进行下一轮选举(出现这种情况,是由于多个节点同时选举,所有节点均未获得过半选票)。
  • Candidate节点收到来自主节点的信息后,会立即终止选举过程,进入Follower角色。

为了避免陷入选举失败循环,每个节点未收到心跳发起选举的时间是一定范围内的随机值,这样能够避免2个节点同时发起选举。


复制日志

日志复制是指主节点将每次操作形成日志条目,并持久化到本地磁盘,然后通过网络IO发送给其他节点。其他节点根据日志的逻辑时钟(TERM)和日志编号(INDEX)来判断是否将该日志记录持久化到本地。当主节点收到包括自己在内超过半数节点成功返回,那么认为该日志是可提交的(committed),并将日志输入到状态机,将结果返回给客户端。

这里需要注意的是,每次选举都会形成一个唯一的TERM编号,相当于逻辑时钟,每一条日志都有全局唯一的编号。

主节点通过网络IO向其他节点追加日志。若某节点收到日志追加的消息,首先判断该日志的TERM是否过期,以及该日志条目的INDEX是否比当前以及提交的日志的INDEX更早。若已过期,或者比提交的日志更早,那么就拒绝追加,并返回该节点当前的已提交的日志的编号。否则将日志追加,并返回成功。

当主节点收到其他节点关于日志追加的回复后,若发现有拒绝,则根据该节点返回的已提交日志编号,发生其编号下一条日志。

主节点向其他节点同步日志,还作了拥塞控制。主节点发现日志复制的目标节点拒绝了某次日志追加消息,将进入日志探测阶段,一条一条发送日志,直到目标节点接受日志,然后进入快速复制阶段,可进行批量日志追加。

按照日志复制的逻辑,我们可以看到,集群中慢节点不影响整个集群的性能。另外一个特点是,数据只从主节点复制到Follower节点,这样大大简化了逻辑流程。Raft日志复制路程如下图所示:


安全

选举和复制日志并不能保证节点间数据一致。当一个某个节点挂掉了,一段时间后再次重启,并刚好当选为主节点。而在其挂掉这段时间内,集群若有超过半数节点存活,集群会正常工作,那么会有日志提交,这些提交的日志无法传递给挂掉的节点。当挂掉的节点再次当选举节点,它将缺失部分已提交的日志。在这样场景下,按Raft协议,它将自己日志复制给其他节点,会将集群已经提交的日志给覆盖掉,这显然是不可接受的,对于出现这种问题解决办法:

  • 其他协议解决这个问题的办法是:
    新当选的主节点会询问其他节点,和自己数据对比,确定出集群已提交数据,然后将缺失的数据同步过来。这个方案有明显缺陷,增加了集群恢复服务的时间(集群在选举阶段不可服务),并且增加了协议的复杂度。

  • Raft解决的办法是:
    在选举逻辑中,对能够成为主节点加以限制,确保选出的节点已定包含了集群已经提交的所有日志。如果新选出的主节点已经包含了集群所有提交的日志,那就不需要从和其他节点比对数据了,简化了流程,缩短了集群恢复服务的时间。


ETCD应用场景

服务发现

ETCD服务发现示意图如下图所示:

服务发现是分布式系统中最常见的需要解决的问题之一,即在同一个分布式集群中的进程或服务,客户端通过名字就可以查找和连接服务端。要解决服务发现的问题,需要有下面三点:

  • 一个强一致性、高可用的服务存储目录。基于Raft算法的etcd天生就是这样一个强一致性高可用的服务存储目录。
  • 一种注册服务和监控服务健康状态的机制。用户可以在etcd中注册服务,并且对注册的服务设置key TTL,定时保持服务的心跳以达到监控健康状态的效果。
  • 一种查找和连接服务的机制。通过在etcd指定的主题快速找到服务地址。

(1) 在微服务中使用etcd服务发现

随着Docker容器的流行,多种微服务共同协作,构成一个相对功能强大的组织架构。使用etcd服务发现机制,在etcd中注册某个服务名字的目录,在该目录下存储可用的服务节点的IP。服务使用者从etcd目录下查找可用的服务节点IP来连接和调用,达到透明化的动态添加这些服务目的,示意图如下图所示:

(2) 在PaaS平台中使用etcd服务发现

PaaS平台中的应用一般都有多个实例,通过域名不仅可以透明的对多个实例进行访问,而且还可以做到负载均衡。但是应用的某个实例随时都有可能故障重启,这时就需要动态的配置域名解析(路由)信息,通过etcd的服务发现功能就可以轻松解决这个动态配置的问题,实现多实例与实例故障重启透明化目的,示意图如下图所示:


发布订阅消息

etcd的发布订阅消息示意图如下图所示:

在分布式系统中,消息发布与订阅最适合使用用在组件之间通信。使用etcd发布订阅功能可以实现一个配置共享中心,数据提供者在配置中心发布消息,消息消费者订阅他们关心的主题,一旦主题有新消息发布,就会实时通知订阅者,通过这种方式可以做到分布式系统配置的集中式管理与动态更新。

etcd发布订阅最典型应用在kubernetes上,其他场景应用:

  • app或服务用到的一些配置信息放到etcd上进行集中管理。在启动的时候主动从etcd获取一次配置信息,在etcd节点上注册一个Watcher并等待,以后每次配置有更新的时候,etcd都会实时通知订阅者,以此达到获取最新配置信息的目的。
  • 分布式搜索服务中,索引的元信息和服务器集群机器的节点状态存放在etcd中,供各个客户端订阅使用。使用etcd的key TTL功能可以确保机器状态是实时更新的。
  • 分布式日志收集系统。 这个系统的核心工作是收集分布在不同机器的日志。收集器通常是按照应用(或主题)来分配收集任务单元,因此可以在etcd上创建一个以应用(主题)命名的目录,并将这个应用(主题相关)的所有机器ip,以子目录的形式存储到目录上,然后设置一个etcd递归的Watcher,递归式的监控应用(主题)目录下所有信息的变动。这样就实现了机器IP(消息)变动的时候,能够实时通知到收集器调整任务分配。
  • 系统中信息需要动态自动获取与人工干预修改信息请求内容的情况。只需要要将信息存放到指定的etcd目录中,etcd的这些目录就可以通过HTTP的接口在外部访问。

负载均衡

etcd的负载均衡示意图如下图所示:

etcd本身分布式架构存储的信息访问支持负载均衡,etcd集群化以后,每个etcd的核心节点都可以处理用户的请求。所以把数据量小但是访问频繁的消息数据直接存储到etcd中也是个不错的选择。 etcd可以监控一个集群中多个节点的状态,利用etcd维护一个负载均衡节点表,当有一个请求发过来后,可以轮询式的把请求转发给存活着的节点。

分布式系统中,为了保证服务的高可用以及数据的一致性,通常都会把数据和服务部署多份,以此达到对等服务,即使其中的某一个服务失效了,也不影响使用。由此带来的坏处是数据写入性能下降,而好处则是数据访问时的负载均衡。因为每个对等服务节点上都存有完整的数据,所以用户的访问流量就可以分流到不同的机器上。


分布式通知与协调

这里说到的分布式通知与协调,与消息发布和订阅有些相似。都用到了etcd中Watche机制,通过注册与异步通知机制,实现分布式环境下不同系统之间 的通知与协调,从而对数据变更做到实时处理。实现方式:不同系统都在etcd上对同一个目录进行注册,同时设置Watcher观测该目录的变化(如果对子目录的变化也有需要,可以设置递归模式),当某个系统更新了etcd的目录,那么设置了Watcher的系统就会收到通知,并作出相应处理。

使用etcd完成分布式协同工作原理如下图所示:

  • 通过etcd进行低耦合的心跳检测。检测系统和被检测系统通过etcd上某个目录关联而非直接关联起来,这样可以大大减少系统的耦合性。
  • 通过etcd完成系统调度。某系统有控制台和推送系统两部分组成,控制台的职责是控制推送系统进行相应的推送工作。管理人员在控制台作的一些操作,实际上是修改了etcd上某些目录节点的状态,而etcd就把这些变化通知给注册了Watcher的推送系统客户端,推送系统再作出相应的推送任务。
  • 通过etcd完成工作汇报。大部分类似的任务分发系统,子任务启动后,到etcd来注册一个临时工作目录,并且定时将自己的进度进行汇报(将进度写入到这个临时目录),这样任务管理者就能够实时知道任务进度。

分布式锁

因为etcd使用Raft算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁,锁有两种使用方式:

  • 保持独占,即所有获取锁的用户最终只有一个可以得到。etcd为此提供了一套实现分布式锁原子操作CAS(CompareAndSwap)的API。通过设置prevExist值,可以保证在多个节点同时去创建某个目录时只有一个成功,而创建成功的用户就可以认为是获得了锁。
  • 控制时序,即所有想要获得锁的用户都会被安排执行,但是获得锁的顺序也是全局唯一的,同时决定了执行顺序。etcd为此也提供了一套API(自动创建有序键),对一个目录建值时指定为POST动作,这样etcd会自动在目录下生成一个当前最大的值为键,存储这个新的值(客户端编号)。同时还可以使用API按顺序列出所有当前目录下的键值。此时这些键的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号。

从etcd获取的分布式锁如下图所示:


分布式队列

分布式队列的常规用法与场景五中所描述的分布式锁的控制时序用法类似,创建一个先进先出的队列,保证顺序。另一种比较有意思的实现是在保证队列达到某个条件时再统一按顺序执行。这种方法的实现可以在/queue这个目录中另外建立一个/queue/condition节点,condition可以表示信息如下:

  • condition可以表示队列大小。比如一个大的任务需要很多小任务就绪的情况下才能执行,每次有一个小任务就绪,就给这个condition数字加1,直到达到大任务规定的数字,再开始执行队列里的一系列小任务,最终执行大任务,如下图所示:

  • condition可以表示某个任务在不在队列。这个任务可以是所有排序任务的首个执行程序,也可以是拓扑结构中没有依赖的点。通常必须执行这些任务后才能执行队列中的其他任务。

  • condition还可以表示其它的一类开始执行任务的通知。可以由控制程序指定,当condition出现变化时,开始执行队列任务。


集群监控

使用etcd来实现集群的实时性的监控,可以第一时间检测到各节点的健康状态,以完成集群的监控要求。etcd本身就有自带检点健康监控功能,实现起来也比较简单。

使用Watcher机制,当某个节点消失或有变动时,Watcher会第一时间发现并告知用户。
节点可以设置TTL key,比如每隔30s发送一次心跳使代表该机器存活的节点继续存在,否则节点消失。

以上是关于Kubernetes核心组件-ETCD详解的主要内容,如果未能解决你的问题,请参考以下文章

云原生第三周--kubernetes组件详解

kubernetes 核心组件之 etcd

Kubernetes - Kubernetes架构及和核心组件

kubernetes控制平面组件:etcd

Kubernetes 集群组件

云原生训练营模块五 Kubernetes 控制平面组件:etcd