kubernetes核心组件之etcd详解
一 、etcd组件
etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现。etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性。Raft是一个来自Stanford的新的一致性算法,适用于分布式系统的日志复制,Raft通过选举的方式来实现一致性,在Raft中,任何一个节点都可能成为Leader。Google的容器集群管理系统Kubernetes、开源PaaS平台Cloud Foundry和CoreOS的Fleet都广泛使用了etcd。etcd的特性如下:
- 简单: curl可访问的用户的API(HTTP+JSON)
- 安全: 可选的SSL客户端证书认证
- 快速: 单实例每秒 1000 次写操作
- 可靠: 使用Raft保证一致性
ETCD概念词汇表:
概念 | 解释 |
---|---|
Raft | etcd所采用的保证分布式系统强一致性的算法。 |
Node | 一个Raft状态机实例。 |
Member | 一个etcd实例。它管理着一个Node,并且可以为客户端请求提供服务。 |
Cluster | 由多个Member构成可以协同工作的etcd集群。 |
Peer | 对同一个etcd集群中另外一个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来定位数据。 |
1、etcd架构设计
etcd主要分为四个部分:
- HTTP Server: 用于处理用户发送的API请求以及其他etcd节点的同步与心跳信息请求
- Store: 用于处理 etcd 支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等等,是 etcd 对用户提供的大多数 API 功能的具体实现。
- Raft: Raft 强一致性算法的具体实现,是 etcd 的核心。
- WAL:Write Ahead Log(预写式日志/日志先行),是 etcd 的数据存储方式,也是一种实现事务日志的标准方法。etcd通过 WAL 进行持久化存储,所有的数据提交前都会事先记录日志。Snapshot 是为了防止数据过多而进行的状态快照;Entry 表示存储的具体日志内容。
- 流程分析:通常,一个用户的请求发送过来,会经由HTTP Server转发给Store进行具体的事务处理,如果涉及到节点的修改,则交给Raft模块进行状态的变更、日志的记录,然后再同步给别的etcd节点以确认数据提交,最后进行数据的提交,再次同步。
1.1 工作原理
etcd使用Raft协议来维护集群内各个节点状态的一致性。简单说,ETCD集群是一个分布式系统,由多个节点相互通信构成整体对外服务,每个节点都存储了完整的数据,并且通过Raft协议保证每个节点维护的数据是一致的。
- Raft算法
Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。一致性算法允许一组机器像一个整体一样工作,即使其中一些机器出现故障也能够继续工作下去。正因为如此,一致性算法在构建可信赖的大规模软件系统中扮演着重要的角色。Raft算法分为三部分,分别是Leader选举、日志复制和安全性。
- Leader选举:包括
Raft 状态机
和Raft算法中的Term(任期)
两部分。
A) Raft 状态机
Raft集群中的每个节点都处于一种基于角色的状态机中。具体来说,Raft定义了节点的三种角色: Follower、Candidate和Leader。
1、Leader(领导者): Leader节点在集群中有且仅能有一个,它负责向所有的Follower节点同步日志数据。
2、Follower(跟随者): Follower节点从Leader节点获取日志,提供数据查询功能,并将所有修改请求转发给Leader节点。(注意:由于proxy模式的本职就是启一个HTTP代理服务器,所以Etcd的代理节点(proxy)即作为Proxy角色的节点不会参与Leader的选举
,只是将所有接收到的用户查询和修改请求转发到任意一个Follower或者Leader节点上。)
3、Candidate(候选者): 当集群中的Leader节点不存在或者失联之后,其他Follower节点转换为Candidate,然后开始新的Leader节点选举。
这三种角色状态之间的转换,如下图:
B)Raft算法中的Term(任期)
Raft会把时间分割成任意长度的任期,并且任期用连续的整数来标记。每一段任期都是从一次选举开始,一个或者多个候选人尝试成为领导者。如果一个候选人赢得选举,然后他就会在接下来的任期中充当Leader的职责。在某些情况下,一次选举会造成选票瓜分,这样,这一个任期将没有Leader。如果没有Leader,那么新的一轮选举就马上开始,也就是新的任期就会开始。Raft保证了在一个Term任期内,有且只有一个Leader。
- 日志复制:是指主节点将每次操作形成日志条目,并持久化到本地磁盘,然后通过网络IO发送给其他节点。
一旦一个领导人被选举出来,他就开始为客户端提供服务。客户端的每一个请求都包含一条被复制状态机执行的指令。领导人把这条指令作为一条新的日志条目附加到日志中去,然后并行的发起附加条目 RPCs 给其他的服务器,让他们复制这条日志条目。
Raft 算法保证所有已提交的日志条目都是持久化的并且最终会被所有可用的状态机执行。当主节点收到包括自己在内超过半数节点成功返回,那么认为该日志是可提交的(committed),并将日志输入到状态机,将结果返回给客户端。
在正常的操作中,领导人和跟随者的日志保持一致性,所以附加日志 RPC 的一致性检查从来不会失败。然而,领导人崩溃的情况会使得日志处于不一致的状态(老的领导人可能还没有完全复制所有的日志条目)。这种不一致问题会在一系列的领导人和跟随者崩溃的情况下加剧。跟随者的日志可能和新的领导人不同的方式。跟随者可能会丢失一些在新的领导人中有的日志条目,他也可能拥有一些领导人没有的日志条目,或者两者都发生。丢失或者多出日志条目可能会持续多个任期。这就引出了另一个部分,就是安全性。 - 安全性:选主以及日志复制并不能保证节点间数据一致。试想,当一个某个节点挂掉了,一段时间后再次重启,并当选为主节点。而在其挂掉这段时间内,集群若有超过半数节点存活,集群会正常工作,那么会有日志提交。这些提交的日志无法传递给挂掉的节点。当挂掉的节点再次当选主节点,它将缺失部分已提交的日志。在这样场景下,按Raft协议,它将自己日志复制给其他节点,会将集群已经提交的日志给覆盖掉。这显然是错误的。
其他协议解决这个问题的办法是,新当选的主节点会询问其他节点,和自己数据对比,确定出集群已提交数据,然后将缺失的数据同步过来。这个方案有明显缺陷,增加了集群恢复服务的时间(集群在选举阶段不可服务),并且增加了协议的复杂度。Raft解决的办法是,在选主逻辑中,对能够成为主的节点加以限制,确保选出的节点已定包含了集群已经提交的所有日志。如果新选出的主节点已经包含了集群所有提交的日志,那就不需要从和其他节点比对数据了。简化了流程,缩短了集群恢复服务的时间。
2、etcd应用场景
etcd应用场景很多,主要的有以下六种:服务发现、消息发布与订阅(配置中心)、负载均衡(集群管理)、分布式锁、分布式队列、集群监控与LEADER竞选。
2.1 服务发现
服务发现(Service Discovery)要解决的是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务如何才能找到对方并建立连接。
从本质上说,服务发现就是想要了解集群中是否有进程在监听udp或tcp端口,并且通过名字就可以进行查找和连接。要解决服务发现的问题,需要有下面三大支柱,缺一不可。
- 一个强一致性、高可用的服务存储目录。基于Raft算法的etcd天生就是这样一个强一致性高可用的服务存储目录。
- 一种注册服务和监控服务健康状态的机制。用户可以在etcd中注册服务,并且对注册的服务设置key TTL,定时保持服务的心跳以达到监控健康状态的效果。
- 一种查找和连接服务的机制。通过在etcd指定的主题下注册的服务也能在对应的主题下查找到。为了确保连接,我们可以在每个服务机器上都部署一个proxy模式的etcd,这样就可以确保能访问etcd集群的服务都能互相连接。
2.2 消息发布与订阅(配置中心)
在分布式系统中,最为适用的组件间通信方式是消息发布与订阅机制。
具体而言,即构建一个配置共享中心,数据提供者在这个配置中心发布消息,而消息使用者则订阅他们关心的主题,一旦相关主题有消息发布,就会实时通知订阅者。
通过这种方式可以实现分布式系统配置的集中式管理与实时动态更新。
2.3 负载均衡(集群管理)
利用etcd维护一个负载均衡节点表。etcd可以监控一个集群中多个节点的状态,当有一个请求发过来后,可以轮询式地把请求转发给存活着的多个节点。
类似KafkaMQ,通过Zookeeper来维护生产者和消费者的负载均衡。同样也可以用etcd来做Zookeeper的工作。
2.4 分布式锁
因为etcd使用Raft算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。
锁服务有以下两种使用方式:
- 保持独占,即所有试图获取锁的用户最终只有一个可以得到。etcd为此提供了一套实现分布式锁原子操作CAS(CompareAndSwap)的API。通过设置prevExist值,可以保证在多个节点同时创建某个目录时,只有一个成功,而该用户即可认为是获得了锁。
- 控制时序,即所有试图获取锁的用户都会进入等待队列,获得锁的顺序是全局唯一的,同时决定了队列执行顺序。etcd为此也提供了一套API(自动创建有序键),对一个目录建值时指定为POST动作,这样etcd会自动在目录下生成一个当前最大的值为键,存储这个新的值(客户端编号)。同时还可以使用API按顺序列出所有当前目录下的键值。此时这些键的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号。
2.5 分布式队列
在保证队列达到某个条件时再统一按顺序执行。这种方法的实现可以在/queue这个目录中另外建立一个/queue/condition节点。
- condition可以表示队列大小。比如一个大的任务需要很多小任务就绪的情况下才能执行,每次有一个小任务就绪,就给这个condition数字加1,直到达到大任务规定的数字,再开始执行队列里的一系列小任务,最终执行大任务。
- condition可以表示某个任务在不在队列。这个任务可以是所有排序任务的首个执行程序,也可以是拓扑结构中没有依赖的点。通常,必须执行这些任务后才能执行队列中的其他任务。
- condition还可以表示其它的一类开始执行任务的通知。可以由控制程序指定,当condition出现变化时,开始执行队列任务。
2.6 集群监控与LEADER竞选
通过etcd来进行监控实现起来非常简单并且实时性强,用到了以下两点特性。
- Watcher机制,当某个节点消失或有变动时,Watcher会第一时间发现并告知用户。
- 节点可以设置TTL key,比如每隔30s向etcd发送一次心跳使代表该节点仍然存活,否则说明节点消失。
这样就可以第一时间检测到各节点的健康状态,以完成集群的监控要求。
3、etcd启动参数详解
- 2379和2380为etcd在IANA 的注册端口,以前为私有端口4001和7001。
3.1 member flags
参数 | 使用说明 |
---|---|
--name 'default’ | 本member的名字 |
--data-dir '${name}.etcd’ | 指定节点的数据存储目录,这些数据包括节点ID,集群ID,集群初始化配置,Snapshot文件,若未指定-wal-dir,还会存储WAL文件;如果不指定会用缺省目录。 |
--listen-peer-urls http://0.0.0.0:2380 | 本member侧使用,用于监听其他member发送信息的地址。ip为全0代表监听本member侧所有接口 |
--listen-client-urls http://0.0.0.0:2379 | 本member侧使用,用于监听etcd客户发送信息的地址。ip为全0代表监听本member侧所有接口 |
--wa |