1、绪论

etcd作为华为云PaaS的核心部件,实现了PaaS大多数组件的数据持久化、集群选举、状态同步等功能。如此重要的一个部件,我们只有深入地理解其架构设计和内部工作机制,才能更好地学习华为云Kubernetes容器技术,笑傲云原生的“江湖”。本系列将从整体框架再细化到内部流程,对etcd的代码和设计进行全方位解读。本文是《深入浅出etcd》系列的第一篇,重点解析etcd的架构和代码框架,下文所用到的代码均基于etcd v3.2.X版本。

另,由华为云容器服务团队倾情打造的《云原生分布式存储基石:etcd深入解析》一书已正式出版,各大平台均有发售,购书可了解更多关于分布式key—value存储和etcd的相关内容!

2、etcd简介

etcd是一个分布式的key-value存储系统,底层通过Raft协议进行leader选举和数据备份,对外提供高可用的数据存储,能有效应对网络问题和机器故障带来的数据丢失问题。同时它还可以提供服务发现、分布式锁、分布式数据队列、分布式通知和协调、集群选举等功能。为什么etcd如此重要?因为etcd是Kubernetes的后端唯一存储实现,毫不夸张地说,etcd就是Kubernetes的“心脏”。

2.1 Raft协议

要理解etcd分布式协同的工作原理,必须提到Raft算法。Raft算法是斯坦福的Diego Ongaro、John Ousterhout两人以易懂(Understandability)为目标设计的一致性共识算法。在此之前,提到共识算法(Consensus Algorithm)必然会提到Paxos,但是Paxos的实现和理解起来都非常复杂,以至于Raft算法提出者的博士论文中,作者提到,他们用了将近一年时间研究这个算法的各种解释,但还是没有完全理解这个算法。Paxos的算法原理和真正实现也有很大的距离,实现Paxos的系统,如Chubby,对Paxos进行了很多的改进有优化,但是细节却是不为人所知的。 Raft协议采用分治的思想,把分布式协同的问题分为3个问题:

  • 选举: 一个新的集群启动时,或者老的leader故障时,会选举出一个新的leader;

  • 日志同步: leader必须接受客户端的日志条目并且将他们同步到集群的所有机器;

  • 安全: 保证任何节点只要在它的状态机中生效了一条日志条目,就不会在相同的key上生效另一条日志条目。

一个Raft集群一般包含数个节点,典型的是5个,这样可以承受其中2个节点故障。每个节点实际上就是维护一个状态机,节点在任何时候都处于以下三种状态中的一个。

  • leader:负责日志的同步管理,处理来自客户端的请求,与Follower保持这heartBeat的联系;

  • follower:刚启动时所有节点为Follower状态,响应Leader的日志同步请求,响应Candidate的请求,把请求到Follower的事务转发给Leader;

  • candidate:负责选举投票,Raft刚启动时由一个节点从Follower转为Candidate发起选举,选举出Leader后从Candidate转为Leader状态。

节点启动以后,首先都是follower状态,在follower状态下,会有一个选举超时时间的计时器(这个时间是在配置的超时时间基础上加一个随机的时间得来的)。如果在这个时间内没有收到leader发送的心跳包,则节点状态会变成candidate状态,也就是变成了候选人,候选人会循环广播选举请求,如果超过半数的节点同意选举请求,则节点转化为leader状态。如果在选举过程中,发现已经有了leader或者有更高的任期值的选举信息,则自动变成follower状态。处于leader状态的节点如果发现有更高任期值的leader存在,则也是自动变成follower状态。

Raft把时间划分为任期(Term)(如下图所示),任期是一个递增的整数,一个任期是从开始选举leader到leader失效的这段时间。有点类似于一届总统任期,只是它的时间是不一定的,也就是说只要leader工作状态良好,它可能成为一个独裁者,一直不下台。

2.2 etcd的代码整体架构

etcd整体架构如下图所示:

从大体上可以将其划分为以下4个模块

- http:负责对外提供http访问接口和http client

- raft 状态机:根据接受的raft消息进行状态转移,调用各状态下的动作。

- wal 日志存储:持久化存储日志条目。

- kv数据存储:kv数据的存储引擎,v3支持不同的后端存储,当前采用boltdb。通过boltdb支持事务操作。

相对于v2,v3的主要改动点为:

1. 使用grpc进行peer之间和与客户端之间通信;

2. v2的store是在内存中的一棵树,v3采用抽象了一个kvstore,支持不同的后端存储数据库。增强了事务能力。

去除单元测试代码,etcd v2的代码行数约40k,v3的代码行数约70k。

2.3 典型内部处理流程

我们将上面架构图的各个部分进行编号,以便下文的处理流程介绍中,对应找到每个流程处理的组件位置。

2.3.1 消息入口

一个etcd节点运行以后,有3个通道接收外界消息,以kv数据的增删改查请求处理为例,介绍这3个通道的工作机制。 1. client的http调用:会通过注册到http模块的keysHandler的ServeHTTP方法处理。解析好的消息调用EtcdServer的Do()方法处理。(图中2) 2. client的grpc调用:启动时会向grpc server注册quotaKVServer对象,quotaKVServer是以组合的方式增强了kvServer这个数据结构。grpc消息解析完以后会调用kvServer的Range、Put、DeleteRange、Txn、Compact等方法。kvServer中包含有一个RaftKV的接口,由EtcdServer这个结构实现。所以最后就是调用到EtcdServer的Range、Put、DeleteRange、Txn、Compact等方法。(图中1) 3. 节点之间的grpc消息:每个EtcdServer中包含有Transport结构,Transport中会有一个peers的map,每个peer封装了节点到其他某个节点的通信方式。包括streamReader、streamWriter等,用于消息的发送和接收。streamReader中有recvc和propc队列,streamReader处理完接收到的消息会将消息推到这连个队列中。由peer去处理,peer调用raftNode的Process方法处理消息。(图中3、4)

2.3.2 EtcdServer消息处理

对于客户端消息,调用到EtcdServer处理时,一般都是先注册一个等待队列,调用node的Propose方法,然后用等待队列阻塞等待消息处理完成。Propose方法会往propc队列中推送一条MsgProp消息。 对于节点间的消息,raftNode的Process是直接调用node的step方法,将消息推送到node的recvc或者propc队列中。 可以看到,外界所有消息这时候都到了node结构中的recvc队列或者propc队列中。(图中5)

2.3.3 node处理消息

node启动时会启动一个协程,处理node的各个队列中的消息,当然也包括recvc和propc队列。从propc和recvc队列中拿到消息,会调用raft对象的Step方法,raft对象封装了raft的协议数据和操作,其对外的Step方法是真正raft协议状态机的步进方法。当接收到消息以后,根据协议类型、Term字段做相应的状态改变处理,或者对选举请求做相应处理。对于一般的kv增删改查数据请求消息,会调用内部的step方法。内部的step方法是一个可动态改变的方法,将随状态机的状态变化而变化。当状态机处于leader状态时,该方法就是stepLeader;当状态机处于follower状态时,该方法就是stepFollower;当状态机处于Candidate状态时,该方法就是stepCandidate。leader状态会直接处理MsgProp消息。将消息中的日志条目存入本地缓存。follower则会直接将MsgProp消息转发给leader,转发的过程是将先将消息推送到raft的msgs数组中。 node处理完消息以后,要么生成了缓存中的日志条目,要么生成了将要发送出去的消息。缓存中的日志条目需要进一步处理(比如同步和持久化),而消息需要进一步处理发送出去。处理过程还是在node的这个协程中,在循环开始会调用newReady,将需要进一步处理的日志和需要发送出去的消息,以及状态改变信息,都封装在一个Ready消息中。Ready消息会推行到readyc队列中。(图中5)

2.3.4 raftNode的处理

raftNode的start()方法另外启动了一个协程,处理readyc队列(图中6)。取出需要发送的message,调用transport的Send方法并将其发送出去(图中4)。调用storage的Save方法持久化存储日志条目或者快照(图中9、10),更新kv缓存。 另外需要将已经同步好的日志应用到状态机中,让状态机更新状态和kv存储,通知等待请求完成的客户端。因此需要将已经确定同步好的日志、快照等信息封装在一个apply消息中推送到applyc队列。

2.3.5 EtcdServer的apply处理

EtcdServer会处理这个applyc队列,会将snapshot和entries都apply到kv存储中去(图中8)。最后调用applyWait的Trigger,唤醒客户端请求的等待线程,返回客户端的请求。

3、重要的数据结构

3.1 EtcdServer

type EtcdServer struct {

// 当前正在发送的snapshot数量

inflightSnapshots int64

//已经apply的日志index

appliedIndex      uint64

//已经提交的日志index,也就是leader确认多数成员已经同步了的日志index

committedIndex    uint64

//已经持久化到kvstore的index

consistIndex consistentIndex

//配置项

Cfg          *ServerConfig

//启动成功并注册了自己到cluster,关闭这个通道。

readych chan struct{}

//重要的数据结果,存储了raft的状态机信息。

r       raftNode

//满多少条日志需要进行snapshot

snapCount uint64

//为了同步调用情况下让调用者阻塞等待调用结果的。

w wait.Wait

//下面3个结果都是为了实现linearizable 读使用的

readMu sync.RWMutex

readwaitc chan struct{}

readNotifier *notifier

//停止通道

stop chan struct{}

//停止时关闭这个通道

stopping chan struct{}

//etcd的start函数中的循环退出,会关闭这个通道

done chan struct{}

//错误通道,用以传入不可恢复的错误,关闭raft状态机。

errorc     chan error

//etcd实例id

id         types.ID

//etcd实例属性

attributes membership.Attributes

//集群信息

cluster *membership.RaftCluster

//v2的kv存储

store       store.Store

//用以snapshot

snapshotter *snap.Snapshotter

//v2的applier,用于将commited index apply到raft状态机

applyV2 ApplierV2

//v3的applier,用于将commited index apply到raft状态机

applyV3 applierV3

//剥去了鉴权和配额功能的applyV3

applyV3Base applierV3

//apply的等待队列,等待某个index的日志apply完成

applyWait   wait.WaitTime

//v3用的kv存储

kv         mvcc.ConsistentWatchableKV

//v3用,作用是实现过期时间

lessor     lease.Lessor

//守护后端存储的锁,改变后端存储和获取后端存储是使用

bemu       sync.Mutex

//后端存储

be         backend.Backend

//存储鉴权数据

authStore  auth.AuthStore

//存储告警数据

alarmStore *alarm.AlarmStore

//当前节点状态

stats  *stats.ServerStats

//leader状态

lstats *stats.LeaderStats

//v2用,实现ttl数据过期的

SyncTicker *time.Ticker

//压缩数据的周期任务

compactor *compactor.Periodic

//用于发送远程请求

peerRt   http.RoundTripper

//用于生成请求id

reqIDGen *idutil.Generator

// forceVersionC is used to force the version monitor loop

// to detect the cluster version immediately.

forceVersionC chan struct{}

// wgMu blocks concurrent waitgroup mutation while server stopping

wgMu sync.RWMutex

// wg is used to wait for the go routines that depends on the server state

// to exit when stopping the server.

wg sync.WaitGroup

// ctx is used for etcd-initiated requests that may need to be canceled

// on etcd server shutdown.

ctx    context.Context

cancel context.CancelFunc

leadTimeMu      sync.RWMutex

leadElectedTime time.Time

}

3.2 raftNode

raftNode是Raft节点,维护Raft状态机的步进和状态迁移。

type raftNode struct {

// Cache of the latest raft index and raft term the server has seen.

// These three unit64 fields must be the first elements to keep 64-bit

// alignment for atomic access to the fields.

//状态机当前状态,index代表当前已经apply到状态机的日志index,term是最新日志条目的term,lead是当前的leader id

index uint64

term  uint64

lead  uint64

//包含了node、storage等重要数据结构

raftNodeConfig

// a chan to send/receive snapshot

msgSnapC chan raftpb.Message

// a chan to send out apply

applyc chan apply

// a chan to send out readState

readStateC chan raft.ReadState

// utility

ticker *time.Ticker

// contention detectors for raft heartbeat message

td *contention.TimeoutDetector

stopped chan struct{}

done    chan struct{}

}

3.3 node

type node struct {

//Propose队列,调用raftNode的Propose即把Propose消息塞到这个队列里

propc      chan pb.Message

//Message队列,除Propose消息以外其他消息塞到这个队列里

recvc      chan pb.Message

//集群配置信息队列,当集群节点改变时,需要将修改信息塞到这个队列里

confc      chan pb.ConfChange

//外部通过这个队列获取修改后集群配置信息

confstatec chan pb.ConfState

//已经准备好apply的信息队列

readyc     chan Ready

//每次apply好了以后往这个队列里塞个空对象。通知可以继续准备Ready消息。

advancec   chan struct{}

//tick信息队列,用于调用心跳

tickc      chan struct{}

done       chan struct{}

stop       chan struct{}

status     chan chan Status

logger Logger

}

4、小结

本文简要介绍了raft协议和etcd的框架,介绍了etcd内部的和消息流的处理。后续将分心跳和选举、数据同步、数据持久化等不同专题详细讲述etcd的内部机制。

深入浅出etcd系列Part 1 – etcd架构和代码框架的更多相关文章

  1. 彻底搞懂 etcd 系列文章(三):etcd 集群运维部署

    0 专辑概述 etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管.etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件 ...

  2. 彻底搞懂 etcd 系列文章(二):etcd 的多种安装姿势

    0 专辑概述 etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管.etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件 ...

  3. 彻底搞懂 etcd 系列文章(一):初识 etcd

    0 专辑概述 etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管.etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件 ...

  4. saltstack自动化运维系列11基于etcd的saltstack的自动化扩容

    saltstack自动化运维系列11基于etcd的saltstack的自动化扩容 自动化运维-基于etcd加saltstack的自动化扩容# tar -xf etcd-v2.2.1-linux-amd ...

  5. K8S从入门到放弃系列-(3)部署etcd集群

    摘要:etcd 是k8s集群最重要的组件,用来存储k8s的所有服务信息, etcd 挂了,集群就挂了,我们这里把etcd部署在master三台节点上做高可用,etcd集群采用raft算法选举Leade ...

  6. 二进制安装kubernetes(一) 环境准备及etcd组件安装及etcd管理软件etcdkeeper安装

    实验环境: 架构图: 主机环境: 操作系统:因docker对内核需要,本次部署操作系统全部采用centos7.6(需要内核3.8以上) VM :2C 2G 50G * 5  PS:因后面实验需要向k8 ...

  7. ETCD快速入门-01 ETCD概述

    1.ETCD概述 1.1 ETCD概述     etcd是一个高可用的分布式的键值对存储系统,常用做配置共享和服务发现.由CoreOS公司发起的一个开源项目,受到ZooKeeper与doozer启发而 ...

  8. Kubernetes后台数据库etcd:安装部署etcd集群,数据备份与恢复

    目录 一.系统环境 二.前言 三.etcd数据库 3.1 概述 四.安装部署etcd单节点 4.1 环境介绍 4.2 配置节点的基本环境 4.3 安装部署etcd单节点 4.4 使用客户端访问etcd ...

  9. 深入浅出Mybatis系列(九)---强大的动态SQL

    上篇文章<深入浅出Mybatis系列(八)---mapper映射文件配置之select.resultMap>简单介绍了mybatis的查询,至此,CRUD都已讲完.本文将介绍mybatis ...

随机推荐

  1. orcl 复杂查询

    测试环境: create table bqh6 (xm varchar2(10),bmbh number(2),bmmc varchar2(15),gz int);insert into bqh6 v ...

  2. FinalShell使用---Xshell的良心国产软件

    最近发现了一款同类产品FinalShell,还是一块良心国货.初步体验了一下,确实是良心之作.且免费(通用版),支持国货. FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还 ...

  3. JDK5 新特性之 可变参数的方法(2)---asList

    > Arrays.asList(T - a)方法的使用 >UnsupportedOperationException分析     Arrays.asList(T - a)方法的使用 pac ...

  4. Linux之添加交换分区

    Linux下的交换分区我们可以随意改变大小,如果说日常生活中分区不够用,今天我们来举个例子如何添加. 1.首先是使用dd命令创建一个空文件,这个空文件的大小就是你要继续添加的swap的大小,比如我这里 ...

  5. TIDB单机多实例进程

    TIDB节点: TIKV节点(tidb服务也有放在这里也有) tidb进程 tikv进程 当使用单机多实例(就是一个机器多个tikv的存储节点)的时候,每个实例都有对应的一个进程,这个进程号就是我们在 ...

  6. 一段刚刚出炉的CSV文件转换为DataTable对象的代码

    CSV是以文本形式保存的表格数据,具体是每列数据使用逗号分割,每行数据使用CRLF(\r\n)来结尾,如果数据值包含逗号或CRLF则使用双引号将数值包裹,如果数据值包含双引号则使用两个双引号做为转义. ...

  7. 《面向对象程序设计》六 GUI

    git传送门 我这无药可救的拖延症和懒癌orz 主界面 文件读取界面 提示界面 最初选择vs+mfc,发现许多自动生成的代码读不懂(不须懂),尝试qt后感觉人生迎来了希望,看了推荐的视频与教程稍微了解 ...

  8. 张高兴的 .NET Core IoT 入门指南:(二)GPIO 的使用

    什么是 GPIO GPIO 是 General Purpose Input Output 的缩写,即"通用输入输出". Raspberry Pi 有两行 GPIO 引脚, Rasp ...

  9. PyQt5--QSlide

    # -*- coding:utf-8 -*- ''' Created on Sep 20, 2018 @author: SaShuangYiBing Comment: ''' import sys f ...

  10. Mac svn使用学习-1-简介

    在Windows环境中,可以使用TortoiseSVN来搭建svn环境.但是由于Mac自带了svn的服务器端和客户端功能,因此可以直接使用svn功能. svn即subversion,Subversio ...