在云计算时代,如何让服务快速透明地接入到计算集群中,如何让共享配置信息快速被集群中的所有机器发现,更为重要的是,如何构建这样一套高可用、安全、易于部署以及响应快速的服务集群,已经成为了迫切需要解决的问题。 etcd作为一个基于Raft算法的分布式、高可用、强一致性的服务发现存储仓库,给这类问题带来了福音。作为一个受到ZooKeeper与doozer启发而催生的项目,除了拥有与之类似的功能外,更专注于以下四点: 简单:基于HTTP+JSON的API(通过curl就可以轻松使用) 安全:可选SSL客户认证机制 快速:每个实例每秒支持一千次写操作 可信:使用Raft算法充分实现了分布式 分布式系统中的数据分为两类,在etcd使用场景中分别进行如下处理: 控制数据:默认处理控制数据 应用数据:可以处理数据量很小,但是更新访问频繁的应用数据 服务发现 服务发现要解决的是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务,要如何才能找到对方并建立连接。 本质上来说,服务发现就是想要了解集群中是否有进程在监听udp或tcp端口,并且通过名字就可以查找和连接。 要解决服务发现的问题,需要有下面三大支柱,缺一不可: 一个强一致性、高可用的服务存储目录:基于Raft算法的etcd天生就是一个强一致性高可用的服务存储目录 一种注册服务和监控服务健康状态的机制:用户可以在etcd中注册服务并设置TTL key,定时保持服务的心跳以达到监控健康状态的效果 一种查找和连接服务的机制:在etcd指定的主题下注册的服务也能在对应的主题下查找到。为了确保连接,可以在每台服务器上都部署一个Proxy模式的etcd,这样就可以确保能访问etcd集群的服务都能互相连接 例子 微服务协同工作架构中,服务动态添加 > 通过服务发现机制,在etcd中注册某个服务名字的目录,在该目录下存储可用的服务节点的IP。在使用服务的过程中,只要从服务目录下查找可用的服务节点去使用即可。 PaaS平台中应用多实例与实例故障重启透明化 > PaaS平台中的应用一般都有多个实例,通过域名,不仅可以透明的对这多个实例进行访问,而且还可以做到负载均衡。但是应用的某个实例随时都有可能故障重启,这时就需要动态的配置域名解析(路由)中的信息。通过etcd的服务发现功能就可以轻松解决这个动态配置的问题。 消息(控制数据或配置信息)发布与订阅 在分布式系统中,最适用的一种组件间通信方式就是消息发布与订阅。即构建一个配置共享中心,消息提供者在配置中心发布消息,消息使用者则订阅他们关心的主题,一旦主题有消息发布,就会实时通知订阅者。 通过这种方式可以做到分布式系统配置的集中式管理与动态更新: 应用中用到的一些配置信息放到etcd上进行集中管理。 > 这类场景的使用方式通常是这样:应用在启动的时候主动从etcd获取一次配置信息,同时,在etcd节点上注册一个Watcher并等待,以后每次配置有更新的时候,etcd都会实时通知订阅者,以此达到获取最新配置信息的目的。 分布式搜索服务中,索引的元信息和服务器集群机器的节点状态存放在etcd中,供各个客户端订阅使用。使用etcd的TTL key功能可以确保机器状态是实时更新的。 分布式日志收集系统。核心工作是收集分布在不同机器的日志。 > 收集器通常是按照应用(或主题)来分配收集任务单元,因此可以在etcd上创建一个以应用(主题)命名的目录P,并将这个应用(主题相关)的所有机器ip,以子目录的形式存储到目录P上,然后设置一个etcd递归的Watcher,递归式的监控应用(主题)目录下所有信息的变动。这样就实现了机器IP(消息)变动的时候,能够实时通知到收集器调整任务分配。 系统中信息需要动态获取与人工干预修改信息请求内容的情况。通常是暴露出接口(例如JMX接口),来获取一些运行时的信息。引入etcd之后,只要将这些信息存放到指定的etcd目录中即可,etcd的这些目录就可以通过HTTP的接口在外部访问。 负载均衡 分布式系统中,为了保证服务的高可用以及数据的一致性,通常都会把数据和服务部署多份,以此达到对等服务,即使其中的某一个服务失效了,也不影响使用。 由此带来的坏处是数据写入性能下降, 而好处则是数据访问时的负载均衡。 因为每个对等服务节点上都存有完整的数据,所以用户的访问流量就可以分流到不同的机器上。 etcd本身分布式架构存储的信息访问支持负载均衡。etcd集群化以后,每个etcd的核心节点都可以处理用户的请求。 > 把数据量小但是访问频繁的消息数据直接存储到etcd中,如业务系统中常用的二级代码表(在表中存储代码,在etcd中存储代码所代表的具体含义,业务系统调用查表的过程,就需要查找表中代码的含义)。 利用etcd维护一个负载均衡节点表。 > etcd可以监控一个集群中多个节点的状态,当有一个请求发过来后,可以轮询式的把请求转发给存活着的多个状态。类似KafkaMQ,通过ZooKeeper来维护生产者和消费者的负载均衡。同样也可以用etcd来做ZooKeeper的工作。 分布式锁 因为etcd使用Raft算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。 锁服务有两种使用方式: 一是保持独占 二是控制时序 保持独占,即所有获取锁的用户最终只有一个可以得到。etcd为此提供了一套实现分布式锁原子操作CAS(CompareAndSwap)的API。通过设置prevExist值,可以保证在多个节点同时去创建某个目录时,只有一个成功。而创建成功的用户就可以认为是获得了锁。 控制时序,即所有想要获得锁的用户都会被安排执行,但是获得锁的顺序也是全局唯一的,同时决定了执行顺序。etcd为此也提供了一套API(自动创建有序键),对一个目录建值时指定为POST动作,这样etcd会自动在目录下生成一个当前最大的值为键,存储这个新的值(客户端编号)。同时还可以使用API按顺序列出所有当前目录下的键值。此时这些键的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号。 集群监控与Leader竞选 集群监控 通过etcd来进行监控实现起来非常简单并且实时性强。 在Watcher机制中,当某个节点消失或有变动时,Watcher会第一时间发现并告知用户。 节点可以设置key TTL,比如每隔30s发送一次心跳代表该机器存活的节点存在,否则节点消失。 这样就可以第一时间检测到各节点的健康状态,以完成集群的监控要求。 Leader竞选 使用分布式锁,可以完成Leader竞选。这种场景通常是一些长时间CPU计算或者使用IO操作的机器,只需要竞选出的Leader计算或处理一次,就可以把结果复制给其他的Follower。从而避免重复劳动,节省计算资源。 这个的经典场景是搜索系统中建立全量索引。如果每个机器都进行一遍索引的建立,不但耗时而且建立索引的一致性不能保证。通过在etcd的CAS机制同时创建一个节点,创建成功的机器作为Leader,进行索引计算,然后把计算结果分发到其它节点。 为什么是etcd而不是zookeeper etcd实现的上述功能,ZooKeeper都能实现。那么为什么要用etcd而非直接使用ZooKeeper呢? ZooKeeper缺点 复杂。 ZooKeeper的部署维护复杂,管理员需要掌握一系列的知识和技能; Paxos强一致性算法也是素来以复杂难懂而闻名于世; ZooKeeper的使用复杂,需要安装客户端,官方只提供了Java和C两种语言的接口。 Java编写。这里不是对Java有偏见,而是Java本身就偏向于重型应用,它会引入大量的依赖。而运维人员则普遍希望保持强一致、高可用的机器集群尽可能简单,维护起来也不易出错。 发展缓慢。Apache基金会项目特有的“ApacheWay”在开源界饱受争议,其中一大原因就是由于基金会庞大的结构以及松散的管理导致项目发展缓慢。 etcd优点 简单。使用Go语言编写部署简单;使用HTTP作为接口使用简单;使用Raft算法保证强一致性让用户易于理解。 数据持久化。etcd默认数据一更新就进行持久化。 安全。etcd支持SSL客户端安全认证。 etcd作为一个年轻的项目,正处于高速迭代和开发中,这既是一个优点,也是一个缺点。优点是它的未来具有无限的可能性,缺点是无法得到大项目长时间使用的检验。然而,目前CoreOS、Kubernetes和CloudFoundry等知名项目均在生产环境中使用了etcd,所以总的来说,etcd值得尝试。
架构 etcd 主要分为四个部分: HTTP Server: 用于处理用户发送的 API 请求以及其它 etcd 节点的同步与心跳信息请求。 Store:用于处理 etcd 支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等等,是 etcd 对用户提供的大多数 API 功能的具体实现。 Raft:Raft 强一致性算法的具体实现,是 etcd 的核心。 WAL:Write Ahead Log(预写式日志),是 etcd 的数据存储方式。除了在内存中存有所有数据的状态以及节点的索引以外,etcd 就通过 WAL 进行持久化存储。WAL 中,所有的数据提交前都会事先记录日志。Snapshot 是为了防止数据过多而进行的状态快照;Entry 表示存储的具体日志内容。 通常,一个用户的请求发送过来,会经由 HTTP Server 转发给 Store 进行具体的事务处理,如果涉及到节点的修改,则交给 Raft 模块进行状态的变更、日志的记录,然后再同步给别的 etcd 节点以确认数据提交,最后进行数据的提交,再次同步。