Etcd必知必会

前言

在前面的k8s的学习中,我们知道etcd存储了整个集群的状态信息,集群状态是非常重要的信息,在分布式环境下要能容忍失败,而且还不能挂。那么etcd是怎么做到的呢?

什么是etcd

我们来看下官方是怎么定义etcd的。

etcd是一个强一致的分布式键值存储,它提供了一种可靠的方式来存储需要由分布式系统或机器群访问的数据。 它优雅地处理网络分区期间的领导者选举,并且可以容忍机器故障,即使在领导者节点中也是如此。

我们看到了好几个关键字:分布式,键值,网络分区,机器故障,领导选举,尝试分别对这些名词进行解释下。

分布式,那么在架构上etcd部署应该多台服务器,集群部署一般由3-7个节点组成。

键值,类似于标准文件系统的数据存储。

网络分区,大多数分布式系统都分布在多个子网络,而区间通信可能会失败,就变成了网络分区。

机器故障,也就是集群中的节点,会因为各种原因失败,无法访问。

领导选举,节点挂掉了,领导者直接踢掉这台服务器即可,那么领导者也挂掉了呢,这时候就重新选择新的领导者了。

实现原理

etcd使用grpc作为消息传递的协议。对于不支持grpc的语言,提供Http Restful代理,该代理将Http/Json请求转换为grpc消息。

etcd采用Raft协议解决多个节点数据一致性。无论是领导节点还是从节点挂掉了,etcd都可以继续提供服务。

etcd底层存储在v2版本只是一个纯内存的实现,数据并没有存储在磁盘上,v3版本的实例采用bolt实现了数据的持久化。

Raft

etcd最核心的部分应该就raft协议。在etcd中,最常见的有2种角色,也就是Leader和Follower。Candidate角色在选举的时候开始出现。

这3个角色的规则如下:

Followers:

  1. 响应Leader 和 Candidate 发出的RPC请求
  2. 如果选举超时,没有从当前的Leader收到AppendEntries RPC请求或者没有收到节点给予的候选人投票,那么就把自己变为Candidate

Candidates:

  1. 成为候选人后,开始选举:
    增加当前的任期
    自己投票
    重置选举计时器
    发送equestVote RPC请求到所有其他服务器
  2. 如果从大多数服务器收到投票,那么就成为Leader
  3. 如果从Leader收到AppendEntries RPC请求,那么就变为Follower
  4. 如果选举超时,等待150-300ms重新发起选举

Leader:

  1. 选举期间:发送初始化空的AppendEntries RPC 心跳请求到每一个服务器,在idle空闲期间重复这个操作,防止选举超时。
  2. 如果从客户端收到,追加日志到本地日志,在进入状态机后响应请求。
  3. 如果Follower的最后日志索引(last log index) 大于等于 下一个索引(nextIndex),发送AppendEntries RPC请求,下一个日志从nextIndex开始。如果成功,为Follower更新 nextIndex和matchIndex。如果追加日志因不一致失败,递减nextIndex 并重试。

下面我们尝试从3个case出发,了解raft协议是如何实现数据一致性的。

第一次选举

使用的Raft协议的etcd集群在启动节点时,都是Follower的角色。集群中的Follower节点长时间没有收到Leader的心跳请求,就会进行预选举或者选举流程。

节点首先把自己变为Candidate角色,并给自己投票,然后对其他节点发起投票请求。当前节点接受到大于51%的节点投票,就会成为Leader。

节点失败

部分节点失败,并不会影响etcd服务正常的运行,客户端依然可以正常读写数据。

Leader并不需要等待全部的Follower写成功,只需要大部分,也就是超过半数即可。

失败的节点,重新加入集群,必须通过Leader维护的nextIndex索引,同步复制最新的日志条目。

Leader失败

Follower在指定时间内,如果没有收到AppendEntries RPC请求,那么就会发起投票,选举出新的Leader。

日志一致性

选举出Leader后,采用定时器同步日志到Follower节点。Raft维护着日志匹配的特性:

  1. 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令。
  2. 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们之前的所有日志条目也全部相同。

Raft采用以下2个办法来保证日志的一致性:

  1. 选举限制。一个候选人,必须包含最新的日志列表,也就是说它的日志是在当前集群中最新的。Raft通过对比最后一条日志的索引值和任期号,决定日志的是否为最新。如果两份日志的任期不一致,那么任期号较大的日志为最新。如果两份日志任期号相同,那么日志索引值较大的为最新。
  2. 提交之前任期内的日志条目。Leader在当前任期里的日志通过计算副本可以提交。一旦当前任期的日志以这种方式被提交,因为日志是匹配的,那么之前的日志也一定会被提交。

总结

在这篇文章里,我们理解了etcd的定义,实现原理以及Raft协议的实现。当然,文章是从设计原理以及中文版的论文来尝试理解Raft协议,毕竟没有阅读源码来着直接,如果可能,阅读源码肯定是最快了解Raft的协议实现的方法了。

参考链接

  1. 寻找一种易于理解的一致性算法

发表评论

电子邮件地址不会被公开。 必填项已用*标注