searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

ETCD简介

2023-10-13 07:12:10
40
0

1、初认ETCD

ETCD是SoreOs公司发布的一个分布式的、高可用的、key-value存储的数据库。基于Go语言实现,k8s中也使用了ETCD作为数据库。主要用于共享配置和服务发现。相对于zookeeper采用的Paxos,ETCD采用的是Raft算法,该算法具备的性能更佳、数据一致性强等优点。

ETCD存储数据量默认为2GB,最大可以设置为8GB。针对ETCD存储数据量较小的特性,故建议ETCD最好作为热点数据存储的数据库使用,而不能作为应用数据持久化的数据库使用。常见的使用场景如下:

  • 场景1:服务发现(Service Discovery)
  • 场景2:消息发布与订阅
  • 场景3:负载均衡
  • 场景4:分布式通知与协调
  • 场景5:分布式锁、分布式队列
  • 场景6:集群监控与Leader竞选。比如:某个用户需要一个分布式存储仓库来存储配置信息,并且对于读写速度、高可用、部署便捷等方面都有要求,则该场景可以优先考虑ETCD数据库。当前使广泛的kubernetes,亦使用ETCD来存储docker集群的配置信息。

2、ETCD基本架构

2.1、ETCD中的专有名词

(1)心跳机制:ETCD集群的所有节点都是设定为随机的超时时间(相当于定时器,防止出现选票瓜分的情况)。当集群已经产生了 Leader节点,则 Leader节点会在固定间隔内给所有节点发送心跳。其他节点收到心跳以后重置心跳等待时间,只要心跳等待不超时,Follower节点的状态就不会改变。当Follower节点未接收到心跳时(心跳超时),则重新选举Leader节点。

(2)Leader节点:发送的心跳包中,会携带Leader节点的任期term,用于发现哪些节点已经过期了。

(3)Raft:etcd所采用的保证分布式系统强一致性的算法。

(4)Node:一个Raft状态机实例。

(5)Member:一个etcd实例。它管理着一个Node,并且可以为客户端请求提供服务。

(6)Cluster:由多个Member构成可以协同工作的ETCD集群。

(7)Peer:对同一个ETCD集群中另外一个Member的称呼。

(8)Client:向etcd集群发送HTTP请求的客户端。

(9)WAL:(write ahead log)预写式日志,保存的是Raft Log中的操作指令。ETCD用于持久化存储的日志格式。

(10)Raft Log:用于保存客户端发送的写数据操作指令。

(11)snapshot:ETCD防止WAL文件过多而设置的快照,存储ETCD数据状态。

(12)Proxy:ETCD的一种模式,为etcd集群提供反向代理服务。

(13)Index:数据项编号。Raft中通过Term和Index来定位数据。

(14)Follower:负责处理客户端的读数据请求,同时需要从 Leader 同步数据,将Leader发送的raft log日志数据写入该节点的raft log文件,最后再写入状态机=》写入dolt db数据库。如果客户端的写数据操作请求发送给了 Follower,会由 Follower 重定向给 Leader 。

(15)Candidate:如果 Follower在一定时间内没有收到 Leader 的心跳,则判断 Leader 可能已经故障,此时启动 Leader election 过程,本节点(心跳超时的Follower节点)切换为 Candidate,参与Leader 选举, 直到选主流程结束。

(16)Leader:用于接收客户端发起的写数据操作请求,写入本地raft log日志后同步至集群其它节点。

(16)Term:每开始一次新的选举,称为一个任期(term),每个 term 都是单调递增的。每当 candidate 触发 leader election 时都会增加 term值,如果一个 candidate 赢得选举,他将在本 term 中担任 leader 的角色。但并不是每个 term 都一定对应一个 leader,有时候某个 term 内会由于选举超时导致选不出 leader,这时 candicate 会递增 term 号并开始新一轮选举。每一个节点都保存一个 current term,在通信时带上这个 term ,通过对比Leader节点的term,就可以发现哪些节点的状态已经过期,然后进行后续的raft log日志更新操作。

2.2、Raft算法的名词

(1)法定人数机制:客户端的指令执行成功、保存的判断依据。Raft 算法使用 法定人数(Quorum) 机制判断执行的命令是否成功,即:客户端发送的写数据请求,必须有50%以上的节点成功写入该指令后,才能算指令执行成功,否则不对该指令进行数据写入操作。

(2)Leader、Candidata、Follower、Term概念:含义同ETCD中的名词。

(3)Lease租约机制

(4)Watch监控机制

(5)状态机:每个ETCD节点都维护了一个状态机,用于写入已提交的raft log日志数据(保证leader的状态机数据是最新的,wal日志则是用于持久化数据操作)。当ETCD集群接收到读请求时,在任意节点都可以读,而写请求只能重定向到Leader节点,由Leader节点执行,通过Raft协议保证写操作对状态机的改动会可靠的同步到其他Follower节点。

(6)raft log日志:负责存储客户端发过来的写数据指令,Leader节点需要将该日志中的数据同步给其它Follower节点,当超过50%的Follower节点都同步了客户端发送来的写数据指令后,就认为该集群已经安全复制了该指令,Leader节点将该指令提交至状态机中,最后返回该指令的执行状态给客户端.

(7)Leader选举:ETCD的Leader节点负责写日志(处理客户端的写数据指令,并将这些指令同步至Follower节点的日志中),Follower节点负责读日志。Leader节点的作用:保证整个集群仅有一份日志 =》保证所有节点执行数据写入操作的顺序一致 =》保证集群数据的一致性。

(8)安全性:对Leader选举的规则限制。ETCD服务端server对客户端client的数据操作指令提交的限。Leader 节点从来不会覆盖或者删除自己的日志。Leader 只允许 commit 包含当前 term 的日志。

2.3、ETCD的基本架构

(1)raft-http:用于不同etcd node之间进行通信,接收来自其他node的消息。

(2)server:用于接收client客户端发送的请求,eg:etcdctl命令行工具、go编写的client。

(3)raft模块 :实现分布式一致性raft协议, raft模块与server模块的通信采用了四个channel。

(4)propc:处理server发送来的client命令。

(5)recvc:处理raft-http发送来的http消息。

(6)readyc: 消息经过raft处理之后封装成Ready交给server处理。

(7)advanceC:server处理一条raft发送来的消息后,将处理结果返回给raft。

(8)raftlog:raftlog模块包含unstable和raft的snapshot,unstable保存log entries,但是entries数量比较多的时候,就需要compact,创建一个snapshot,这里的snapshot还是保存在memory中的。raft模块会定时收集entries交给server处理。

(9)mvcc:实现多版本的并发控制,使用revision(main和sub)来描述一个key的整个过程:从创建到删除。mvcc中还包含了watcher,用于实现监听key、prefix、range的变化。

(10)backend 和 boltdb:持久化key value数据到boltdb数据库.

各个模块之间的关系如图所示:

 

 

2.4、ETCD的数据结构

ETCD采用boltdb存储数据,故此处主要介绍boltdb数据库的结构。按照功能可分为:元数据页 (meta page)、B+ tree 索引节点页 (branch page)、B+ tree 叶子节点页 (leaf page)、空闲页管理页 (freelist page)、空闲页 (free page)。

(1)meta page:存储固定的 db 元数据

(2)branch page:编排所有数据的 key,存取数据时即可通过二分搜索查找需要的 key数据

(3)leaf page:负责存储key-value 数据、bucket 数据,从branch page页获取key之后,根据key来该页查找对应的key-value数据。

(4)freelist page:存储空闲页free page的ID,用于记录db 中哪些页是空闲、可使用的。当boltdb 中删除大量数据的时候,其对应的 page 就会被释放。

(5)free page:当写入数据的时候,就可直接从空闲页中申请页面使用。

3、ETCD基本原理

ETCD数据库主要通过Raft来实现数据强一致性,而Raft算法的数据强一致性的实现主要涉及到状态机、Leader节点的选举流程。故此处主要介绍这状态机、、Leader节点的选举流程。由此来讲解ETCD的基本原理。

3.1、状态机

3.1.1、状态机的基本概念

状态机执行指令的顺序并不一定等同于指令的发出顺序。复制状态机只是保证所有的状态机都以相同的顺序执行命令。

(1)状态机:状态机中数据的含义:Leader节点已提交的raft log数据 = 那些已经被安全复制至Follower节点的raftlog数据。每个ETCD节点都维护了一个状态机(每个节点都拥有Leader节点所有的状态机数据),用于写入已提交的raft log日志数据。当ETCD集群接收到读请求时,在任意节点都可以读,而写请求只能重定向到Leader节点,由Leader节点执行,通过Raft协议保证写操作对状态机的改动会可靠的同步到其他Follower节点。

(2)raft log:Leader节点的raft log,用于存放客户端发送的写数据指令(包含已提交至状态机的日志数据)

(3)wal文件:用于持久化数据操作,将状态机的数据写入本地磁盘。

(4)复制状态机系统的概念

1)复制单元:复制单元是一个状态机,其数据存储在日志中,复制单元严格按照顺序逐条执行日志上的指令。

2)复制状态机系统:由多个复制单元组成。用于解决一份数据存在多个副本的问题。

3)实现方式:通过Leader节点分发数据至Follower节点(复制单元、状态机),因此每个状态机都是确定的,所以每个外部命令都将产生相同的操作顺序(日志)。又因为每一个日志都是按照相同的顺序存放相同的指令,所以每个状态机都将执行相同的指令序列,并且最终保证所有状态机具有相同的状态。

3.1.2、为什么用状态机

Raft一致性算法的目的是:保证集群中所有节点的数据、状态一致。按照节点执行的命令分类,可分为2类:读、写。只有写命令可以改变节点数据、状态。因此需要将写命令同步至所有节点中。但是,由于网络存在延时,就会导致各个节点接收到的写命令顺序不同,进而导致写入数据的不一致。因此,ETCD中Leader节点负责执行写命令,Follower节点负责执行读命令的方式,且所有节点的日志数据均有Leader节点维护,从而保证集群中所有节点数据的一致性。

3.1.3、节点提交日志的原则

(1)判断是否安全复制日志

客户端提交写数据请求给Leader时,Leader节点将命令写入日志中,并发送至所有的Follower节点,仅当超过50%的Follower节点都已经复制日志后该后,leader 会将该日志提交到它本地的状态机中,然后把操作成功的结果返回给客户端。

(2)Leader将roft log日志提交至状态机的准则

1)Leader 节点从来不会覆盖或者删除自己的日志

2)Leader 只允许 commit 包含当前 term 的日志

3.1.4、发送日志的数据结构

Leader节点发送至Follower节点的日志数据中,都包含Leader节点的任期term、日志数据的索引index。具体为:当前日志的(index、term) + 上一条日志的(index、term)+ 下一条日志的(index,term)

3.1.5、ETCD的状态机执行流程

集群中raft log日志数据同步的流程:(复制状态机系统的执行流程)

(1)ETCD集群启动,会初始化一个Leader节点,决定日志的顺序,负责发送日志到其他Follower节点。

(2)当Leader节点的一致性模块(consensus module)接收到客户端的写请求时,先将命令写入自己的日志,然后同步给所有Follower节点,仅当50%以上Follower节点都接收到日志后,Leader节点才提交日志。日志提交后,由Leader节点按顺序应用于状态机。

(3)Leader节点发送至Follower节点的日志数据同步流程如下:

 

(4)仅当Leader日志提交成功后,其他Follower节点才会将来自步骤2:Leader节点中consensus module发送的日志数据,应用到该Follower节点的状态机中,然后进行数据写入操作。(从而保证所有Follower节点的数据一致性)

(5)最后,Leader节点将数据写入命令的执行状态,返回给客户端client。

 

 

3.1.6、Follower节点和Leader节点日志同步

上图中,raft log日志数据同步过程中,步骤②执行失败时,如何保证Follower节点数据与Leader节点数据一致。集群中的节点,日志不同步的处理流程如下:

(1)Leader节点发送至Follower节点的日志数据中,都包含Leader节点的任期term、日志数据的索引index。

(2)Leader节点会用n个参数(next index),来记录应发送给每一个Follower节点的下一条日志索引位置。如果此时Leader节点宕机,并完成了Leader选举流程,Candidate节点 =》新的Leader节点之后,这个新的Leader节点会将所有的next index重置为自己的最新的日志索引。

(3)开始进行数据同步,Leader节点发送自己的日志至Follower节点中。当Follower节点接收到Leader节点发送的日志数据时,需要判断所接收到的日志数据的index_A 是否等于 Follower节点最新日志的index_B + 1。若相等,则说明Leader节点需要从index_A开始同步日志数据值Follower。若不相等,则Follwer节点拒绝接收该数据,说明Follower缺失了部分日志数据,Leader节点需要将index_A - 1(即:发送上一条日志),然后继续发送日志数据至Follower节点。直至index_A = index_B + 1。

3.2、Leader选举流程

首先,需要明确一下Leader节点重新选举的原因。当Leader节点崩溃后,集群无法进行数据写入操作,其他Follower节点通过心跳可以感知到,并从剩余的节点中选举出新的Leader节点,以维持集群的正常运转。

其次,规定节点投票的原则。Candidate节点只会投票给自己,仅当某个Candidate节点的票数超过50%时,才能成为Leader节点。Follower节点只会投票给任期比自己大、最后一条日志比自己新的Candidate节点。

基于上述的2点,以下开始介绍Leader选举流程:

(1)第一阶段:ETCD集群启动时

ETCD集群启动时,Raft算法给每个节点设置了随机的超时时间(控制在一定范围内的随机值)——这样可以降低同时出现多个节点心跳超时的概率,减小瓜分选票出现的概率。此时,集群中的节点都是Follower节点,不存在Leader节点。需要从Follower节点中选举出一个Leader节点。

等待集群中有一个Follower节点出现心跳超时,该Follower节点 =》Candidate节点(并给自己的任期 + 1),开始给其他节点发起投票请求,即:进入Leader选举。(一般来说,因为设置了随机超时时间,所以集群刚启动时,同时只有一个Follower节点 =》Candidate,这个阶段进行Leader选举,可以成功获取50%以上的选票,产生一个Leader节点)

(2)第二阶段:ETCD集群的Leader节点宕机,此时开始选举新的Leader节点,由Candidate节点状态的转换而来。

Candidate节点发起投票后,可能存在3种结果:选举成功、选举失败、选举超时。一般来说,ETCD集群刚启动时,进行第一次Leader选举都会成功。

1)选举成功

该Candidate节点获取了50%以上的Follower节点选票,成功转变为Leader节点,并且发送自己的心跳报包至Follower节点,以维持整个集群的正常运行。

2)选举失败(ETCD集群的Leader节点宕机后,再次恢复后,加入集群时)

当Candidate节点进行Leader选举时,收到了某个Leader节点(该节点可能之前时Leader,但是宕机了,现在又恢复了)发送来的心跳包。此时需要判断该心跳包中的任期term和Candidate节点的term大小。该心跳包中的任期term ≥ Candidate节点的term,说明集群中已经选举出新的Leader节点。Candidate节点切换为Follower节点,结束选举流程。该心跳包中的任期term < Candidate节点的term。该Candidate继续进行Leader选举流程,直至集群中选举出新的Leader节点。

3)选举超时(选票瓜分情况)

集群中存在多个Candidate节点参与选举Leader节点。此时所有的Candidate节点的得票一致,无法选举出新的Leader节点。就需要进行下一轮Leader选举,直至集群中选举出新的Leader节点。

(3)第三阶段:ETCD集群的Leader节点宕机后,再次恢复后,加入集群时。

当集群中的Leader节点宕机时,集群中已经心跳超时的Follower节点会进入Leader选举流程,Follower节点 =》 Candidate节点,Candidate节点的term + 1,并开始投票流程,成功选举某个Candidate节点为Leader节点。

此时,宕机的Leader节点恢复,集群中会出现一个过期的Leader节点。当过期的Leader节点接收到新Leader节点发送的心跳包时,由于该过期的Leader节点的term 小于 Candidate的term, 该过期节点会变为Follower节点。

Leader选举流程图如下所示:

 

4、ETCD容器化部署方案

此处通过docker方式部署具备3个ETCD节点的集群环境。

4.1、ETCD部署要求

(1)系统要求:amd64-linux。

(2)存储设备:80GB以上的SDD。ETCD数据需要写入磁盘中,因此为增加数据读写速度,ETCD数据存储设备需要使用SSD,且需要验证存储设备的性能。可使用fio工具进行检测。

(3)处理器:双核以上。

(4)ETCD需要使用的存储空间:持久化数据的空间.最大8GB,最小2GB。因为ETCD需要强制设置一个默认大小(2GB)。设置占用RAM的大小:--quota-backend-bytes。

4.2、部署原则

(1)ETCD集群的数量必须为奇数,因为ETCD主机出现宕机时,需要从其他节点中选取leaer。若集群节点数量为偶数,则可能会出现某个节点所得票数相等,无法选举出leader。

(2)ETCD集群最大数量 ≤ 7。虽然增加集群的数量,可以增加ETCD的容错性,但是集群数量越大数据的写入性能就会下降,因为需要将数据复制至更多的节点中。推荐集群数量 = 5,可容忍2个节点故障

(3)ETCD可用于跨区域、跨数据中心部署,但是由于集群中的节点分布在不同的网段中,会增加数据请求的延时。并且,节点之间的数据复制,将会占用带宽。

4.3、构建ETCD镜像包

(1)准备文件:Dockerfile

(2)构建命令:docker build -t DockerHub的ID/etcd:版本号。

(3)示例配置文件

FROM centos

MAINTAINER chris

ENV TZ="Asia/Shanghai";\

    DOWNLOAD=h体体ps://github.com/etcd-io/etcd/releases/download/v3.4.16/ \

 ETCDVERSION=etcd-v3.4.16-linux-amd64 USER=admin

RUN yum install curl wget tar -y ;\

    useradd ${USER} ;\

    mkdir -p /export/{servers,Logs,packages,Apps,Shell} ;\

    wget ${DOWNLOAD}${ETCDVERSION}.tar.gz && tar -zxf ${ETCDVERSION}.tar.gz -C /export/servers/ && /bin/rm -rf ${ETCDVERSION}.tar.gz ; chown -R ${USER}.${USER} /export ;\

    ln -s /export/servers/${ETCDVERSION}/etcd* /usr/local/bin/;\

rm -rf /etcd-data/ ;

EXPOSE 2379 2380

 

 

4.4、准备docker-compose文件

一共准备3份如下示例配置文件,具体的参数修改如下:

(1)配置文件参数解释

1)--name:修改为对应节点名称。

2)--initial-cluster:修改对应的etcd的IP,本机IP为0.0.0.0:2380,其他机器为相应网卡的IP:2380。注意:0.0.0.0、127.0.0.1、localhost的区别。

0.0.0.0在服务器中,表示本机上的所有IPV4地址,若主机有两个IP,192.168.1.1 和 10.1.2.1,并且该主机上的一个服务监听的是0.0.0.0,那么通过两个ip都能够访问该服务。在路由中,0.0.0.0表示的是默认路由,即当路由表中没有找到完全匹配的路由的时,则使用该IP进行路由。

127.0.0.1属于{127,}集合中的一个,而所有网络号为127的地址都被称之为回环地址,所以回环地址 ≠ 127.0.0.1,它们是包含关系,即回环地址包含127.0.0.1。

localhost是个域名,而不是一个ip。一般情况下,认为localhost、127.0.0.1是等价的,因为大多数电脑上都在hosts文件中将localhost指向了127.0.0.1。

(2)示例配置文件

#docker-compose.yml

version: '2'

services:

  etcd:

    image: z1294550676/etcd:3.4.16

    command: ["/usr/local/bin/etcd","--name", "etcd1",

               "--data-dir", "/etcd-data",

               "--listen-client-urls","h体体p://0.0.0.0:2379",

               "--advertise-client-urls","h体体p://0.0.0.0:2379",

               "--listen-peer-urls","h体体p://0.0.0.0:2380",

               "--initial-advertise-peer-urls","h体体p://0.0.0.0:2380",

    "--initial-cluster","etcd1=h体体p://0.0.0.0:2380,etcd2=h体体p://192.168.83.137:2380,etcd3=h体体p://192.168.83.138:2380",

               "--initial-cluster-token","2021CTyun!",

               "--initial-cluster-state","new",

               "--auto-compaction-retention","10"]

    volumes:

      - /data/etcd:/etcd-data

    ports:

      - 2379:2379

      - 2380:2380

5、ETCD使用过程中的常见问题

(1)查看ETCD数据库当前的剩余空间

在ETCD容器中执行如下命令:docker exec -it 5e9a3a96b3fe etcdctl --write-out=table endpoint status

结果如下:

 

(2)ETCD存储空间扩容

在ETCD容器启动时,添加如下参数:--quota-backend-bytes

注意:

1)该参数单位为字节。

2)ETCD容器启动时,若不指定当前需要使用的持久化空间大小,则默认为2GB。

3)ETCD最大的持久化空间大小为8GB。

4)因此,ETCD数据库并不适合用于存储大量的数据。

(3)ETCD持久化数据压缩

当ETCD集群中存储大量数据时(小于8GB),此时ETCD可用的数据持久化空间较少,可以通过ETCD的数据压缩命令,将其中一个ETCD容器的数据进行压缩。通过Raft协议,可以保证其他的ETCD容器数据也被同步为压缩后的数据,即:保证了数据强一致。

压缩示例如下:

 

(4)ETCD持久化数据备份

针对需要备份的应用数据场景,防止ETCD由于意外删除持久化数据,导致数据丢失的问题,直接使用ETCD的快照功能,对数据进行定期备份。脚本如下:

etcdId=$(sudo docker ps -q --filter name=etcd)

a=`date +%Y%m%d`

backfile=/etcd-data/snap-$a\.db

srcpath='/data/etcd'

sudo docker exec -i $etcdId etcdctl snapshot save $backfile

cd $srcpath

ls $srcpath -t|grep db |sed -n '6,$p' | xargs -I {} rm -rf {}

0条评论
0 / 1000