参考:

etcd 是 CoreOS 团队于 2013 年 6 月发起的开源项目,它的目标是构建一个高可用的分布式键值 (key-value) 数据库。etcd 内部采用raft共识算法作为一致性算法,etcd 基于 Go 语言实现。

raft共识算法是一套通过选举主节点来实现分布式系统一致性的算法。

概述

特点:

  • 简单:安装配置简单,而且提供了 HTTP API 进行交互,使用也很简单
  • 安全:支持 SSL 证书验证
  • 快速:根据官方提供的 benchmark 数据,单实例支持每秒 2k + 读操作
  • 可靠:采用 raft 算法,实现分布式系统数据的可用性和一致性

术语

术语 说明
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 开始竞选。
Term 某个节点成为 Leader 到下一次竞选时间,称为一个 Term。
Index 数据项编号。Raft 中通过 Term 和 Index 来定位数据。

数据读写顺序

为了保证数据的强一致性,etcd 集群中所有的数据流向都是一个方向,从 Leader (主节点)流向 Follower,也就是所有 Follower 的数据必须与 Leader 保持一致,如果不一致会被覆盖。

用户对于 etcd 集群所有节点进行读写

  • 读取:由于集群所有节点数据是强一致性的,读取可以从集群中随便哪个节点进行读取数据
  • 写入:etcd 集群有 leader,如果写入往 leader 写入,可以直接写入,然后然后 Leader 节点会把写入分发给所有 Follower,如果往 follower 写入,然后 Leader 节点会把写入分发给所有 Follower

leader 选举

假设三个节点的集群,三个节点上均运行 Timer(每个 Timer 持续时间是随机的),Raft 算法使用随机 Timer 来初始化 Leader 选举流程,第一个节点率先完成了 Timer,随后它就会向其他两个节点发送成为 Leader 的请求,其他节点接收到请求后会以投票回应然后第一个节点被选举为 Leader。

成为 Leader 后,该节点会以固定时间间隔向其他节点发送通知,确保自己仍是 Leader。有些情况下当 Follower 们收不到 Leader 的通知后,比如说 Leader 节点宕机或者失去了连接,其他节点会重复之前选举过程选举出新的 Leader。

判断数据是否写入

etcd 认为写入请求被 Leader 节点处理并分发给了多数节点后,就是一个成功的写入。那么多少节点如何判定呢,假设总结点数是 N,那么多数节点 Quorum=N/2+1。关于如何确定 etcd 集群应该有多少个节点的问题,上图的左侧的图表给出了集群中节点总数 (Instances) 对应的 Quorum 数量,用 Instances 减去 Quorom 就是集群中容错节点(允许出故障的节点)的数量。

所以在集群中推荐的最少节点数量是 3 个,因为 1 和 2 个节点的容错节点数都是 0,一旦有一个节点宕掉整个集群就不能正常工作了。

架构

架构如图:

img

从 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 节点以确认数据提交,最后进行数据的提交,再次同步。

应用场景

服务注册与发现

etcd 可以用于服务的注册与发现

  • 前后端业务注册发现

img

中间价已经后端服务在 etcd 中注册,前端和中间价可以很轻松的从 etcd 中发现相关服务器然后服务器之间根据调用关系相关绑定调用

  • 多组后端服务器注册发现

img

后端多个无状态相同副本的 app 可以同事注册到 etcd 中,前端可以通过 haproxy 从 etcd 中获取到后端的 ip 和端口组,然后进行请求转发,可以用来故障转移屏蔽后端端口已经后端多组 app 实例。

消息发布与订阅

img

etcd 可以充当消息中间件,生产者可以往 etcd 中注册 topic 并发送消息,消费者从 etcd 中订阅 topic,来获取生产者发送至 etcd 中的消息。

负载均衡

img

后端多组相同的服务提供者可以经自己服务注册到 etcd 中,etcd 并且会与注册的服务进行监控检查,服务请求这首先从 etcd 中获取到可用的服务提供者真正的 ip:port,然后对此多组服务发送请求,etcd 在其中充当了负载均衡的功能。

分部署通知与协调

img

  • 当 etcd watch 服务发现丢失,会通知服务检查
  • 控制器向 etcd 发送启动服务,etcd 通知服务进行相应操作
  • 当服务完成 work 会讲状态更新至 etcd,etcd 对应会通知用户

分布式锁

img

当有多个竞争者 node 节点,etcd 作为总控,在分布式集群中与一个节点成功分配 lock。

分布式队列

img

有对个 node,etcd 根据每个 node 来创建对应 node 的队列,根据不同的队列可以在 etcd 中找到对应的 competitor。

集群与监控与 Leader 选举

img

etcd 可以根据 raft 算法在多个 node 节点来选举出 leader。

安装部署

单机部署

安装

1
2
3
4
yum install epel-release -y
yum install etcd -y
systemc start etcd
systemctl enable etcd

配置说明

  • 默认配置文件路径:/etc/etcd/etcd.conf
  • 默认数据存放路径:/var/lib/etcd/default.etcd
  • 默认NAME:default
  • HTTP API服务:http://localhost:2379
  • hearbeat:100ms
  • election超时:1000ms

常用命令

etcdctl命令常用选项:

1
2
3
4
5
6
7
8
9
--debug 输出CURL命令,显示执行命令的时候发起的请求
--no-sync 发出请求之前不同步集群信息
--output, -o 'simple' 输出内容的格式(simple 为原始信息,json 为进行json格式解码,易读性好一些)
--peers, -C 指定集群中的同伴信息,用逗号隔开(默认为: "127.0.0.1:4001")
--cert-file HTTPS下客户端使用的SSL证书文件
--key-file HTTPS下客户端使用的SSL密钥文件
--ca-file 服务端使用HTTPS时,使用CA文件进行验证
--help, -h 显示帮助命令信息
--version, -v 打印版本信息

数据库操作

数据库操作围绕对键值和目录的CRUD完整生命周期的管理。

etcd在键的组织上采用了层次化的空间结构(类似于文件系统中目录的概念),用户指定的键可以为单独的名字,如:testkey,此时实际上放在根目录/下面,也可以为指定目录结构,如/cluster1/node2/testkey,则将创建相应的目录结构。

Tips:CRUD即Create,Read,Update,Delete是符合REST风格的一套API操作。

  • set:指定某个键的值。例如:
1
2
$ etcdctl set /testdir/testkey "Hello world"
Hello world

支持的选项包括:

1
2
3
--ttl '0' 该键值的超时时间(单位为秒),不配置(默认为0)则永不超时
--swap-with-value value 若该键现在的值是value,则进行设置操作
--swap-with-index '0' 若该键现在的索引值是指定索引,则进行设置操作
  • get

获取指定键的值。例如:

1
2
$ etcdctl get /testdir/testkey
Hello world

当键不存在时,则会报错。例如:

1
2
$ etcdctl get /testdir/testkey2
Error: 100: Key not found (/testdir/testkey2) [5]

支持的选项为:

1
2
--sort 对结果进行排序
--consistent 将请求发给主节点,保证获取内容的一致性。
  • update

当键存在时,更新值内容。例如:

1
2
$ etcdctl update /testdir/testkey "Hello"
Hello

当键不存在时,则会报错。例如:

1
2
$ etcdctl update /testdir/testkey2 "Hello"
Error: 100: Key not found (/testdir/testkey2) [6]

支持的选项为:

1
--ttl '0' 超时时间(单位为秒),不配置(默认为 0)则永不超时。
  • rm

删除某个键值。例如:

1
2
$ etcdctl rm /testdir/testkey
PrevNode.Value: Hello

当键不存在时,则会报错。例如:

1
2
$ etcdctl rm /testdir/testkey
Error: 100: Key not found (/testdir/testkey) [7]

支持的选项为:

1
2
3
4
--dir 如果键是个空目录或者键值对则删除
--recursive 删除目录和所有子键
--with-value 检查现有的值是否匹配
--with-index '0'检查现有的index是否匹配
  • mk

如果给定的键不存在,则创建一个新的键值。例如:

1
2
$ etcdctl mk /testdir/testkey "Hello world"
Hello world

当键存在的时候,执行该命令会报错,例如:

1
2
$ etcdctl mk /testdir/testkey "Hello world"
Error: 105: Key already exists (/testdir/testkey) [8]

支持的选项为:

1
--ttl '0'  超时时间(单位为秒),不配置(默认为 0)。则永不超时
  • mkdir

如果给定的键目录不存在,则创建一个新的键目录。例如:

1
$ etcdctl mkdir testdir2

当键目录存在的时候,执行该命令会报错,例如:

1
2
$ etcdctl mkdir testdir2
Error: 105: Key already exists (/testdir2) [9]

支持的选项为:

1
--ttl '0' 超时时间(单位为秒),不配置(默认为0)则永不超时。
  • setdir

创建一个键目录。如果目录不存在就创建,如果目录存在更新目录TTL。

1
$ etcdctl setdir testdir3

支持的选项为:

1
--ttl '0' 超时时间(单位为秒),不配置(默认为0)则永不超时。
  • updatedir

更新一个已经存在的目录。

1
$ etcdctl updatedir testdir2

支持的选项为:

1
--ttl '0' 超时时间(单位为秒),不配置(默认为0)则永不超时。
  • rmdir

删除一个空目录,或者键值对。

1
2
$ etcdctl setdir dir1
$ etcdctl rmdir dir1

若目录不空,会报错:

1
2
3
4
$ etcdctl set /dir/testkey hi
hi
$ etcdctl rmdir /dir
Error: 108: Directory not empty (/dir) [17]
  • ls

列出目录(默认为根目录)下的键或者子目录,默认不显示子目录中内容。

例如:

1
2
3
4
5
6
7
$ etcdctl ls
/testdir
/testdir2
/dir

$ etcdctl ls dir
/dir/testkey

支持的选项包括:

1
2
3
--sort 将输出结果排序
--recursive 如果目录下有子目录,则递归输出其中的内容
-p 对于输出为目录,在最后添加/进行区分

非数据库操作

  • backup

备份etcd的数据。

1
$ etcdctl backup --data-dir /var/lib/etcd  --backup-dir /home/etcd_backup

支持的选项包括:

1
2
--data-dir  etcd的数据目录
--backup-dir 备份到指定路径
  • watch

监测一个键值的变化,一旦键值发生更新,就会输出最新的值并退出。

例如:用户更新testkey键值为Hello watch。

1
2
3
4
5
6
$ etcdctl get /testdir/testkey
Hello world
$ etcdctl set /testdir/testkey "Hello watch"
Hello watch
$ etcdctl watch testdir/testkey
Hello watch

支持的选项包括:

1
2
3
--forever  一直监测直到用户按CTRL+C退出
--after-index '0' 在指定index之前一直监测
--recursive 返回所有的键值和子键值
  • exec-watch

监测一个键值的变化,一旦键值发生更新,就执行给定命令。

例如:用户更新testkey键值。

1
2
$ etcdctl exec-watch testdir/testkey -- sh -c 'ls'
config Documentation etcd etcdctl README-etcdctl.md README.md READMEv2-etcdctl.md

支持的选项包括:

1
2
--after-index '0' 在指定 index 之前一直监测
--recursive 返回所有的键值和子键值
  • member

通过listaddremove命令列出、添加、删除etcd实例到etcd集群中。

备份

1
etcdctl backup --data-dir /var/lib/etcd/default.etcd --backup-dir /opt/etcd_backup