简介
Etcd是CoreOS基于Raft协议开发的分布式key-value存储,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)。
在分布式系统中,如何管理节点间的状态一直是一个难题,etcd像是专门为集群环境的服务发现和注册而涉及,它提供了数据TTL失效、数据改变监视、多值、目录监听、分布式锁原子操作等功能,可以方便的跟踪并管理集群节点的状态。
特点
- 简单:curl可访问的用户的API(HTTP + JSON)
- 安全:可选的SSL客户端证书认证
- 快速: 单实例每秒1000次写操作
- 可靠:使用Raft算法保证一致性
主要功能
- 基本的key-value存储
- 监听机制
- key的过期及续约机制, 用于监控和服务发现
- 原子Compare And Swap和Compare And Delete, 用于分布式锁和leader选举
写数据流程
- 当客户端对etcd发起请求的时候,如果etcd不是leader的状态而是follower,follower则会将请求转发leader; 如果是leader后, 会对其进行预检查,检查(配额、限速、鉴权【判断请求是否合法】、包大小【需要小于1.5M,过大则会拒绝】)。
- 如果请求本身是合法的,会将请求转发给KVServer处理。
- KVserver一致性模块进行数据处理,一致性模块是基于raft协议实现的,这时候的数据本身是处于unstable状态。
- 当leader该数据处理unstable状态后,会通过rpc通知其他follower也来同步该数据,并且leader本身会在数据同步到日志模块【wal日志, wal日志通过fsync落盘到磁盘中】。而其他follow在同步该数据的时候,本身完成的是步骤3和数据同步到日志模块,follower一致性模块数据变成commited状态,当完成了这些后通过上次rpc返回响应体给leader。
- leader在收到了超过半数集群本身确认后,更新MatchIndex, 一致性模块中数据本身由unstable变化成commited状态。这时候通过MVCC模块【treeIndex和BoltDB开源组件组成】进行状态机的写入,将数据同步到treeIndex【会更新modified版本[当前版本号], generations信息[创建的版本,当前版本数,过往的所有版本号]】。再通过BoltDB落盘到磁盘中。这时候一致性模块数据由commited变化为applied状态。【在这里如果没有要求数据强一致性,弱一致性的话,那么数据在commited状态就认为数据已经同步完成了】。
- 再通过heatbeat将数据同步到follower中MVCC模块中。最终完成数据的一致性。如下图所示。 【如果follower比leader落后好几个版本,leader会通过headbeat带到follower进行同步】。
架构流程
从上图可知,etcd 有 etcd Server、gRPC Server、存储相关的 MVCC 、Snapshot、WAL,以及 Raft 模块。
其中:
- etcd Server 用于对外接收和处理客户端的请求;
- gRPC Server 则是 etcd 与其他 etcd 节点之间的通信和信息同步;
- MVCC 即多版本控制,etcd 的存储模块,键值对的每一次操作行为都会被记录存储,这些数据底层存储在 BoltDB 数据库中;
- WAL 预写式日志,etcd 中的数据提交前都会记录到日志;
- Snapshot 快照,以防 WAL 日志过多,用于存储某一时刻 etcd 的所有数据;
- Snapshot 和 WAL 相结合,etcd 可以有效地进行数据存储和节点故障恢复等操作;
虽然 etcd 内部实现机制复杂,但对外提供了简单的 API 接口,方便客户端调用。我们可以通过 etcdctl 客户端命令行操作和访问 etcd 中的数据,或者通过HTTP API 接口直接访问 etcd。
etcd 中的数据结构很简单,它的数据存储其实就是键值对的有序映射。etcd 还提供了一种键值对监测机制,即 Watch 机制,客户端通过订阅相关的键值对,获取其更改的事件信息。Watch 机制实时获取 etcd 中的增量数据更新,使数据与 etcd 同步。
etcd 目前有 V2.x 和 V3.x 两个大版本。etcd V2 和 V3 是在底层使用同一套 Raft 算法的两个独立应用,但相互之间实现原理和使用方法上差别很大,接口不一样、存储不一样,两个版本的数据互相隔离。
至于由 etcd V2 升级到 etcd V3 的情况,原有数据只能通过 etcd V2 接口访问,V3 接口创建的数据只能通过新的 V3 的接口访问。
架构图:
==通常,etcd 会监听两个端口,默认是 2379 端口和 2380 端口。其中,2380 端口用于集群内部通信,主要涉及集群间数据同步、心跳、选举等。2379 端口用于与客户端通信,比如接收客户端发起的读/写数据请求。==
etcd 节点在部署的时候有两种运行模式:集群模式和代理模式。
当 etcd 节点以集群模式运行时,它会加入已有集群中,作为集群的一部分。也就是说,后续的心跳、数据同步、选举等它都会参与。
集群模式中的节点数一般采用奇数个。为什么呢?因为假如同时有两个 Candidate 发起选举,如果是偶数节点的话,可能存在两个 Candidate 获得相同票数。
这会导致什么问题?如果两个 Candidate 票数一样,就需要再次发起选举,而再次发起选举还是有一定概率出现票数一样,这会导致选举耗时较多,影响稳定性。所以,采用奇数个节点,能有效降低票数一样的概率,提升选举的效率。另外,使用奇数节点来部署,也能让 etcd 很好地处理分区容错问题。
当某个 etcd 节点以代理模式运行时,该节点负责将接收到的请求转发给 etcd 集群节点。目前 etcd 接口有 v2 和 v3 两个版本,其中 v2 是 HTTP 接口,v3 是 gRPC 接口。需要注意的是,代理模式只支持转发 v2 版本的请求,也就是只支持转发 HTTP 请求。
不过,由于 etcd v3 接口在性能、安全、稳定性等方面要比 v2 接口优秀很多,新项目倾向于使用 v3 接口,老项目也逐渐从 v2 接口迁移到 v3 接口。也就是说,代理模式以后可能逐渐被淘汰掉。
单点部署
ETCD_VERSION='3.5.4'
wget https://github.com/etcd-io/etcd/releases/download/v${ETCD_VERSION}/etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
#如果采用离线安装的方式,那么需要将下载的包拷贝
到目标主机上进行解压安装
tar -zxvf etcd-v3.5.4-linux-amd64.tar.gz --strip-components=1 -C /usr/local/bin etcd-v3.5.4-linux-amd64/etcd{,ctl}
# start etcd server,这个只能在自己的设备上登录,其他设备无法进行连接
nohup etcd &
# 使用这个命令启动,外网可以访问登录
etcd --listen-client-urls 'http://0.0.0.0:2379' --advertise-client-urls 'http://0.0.0.0:2379'
#创建service
vim /usr/lib/systemd/system/etcd.service
[Unit]
Description=EtcdService
Documentation=https://coreos.com/etcd/docs/latest/
After=network.target
[Service]
Type=notify
ExecStart=/usr/local/bin/etcd
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
Alias=etcd3.service
systemctl daemon-reload && systemctl enable --now etcd
数据操作
etcdctl
- put(key,value)/delete(key),创建/修改、删除
- get(key)/get(keyfrom,keyend),获取
- watch(key/keyprefix),对key进行监视,可第一时间知道修改的内容
- transactions(if/then/else ops).commit(),判断并根据判断的结果执行不同的操作
- leases: grant / revoke / keepalive,租约管理
# 插入数据
[root@node2 ~]# etcdctl put key1 value1
OK
# 查看数据
[root@node2 ~]# etcdctl get key1
key1
value1
# 批量插入数据
[root@node2 ~]# cat createdata
etcdctl put key0 value0
etcdctl put key1 value1
.....
etcdctl put key8 value8
etcdctl put key9 value9
[root@node2 ~]# cat createdata | sh -x
+ etcdctl put key0 value0
OK
+ etcdctl put key1 value1
OK
......
+ etcdctl put key8 value8
OK
+ etcdctl put key9 value9
OK
[root@node2 ~]# etcdctl get key5
key5
value5
# 查询一个区间key的数据,左开右闭,不包含结束key
[root@node2 ~]# etcdctl get key2 key6
key2
value2
key3
value3
key4
value4
key5
value5
# 指定key的前缀进行查询,列出所有key*的数据
[root@node2 ~]# etcdctl get key --prefix
key0
value0
key1
value1
........
key8
value8
key9
value9
# 删除一个key-value
[root@node2 ~]# etcdctl del key9
数据信息详解
# 以json方式详细查看一个key的信息
[root@node2 ~]# etcdctl get key0 -w json | jq
{
"header": {
"cluster_id": 14841639068965180000,
"member_id": 10276657743932975000,
"revision": 12,
"raft_term": 2
},
"kvs": [
{
"key": "a2V5MA==",
"create_revision": 3,
"mod_revision": 3,
"version": 1,
"value": "dmFsdWUw"
}
],
"count": 1
}
对上述输出进行分析:
- count:返回数据的数量
- header:消息头,描述在etcd集群中全局的信息
- cluster_id:集群id
- member_id:
- revision:全局版本号,每更改一次数据,此数据会单项递增1。如此时执行etcdctl put key100 value100。再次查看可方法revision变成13。
- raft_term:leader每变更一次,此数据会单项递增1。
- kvs:键值信息
- key:键,base64
- create_revision:创建version
- mod_revision:修改version,如果对key0进行修改,会与revision值保持一致
- version:版本号,如果对key0进行修改,那么此值+1
- value:值,base64
参考链接
https://blog.csdn.net/wohu1104/article/details/115764681
https://blog.csdn.net/weixin_39540280/article/details/122007678