Etcd集群构建

 2017年11月14日 11:38   Nick王   运维  云计算    3 评论   801 浏览 

etcd 分布式,强一致的key/value存储

官方地址: https://coreos.com/etcd/

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

etcd版本: 3.2.7

etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现。etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性。

etcd可以用于配置共享和服务发现。

NoSQL和Etcd的差异:

NoSQLEtcd
核心关注点数据的存储效率(包括时间和空间的效率)
集群结构固定的主节点和从节点
节点角色有主节点和从节点的差别
数据写入方式通常情况的主从模式,主节点写入,从节点数据同步和读取
运行服务的节点仅在固定节点运行
客户端链接方式链接到固定的IP地址

etcd主要解决的是分布式系统中数据一致性的问题,而分布式系统中的数据分为控制数据和应用数据,etcd处理的数据是控制数据。对于很少量的应用数据也可以进行处理。


etcd架构与术语

etcd主要分为四个部分:

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

  2. Store:存储,用于处理etcd支持的各类功能的事务,包括数据索引,节点状态变更,事件处理与执行等。它是etcd对用于提供的大多数API功能的具体实现

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

  4. WAL: Write Ahead Log(日志先行),WAL是一种实现事务日志的标准方法。etcd通过WAL进行持久化存储,所有的数据在提交之前都会事先记录日志 a. Snapshot: 防止数据过多而进行的状态快照 b. Entry: 存储的具体的日志内容

架构图如下:

AA

通常一个用户的请求发送过来,会经过HTTP Server转发给Store进行具体的事务处理,如果涉及到节点的修改,则需要交给Raft模块进行状态的变更,日志的记录。然后再同步给别的etcd节点以确认数据提交,最后进行数据的提交,再次同步。

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算法论文(中文翻译): https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md

Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。

一致性算法允许一组机器像一个整体一样工作,即使其中一些机器出现故障也能够继续工作下去。正因为如此,一致性算法在构建可信赖的大规模软件系统中扮演着重要的角色。

Raft算法分为三个部分:

  1. Leader选举

  2. 日志复制

  3. 安全

Raft算法特性:

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

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

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

Raft 状态机

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

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

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

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

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

AA

一个 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,如下图所示:

AA

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

任期在 Raft 算法中充当逻辑时钟的作用,这会允许服务器节点查明一些过期的信息比如陈旧的领导者。每一个节点存储一个当前任期号,这一编号在整个时期内单调的增长。当服务器之间通信的时候会交换当前任期号;如果一个服务器的当前任期号比其他人小,那么他会更新自己的编号到较大的编号值。如果一个候选人或者领导者发现自己的任期号过期了,那么他会立即恢复成跟随者状态。如果一个节点接收到一个包含过期的任期号的请求,那么他会直接拒绝这个请求。

Leader 选举

Raft 使用一种心跳机制来触发领导人选举。当服务器程序启动时,他们都是跟随者身份。

领导者周期性的向所有跟随者发送心跳包(不包含日志项内容的附加日志项 RPCs)来维持自己的权威。

如果一个跟随者在一段时间里没有接收到任何消息,也就是选举超时,然后他就会认为系统中没有可用的领导者然后开始进行选举以选出新的领导者。

要开始一次选举过程,跟随者先要增加自己的当前任期号并且转换到候选人状态。然后他会并行的向集群中的其他服务器节点发送请求投票的 RPCs 来给自己投票。候选人会继续保持着当前状态直到以下三件事情之一发生:(a) 他自己赢得了这次的选举,(b) 其他的服务器成为领导者,(c) 一段时间之后没有任何一个获胜的人。

当一个候选人从整个集群的大多数服务器节点获得了针对同一个任期号的选票,那么他就赢得了这次选举并成为领导人。每一个服务器最多会对一个任期号投出一张选票,按照先来先服务的原则。

一旦候选人赢得选举,他就立即成为领导人。然后他会向其他的服务器发送心跳消息来建立自己的权威并且阻止新的领导人的产生。

在等待投票的时候,候选人可能会从其他的服务器接收到声明它是领导人的附加日志项 RPC。如果这个领导人的任期号(包含在此次的 RPC中)不小于候选人当前的任期号,那么候选人会承认领导人合法并回到跟随者状态。 如果此次 RPC 中的任期号比自己小,那么候选人就会拒绝这次的 RPC 并且继续保持候选人状态。

第三种可能的结果是候选人既没有赢得选举也没有输:如果有多个跟随者同时成为候选人,那么选票可能会被瓜分以至于没有候选人可以赢得大多数人的支持。当这种情况发生的时候,每一个候选人都会超时,然后通过增加当前任期号来开始一轮新的选举。然而,没有其他机制的话,选票可能会被无限的重复瓜分。

Raft 算法使用随机选举超时时间的方法来确保很少会发生选票瓜分的情况,就算发生也能很快的解决。

日志复制

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

Raft 算法保证所有已提交的日志条目都是持久化的并且最终会被所有可用的状态机执行。在领导人将创建的日志条目复制到大多数的服务器上的时候,日志条目就会被提交。

在正常的操作中,领导人和跟随者的日志保持一致性,所以附加日志 RPC 的一致性检查从来不会失败。然而,领导人崩溃的情况会使得日志处于不一致的状态(老的领导人可能还没有完全复制所有的日志条目)。这种不一致问题会在一系列的领导人和跟随者崩溃的情况下加剧。跟随者的日志可能和新的领导人不同的方式。跟随者可能会丢失一些在新的领导人中有的日志条目,他也可能拥有一些领导人没有的日志条目,或者两者都发生。丢失或者多出日志条目可能会持续多个任期。

如下图:

AA

在 Raft 算法中,领导人处理不一致是通过强制跟随者直接复制自己的日志来解决了。这意味着在跟随者中的冲突的日志条目会被领导人的日志覆盖。

要使得跟随者的日志进入和自己一致的状态,领导人必须找到最后两者达成一致的地方,然后删除从那个点之后的所有日志条目,发送自己的日志给跟随者。

Etcd的代理节点

Etcd针对Raft的角色模型进行了扩展,增加了Proxy角色。作为Proxy角色的节点不会参与Leader的选举,只是将所有接收到的用户查询和修改请求转发到任意一个Follower或者Leader节点上。

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

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

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

AA


Etcd集群构建

官方链接如下:https://coreos.com/etcd/docs/latest/v2/clustering.html

Etcd集群的建立有三种方式,分别是静态集群、基于Etcd自动发现服务的集群,以及基于DNS的服务发现构建集群。

静态集群

静态集群不依赖其他任何的服务,是相对稳定的一种方式。但是需要提前知道所有节点的固定的IP地址,因此灵活性也比较差。通常只用于能够为节点分配静态IP地址的环境,或者离线环境。

在启动整个集群之前,需要事先知道集群的大小,急群众各个节点的名称、地址和端口信息。使用inital-cluster参数进行etcd集群的启动。

每个etcd启动的时候,配置环境变量或者添加启动参数的方式如下:

#### 环境变量
ETCD_INITIAL_CLUSTER="t1=http://192.168.100.83:2380,t2=http://192.168.100.84:2380,t3=http://192.168.100.85:2380"
ETCD_INITIAL_CLUSTER_STATE="new"

#### 启动参数
--initial-cluster t1=http://192.168.100.83:2380,t2=http://192.168.100.84:2380,t3=http://192.168.100.85:2380 \
--initial-cluster-state new

注意:inital-cluster指定的URL必须和各个节点启动时设置的initial-advertise-peer-urls参数相同。

参数initial-advertise-peer-urls表示节点监听其他节点同步信号的地址。(advertise表示通告,同步的意思)

如果所在的网络配置了多个etcd集群,还需要使用inital-cluster-token参数为每个集群单独配置一个token认证。这样就可以确保每个集群和集群成员都拥有特定的ID。

通过制定参数listen-client-urls,来接收客户端的请求。

参数advertise-client-urls把当前运行的etcd的client URL地址通知给cluster中的其它成员。

参数listen-peer-urls表示,节点之间的信息同步,所使用的URL。

参数initial-cluster-state新创建的集群指定结果为new,如果是已有的集群,需要修改成existing

完整解释信息见 本站

官方文档解释见 configuration

完整的启动参数如下:

### t1机器,IP地址192.168.100.83
etcd --name t1 --initial-advertise-peer-urls http://192.168.100.83:2380 \
  --listen-peer-urls http://192.168.100.83:2380 \
  --listen-client-urls http://192.168.100.83:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://192.168.100.83:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster t1=http://192.168.100.83:2380,t2=http://192.168.100.84:2380,t3=http://192.168.100.85:2380 \
  --initial-cluster-state new

### t2机器,IP地址192.168.100.84
etcd --name t2 --initial-advertise-peer-urls http://192.168.100.84:2380 \
  --listen-peer-urls http://192.168.100.84:2380 \
  --listen-client-urls http://192.168.100.84:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://192.168.100.84:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster t1=http://192.168.100.83:2380,t2=http://192.168.100.84:2380,t3=http://192.168.100.85:2380 \
  --initial-cluster-state new

### t3机器,IP地址192.168.100.85
etcd --name t3 --initial-advertise-peer-urls http://192.168.100.85:2380 \
  --listen-peer-urls http://192.168.100.85:2380 \
  --listen-client-urls http://192.168.100.85:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://192.168.100.85:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster t1=http://192.168.100.83:2380,t2=http://192.168.100.84:2380,t3=http://192.168.100.85:2380 \
  --initial-cluster-state new

注意:在运行过程中,参数inital-cluster参数会被忽略。


Etcd自动发现模式

在某些时候,你可能不知道IP地址,比如你的网络中使用DHCP,或者使用云服务,那么IP是不能确定的。在这种情况下不能使用静态的配置,可以使用已有的etcd集群来启动一个新的。这个过程称之为"自动发现"。

通过自发现的方式启动etcd集群,需要事先准备一个etcd集群。

此外,自动发现URL应该仅仅提供集群的初始化工作。

使用公共的etcd自动发现服务

如果你没有一个可以访问的现有的集群,那么可以使用公共的自动发现服务discovery.etcd.io。可以使用newendpoint来创建一个私有的URL。

# curl https://discovery.etcd.io/new?size=3
https://discovery.etcd.io/b563aeb9d020cf171ff490330fd92efd

这将创建一个三个成员的集群,如果不指定size,那么默认就是3。如果超过3个,那么将自动转换为proxy节点。

然后要把这个私有的URL地址https://discovery.etcd.io/b563aeb9d020cf171ff490330fd92efd作为discovery参数来启动etcd。这样节点就会自动使用这个URL进行etcd的注册和发现服务。

--discovery https://discovery.etcd.io/b563aeb9d020cf171ff490330fd92efd

最终的启动参数如下:

### t1机器,IP地址192.168.100.83
etcd --name t1 --initial-advertise-peer-urls http://192.168.100.83:2380 \
  --listen-peer-urls http://192.168.100.83:2380 \
  --listen-client-urls http://192.168.100.83:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://192.168.100.83:2379 \
  --discovery https://discovery.etcd.io/b563aeb9d020cf171ff490330fd92efd


### t2机器,IP地址192.168.100.84
etcd --name t2 --initial-advertise-peer-urls http://192.168.100.84:2380 \
  --listen-peer-urls http://192.168.100.84:2380 \
  --listen-client-urls http://192.168.100.84:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://192.168.100.84:2379 \
  --discovery https://discovery.etcd.io/b563aeb9d020cf171ff490330fd92efd


### t3机器,IP地址192.168.100.85
etcd --name t1 --initial-advertise-peer-urls http://192.168.100.85:2380 \
  --listen-peer-urls http://192.168.100.85:2380 \
  --listen-client-urls http://192.168.100.85:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://192.168.100.85:2379 \
  --discovery https://discovery.etcd.io/b563aeb9d020cf171ff490330fd92efd

同样的,当完成了集群的初始化之后,这些信息就失去了作用。为了安全,在每次启动新的etcd集群的时候,请务必使用新的discovery token进行注册。如果初始化时启动的节点超过了指定的数量,多余的节点会自动转为Proxy模式。

使用自己的etcd自动发现

如果已经有一个etcd集群,加入etcd的URL为https://myetcd.local,首先可以自行如下命令设定集群的大小:

$ curl -X PUT https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=3

注意:这里指定集群成员的个数为3,如果大于3个,那么多余的节点会自动转换为Proxy节点。

然后要把这个URL地址https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/作为discovery参数来启动etcd。


DNS 自动发现

etcd还支持使用DNS SRV 记录进行启动。

参数--discovery-srv指定的是etcd服务器所在的域。

DNS中的SRV(服务资源位置)记录是由RFC2052规范定义的一种DNS记录。作用是将具体的服务器提供的服务类型与域名对应起来。

SRV记录看起来如下:

  • _etcd-server-ssl._tcp.example.com

  • _etcd-server._tcp.example.com

如果_etcd-server-ssl._tcp.example.com被发现,etcd将采用SSL方式启动。

帮助客户端自动发现etcd集群,可以使用如下类似的SRV记录:

  • _etcd-client._tcp.example.com

  • _etcd-client-ssl._tcp.example.com

开启DNS服务器上的SRV记录查询,并添加相应的域名记录。使得查询结果如下:

$ dig +noall +answer SRV _etcd-server._tcp.example.com
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra0.example.com.
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra1.example.com.
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra2.example.com.


$ dig +noall +answer SRV _etcd-client._tcp.example.com
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra0.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra1.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra2.example.com.

分别为各个域名配置相关的A记录,指向etcd核心节点对应的机器的IP。使得查询结果如下:

$ dig +noall +answer infra0.example.com infra1.example.com infra2.example.com
infra0.example.com.  300  IN  A  10.0.1.10
infra1.example.com.  300  IN  A  10.0.1.11
infra2.example.com.  300  IN  A  10.0.1.12

最后etcd的启动参数如下:

$ etcd --name infra0 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra0.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra0.example.com:2379 \
--listen-client-urls http://infra0.example.com:2379 \
--listen-peer-urls http://infra0.example.com:2380

当然,可以使用IP地址代替域名:

$ etcd --name infra0 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.10:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.10:2379 \
--listen-client-urls http://10.0.1.10:2379 \
--listen-peer-urls http://10.0.1.10:2380

etcd 客户端配置

DNS SRV 记录可以帮助客户端自动发现集群。

$ etcdctl --discovery-srv example.com set foo bar

使用SSL 构建安全的 etcd

参考链接 公钥基础设施(PKI)/CFSSL工具/etcd通信加密





如无特殊说明,文章均为本站原创,转载请注明出处
  • 转载请注明来源:Etcd集群构建
  • 本文永久连接地址: http://ibash.cc/frontend/article/70/