Kubernetes 之 详解Etcd集群

前言
html

随着kubernetes项目的日益火热,该项目中用到的etcd组件做为一个高可用强一致性的服务发现存储仓库,渐渐的被开发人员所关注。
git

在云计算时代,如何让服务快速、透明的接入到计算集群中,如何让共享配置信息快速被集群中的全部节点发现,如何构建一套高可用、安全、易于部署以及快速响应的服务集群成为了须要解决的问题。github

Etcd为解决这类问题带来便捷。算法

官方地址 https://coreos.com/etcd/数据库

项目地址 https://github.com/coreos/etcdapache

image.png

Etcd是什么api

Etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现,它经过Raft一致性算法处理日志复制以保证强一致性,咱们能够理解它为一个高可用强一致性的服务发现存储仓库
安全

在kubernetes集群中,etcd主要用于配置共享和服务发现服务器

Etcd主要解决的是分布式系统中数据一致性的问题,而分布式系统中的数据分为控制数据和应用数据,etcd处理的数据类型为控制数据,对于不多量的应用数据也能够进行处理。网络

Etcd和Zookeeper的比较

Zookeeper有以下缺点

  1. 1.复杂。ZooKeeper的部署维护复杂,管理员须要掌握一系列的知识和技能;而Paxos强一致性算法也是素来以复杂难懂而闻名于世(ETCD使用[Raft]协议, ZK使用ZAB,类PAXOS协议);另外,ZooKeeper的使用也比较复杂,须要安装客户端,官方只提供了Java和C两种语言的接口。

  2. 2.Java编写。这里不是对Java有偏见,而是Java自己就偏向于重型应用,它会引入大量的依赖。而运维人员则广泛但愿保持强一致、高可用的机器集群尽量简单,维护起来也不易出错。

  3. 3.发展缓慢。Apache基金会项目特有的“Apache Way”在开源界饱受争议,其中一大缘由就是因为基金会庞大的结构以及松散的管理致使项目发展缓慢。

相较之下,Etcd

      1.简单。使用Go语言编写部署简单;使用HTTP做为接口使用简单;使用Raft算法保证强一致性让用户易于理解

  1. 2.数据持久化。etcd默认数据一更新就进行持久化。

  2. 3.安全。etcd支持SSL客户端安全认证。

Etcd的架构与术语

image.png

流程分析

一般一个用户的请求发送过来,会通过HTTP Server转发给Store进行具体的事务处理,若是涉及到节点的修改,则须要交给Raft模块进行状态的变动,日志的记录。

而后再同步给别的etcd节点确认数据提交,最后进行数据提交,再次同步。

工做原理

Etcd使用Raft协议来维护集群内各个节点状态的一致性。简单说,ETCD集群是一个分布式系统,由多个节点相互通讯构成总体对外服务,每一个节点都存储了完整的数据,而且经过Raft协议保证每一个节点维护的数据是一致的。

Etcd主要分为四个部分

  1. HTTP Server: 用于处理用户发送的API请求以及其余etcd节点的同步与心跳信息请求

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

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

  4. WAL:Write Ahead Log(预写式日志/日志先行),是 etcd 的数据存储方式,也是一种实现事务日志的标准方法。etcd经过 WAL 进行持久化存储,全部的数据提交前都会事先记录日志。Snapshot 是为了防止数据过多而进行的状态快照;Entry 表示存储的具体日志内容。

服务发现

服务发现要解决的也是分布式系统中最多见的问题之一,即在同一个分布式集群中的进程或服务,要如何才能找到对方并创建链接。本质上来讲,服务发现就是想要了解集群中是否有进程在监听 udp 或 tcp 端口,而且经过名字就能够查找和链接。要解决服务发现的问题,须要具有如下三点:

1.一个强一致性、高可用的服务存储目录。基于 Raft 算法的 etcd 天生就是这样一个强一致性高可用的服务存储目录。

2.一种注册服务和监控服务健康状态的机制。用户能够在 etcd 中注册服务,而且对注册的服务设置key TTL,定时保持服务的心跳以达到监控健康状态的效果。

3.一种查找和链接服务的机制。经过在 etcd 指定的主题下注册的服务也能在对应的主题下查找到。为了确保链接,咱们能够在每一个服务机器上都部署一个 Proxy 模式的 etcd,这样就能够确保能访问 etcd 集群的服务都能互相链接。
例如随着 Docker 容器的流行,多种微服务共同协做,构成一个相对功能强大的架构的案例愈来愈多。透明化的动态添加这些服务的需求也日益强烈。经过服务发现机制,在 etcd 中注册某个服务名字的目录,在该目录下存储可用的服务节点的 IP。在使用服务的过程当中,只要从服务目录下查找可用的服务节点去使用便可。

Etcd集群中的术语

  • Raft: etcd所采用的保证分布式系统强一致的算法

  • Node: 一个Raft状态机实例

  • Member: 一个etcd实例,管理一个Node,能够为客户端请求提供服务

  • Cluster: 多个Member构成的能够协同工做的etcd集群

  • Peer: 同一个集群中,其余Member的称呼

  • Client: 向etcd集群发送HTTP请求的客户端

  • WAL: 预写日志,是etcd用于持久化存储的日志格式

  • Snapshot: etcd防止WAL文件过多而设置的快照,存储etcd数据状态

  • Proxy: etcd的一种模式,能够为etcd提供反向代理服务

  • Leader: Raft算法中经过竞选而产生的处理全部数据提交的节点

  • Follower: Raft算法中竞选失败的节点,做为从属节点,为算法提供强一致性保证

  • Candidate: Follower超过必定时间接收不到Leader节点的心跳的时候,会转变为Candidate(候选者)开始Leader竞选

  • Term: 某个节点称为Leader到下一次竞选开始的时间周期,称为Term(任界,任期)

  • Index: 数据项编号, Raft中经过Term和Index来定位数据

Raft算法

Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,可是它的算法结构和 Paxos 不一样,使得 Raft 算法更加容易理解而且更容易构建实际的系统。一致性算法容许一组机器像一个总体同样工做,即便其中一些机器出现故障也可以继续工做下去。正由于如此,一致性算法在构建可信赖的大规模软件系统中扮演着重要的角色。

Raft算法分为三部分

Leader选举、日志复制和安全性

Raft算法特性:

1.强领导者: 和其余一致性算法相比,Raft 使用一种更强的领导能力形式。好比,日志条目只从领导者发送给其余的服务器。这种方式简化了对复制日志的管理而且使得 Raft 算法更加易于理解。

2.领导选举: Raft 算法使用一个随机计时器来选举领导者。这种方式只是在任何一致性算法都必须实现的心跳机制上增长了一点机制。在解决冲突的时候会更加简单快捷。

3.成员关系调整: Raft 使用一种共同一致的方法来处理集群成员变换的问题,在这种方法下,处于调整过程当中的两种不一样的配置集群中大多数机器会有重叠,这就使得集群在成员变换的时候依然能够继续工做。

Leader选举

Raft 状态机

Raft集群中的每一个节点都处于一种基于角色的状态机中。具体来讲,Raft定义了节点的三种角色: Follower、Candidate和Leader。

1.Leader(领导者): Leader节点在集群中有且仅能有一个,它负责向全部的Follower节点同步日志数据

2.Follower(跟随者): Follower节点从Leader节点获取日志,提供数据查询功能,并将全部修改请求转发给Leader节点

3.Candidate(候选者): 当集群中的Leader节点不存在或者失联以后,其余Follower节点转换为Candidate,而后开始新的Leader节点选举

这三种角色状态之间的转换,以下图:

image.png

 一个 Raft 集群包含若干个服务器节点;一般是 5 个,这容许整个系统容忍 2 个节点的失效。在任什么时候刻个服务器节点都处于这三个状态之一:领导人、跟随者或者候选人。在一般状况下,系统中只有一个领导人而且其余的节点所有都是跟随者。跟随者都是被动的:他们不会发送任何请求,只是简单的响应来自领导者或者候选人的请求。领导人处理全部的客户端请求(若是一个客户端和跟随者联系,那么跟随者会把请求重定向给领导人)

在节点初始启动的时候,全部节点的Raft状态机都会处于Follower状态。当Follower在必定的时间周期内没有收到来自Leader节点的心跳数据包的时候,节点会将本身的状态切换为Candidate,并向集群中其余Follower节点发送投票请求,Follower都会将本身的票投给收到的第一个投票请求节点。当Candidate收到来自集群中超过半数节点的投票后,会成为新的Leader节点。

Leader节点将接受并保存用户发送的数据,并向其余的Follower节点同步日志。

Follower只响应来自其余服务器的请求。若是Follower接收不到消息,那么他就会变成候选人并发起一次选举。得到集群中大多数选票的候选人将成为Leader。在一个任期(Term)内,领导人一直都会是领导人直到本身宕机了。

Leader节点依靠定时向全部Follower发送心跳数据来保持地位。当急群众的Leader节点出现故障的时候,Follower会从新选举新的节点,保证整个集群正常运行。 

每次成功的选举,新的Leader的Term(任期)值都会比以前的Leader增长1。当集群中因为网络或者其余缘由出现分裂后又从新合并的时候,集群中可能会出现多于一个的Leader节点,此时,Term值更高的节点才会成为真正的Leader。

Raft算法中的Term(任期)

关于Term,以下图:

image.png

Raft会把时间分割成任意长度的任期。而且任期用连续的整数来标记。每一段任期都是从一次选举开始一个或者多个候选人尝试成为领导者。若是一个候选人赢得选举,而后他就会在接下来的任期中充当Leader的职责。在某些状况下,一次选举会形成选票瓜分,这样,这一个任期将没有Leader。若是没有Leader,那么新的一轮选举就立刻开始,也就是新的任期就会开始。Raft保证了在一个Term任期内,有且只有一个Leader。

日志复制

所谓日志复制,是指主节点将每次操做造成日志条目,并持久化到本地磁盘,而后经过网络IO发送给其余节点。

一旦一个领导人被选举出来,他就开始为客户端提供服务。客户端的每个请求都包含一条被复制状态机执行的指令领导人把这条指令做为一条新的日志条目附加到日志中去,而后并行的发起附加条目 RPCs 给其余的服务器,让他们复制这条日志条目。

Raft 算法保证全部已提交的日志条目都是持久化的而且最终会被全部可用的状态机执行。当主节点收到包括本身在内超过半数节点成功返回,那么认为该日志是可提交的(committed),并将日志输入到状态机,将结果返回给客户端。

在正常的操做中,领导人和跟随者的日志保持一致性,因此附加日志 RPC 的一致性检查历来不会失败。然而,领导人崩溃的状况会使得日志处于不一致的状态(老的领导人可能尚未彻底复制全部的日志条目)。这种不一致问题会在一系列的领导人和跟随者崩溃的状况下加重。跟随者的日志可能和新的领导人不一样的方式。跟随者可能会丢失一些在新的领导人中有的日志条目,他也可能拥有一些领导人没有的日志条目,或者二者都发生。丢失或者多出日志条目可能会持续多个任期。这就引出了另外一个部分,就是安全性

安全性

截止此刻,选主以及日志复制并不能保证节点间数据一致。试想,当一个某个节点挂掉了,一段时间后再次重启,并当选为主节点。而在其挂掉这段时间内,集群如有超过半数节点存活,集群会正常工做,那么会有日志提交。这些提交的日志没法传递给挂掉的节点。当挂掉的节点再次当选主节点,它将缺失部分已提交的日志。在这样场景下,按Raft协议,它将本身日志复制给其余节点,会将集群已经提交的日志给覆盖掉。这显然是错误的

其余协议解决这个问题的办法是,新当选的主节点会询问其余节点,和本身数据对比,肯定出集群已提交数据,而后将缺失的数据同步过来。这个方案有明显缺陷,增长了集群恢复服务的时间(集群在选举阶段不可服务),而且增长了协议的复杂度。Raft解决的办法是,在选主逻辑中,对可以成为主的节点加以限制,确保选出的节点已定包含了集群已经提交的全部日志。若是新选出的主节点已经包含了集群全部提交的日志,那就不须要从和其余节点比对数据了。简化了流程,缩短了集群恢复服务的时间。

这里存在一个问题,加以这样限制以后,还可否选出主呢?答案是:只要仍然有超过半数节点存活,这样的主必定可以选出。由于已经提交的日志必然被集群中超过半数节点持久化,显然前一个主节点提交的最后一条日志也被集群中大部分节点持久化。当主节点挂掉后,集群中仍有大部分节点存活,那这存活的节点中必定存在一个节点包含了已经提交的日志了。

Etcd的代理节点(proxy)

Etcd针对Raft的角色模型进行了扩展,增长了Proxy角色proxy模式的本职就是启一个HTTP代理服务器把客户发到这个服务器的请求转发给别的 etcd 节点。

做为Proxy角色的节点不会参与Leader的选举,只是将全部接收到的用户查询和修改请求转发到任意一个Follower或者Leader节点上。

Proxy节点能够在启动Etcd的时候经过"--proxy on"参数指定。在使用了"节点自发现"服务的集群中,能够设置一个固定的"参选节点数目",超过这个数目的成员自动转换为Proxy节点。

一旦节点成为Proxy以后,便再也不参与全部Leader选举和Raft状态变化。除非将这个节点重启并指定为成员的Follower节点

etcd 做为一个反向代理把客户的请求转发给可用的 etcd 集群。这样,你就能够在每一台机器都部署一个 Proxy 模式的 etcd 做为本地服务,若是这些 etcd Proxy 都能正常运行,那么你的服务发现必然是稳定可靠的。

完整的Etcd角色状态转换过程以下图:

image.png

kubernetes项目中,Etcd用来作什么,为何选择它

etcd在kubernetes集群是用来存放数据并通知变更的

Kubernetes中没有用到数据库,它把关键数据都存放在etcd中,这使kubernetes的总体结构变得很是简单。

在kubernetes中,数据是随时发生变化的,好比说用户提交了新任务、增长了新的Node、Node宕机了、容器死掉了等等,都会触发状态数据的变动。状态数据变动以后呢,Master上的kube-scheduler和kube-controller-manager,就会从新安排工做,它们的工做安排结果也是数据。这些变化,都须要及时地通知给每个组件。etcd有一个特别好用的特性,能够调用它的api监听其中的数据,一旦数据发生变化了,就会收到通知。有了这个特性以后,kubernetes中的每一个组件只须要监听etcd中数据,就能够知道本身应该作什么。kube-scheduler和kube-controller-manager呢,也只须要把最新的工做安排写入到etcd中就能够了,不用本身费心去逐个通知了

试想一下,若是没有etcd,那么要怎样作?这里的本质是:数据的传递有两种方式,一种是消息的方式,好比说NodeA有了新的任务,Master直接给NodeA发一个消息,中间不通过任何人;一种是轮询的方式,你们都把数据写到同一个地方,每一个人自觉地盯着看,及时发现变化。前者演化出rabbitmq这样的消息队列系统,后者演化出一些有订阅功能的分布式系统。

第一种方式的问题是,全部要通讯的组件之间都要创建长链接,而且要处理各类异常状况,比例如链接断开、数据发送失败等。不过有了消息队列(message queue)这样的中间件以后,问题就简单多了,组件都和mq创建链接便可,将各类异常状况都在mq中处理。

那么为何kubernetes没有选用mq而是选用etcd呢?mq和etcd是本质上彻底不一样的系统,mq的做用消息传递,不储存数据(消息积压不算储存,由于没有查询的功能,etcd是个分布式存储(它的设计目标是分布式锁,顺带有了存储功能),是一个带有订阅功能的key-value存储。若是使用mq,那么还须要引入数据库,在数据库中存放状态数据。

选择etcd还有一个好处,etcd使用raft协议实现一致性,它是一个分布式锁,能够用来作选举。若是在kubernetes中部署了多个kube-schdeuler,那么同一时刻只能有一个kube-scheduler在工做,不然各自安排各自的工做,就乱套了。怎样保证只有一个kube-schduler在工做呢?那就是前文说到的经过etcd选举出一个leader

相关文章
相关标签/搜索