云原生 etcd 系列 | 为什么值得学习

什么是 etcd ?

etcd 是 CoreOS 团队于 2013 年 6 月发起的开源项目。etcd 官网上有这么一段话:

A distributed, reliable key-value store for the most critical data of a distributed system

etcd 是一个分布式、高可靠的 kv 系统,基于 Go 语言实现,内部使用 raft 协议来保证数据一致性和高可靠性。 哦,就是一个 kv 系统哦,etcd 一般用来做什么呢?

对于 etcd 的定位其实在名字里亦可窥见,etcd 这个名字拆开来看就是 etc distributed ,也是就是分布式的 etc 嘛),大家都知道 linux 的 etc 目录就是单机的配置中心。所以 etcd 的应用定位其实就是分布式的配置中心系统嘛,目的就是为服务集群提供一个的全局性配置中心

etcd 为什么值得学?

超火项目背书

etcd 有两个重要项目为其背书:CoreOS,Kubernetes,有人甚至号称 etcd 是云原生的基石之一。 为什么会这么说? 因为 etcd 在其中扮演的角色太重要了,Kubernetes 把核心元数据信息存储在里面。 etcd 在非常多的互联网大厂里都有深度的应用,如果应聘一些云原生的岗位,对 etcd 的熟悉会是很好的亮点。

优秀代码借鉴

etcd 的数据一致性和高可靠是基于 raft 协议实现的。如果有童鞋对 raft 精通理论,但是苦于没有代码实践,这是一个非常好的学习 demo 。

etcd 对 raft 的封装那简直了,分层抽象的太厉害了。raft 模块几乎就只集中了状态机的实现,把 wal 啥的都抽出去了,在 raft 状态机中,对于日志的使用抽象出了 Storage 接口。

所以单就代码来说,真的值得学习一波。

etcd 的特点

  • 简单:接口简单,使用简单;
  • 安全:支持 TLS 证书认证;
  • 强一致性:etcd 通过 raft 协议保证数据的强一致性,实现 leader 到 follower 节点的正确复制;
  • 高可用性:集群能自动处理单点故障,网络分区等情况,对外提供高可靠、一致性的数据访问;
  • 性能不错:看官网的 benchmark 数据是 10000 次每秒的写入;

其实经过了 k8s 等超大型的项目验证,etcd 已经是一个非常成熟的项目了。

怎么入手学习?

下面节点聊聊怎么去学习,这篇是开篇文章。

代码仓库

奇伢很早就拉了个仓库出来,用于阅读 etcd 的代码,版本是 v3.4.10 。 https://github.com/liqingqiya/readcode-etcd-v3.4.10 一直在不断的在这个仓库里加注释,该仓库代码可直接编译,建议 Go 版本 >= 1.14 。 etcd 说白了就是是 kv 存储,只不过这个 kv 存储是分布式的,数据是多副本冗余的,kv 是有多版本功能的。 既然是多节点那最重要的是一致性,etcd 使用了 raft 算法解决了数据多节点的一致性。

说起来 etcd 也是个不小的项目,要撸开源项目最好的就是找到一个切入点,从一个很小的点开始,慢慢往外延伸。etcd 考虑到这点,提供了一个非常好的演示项目 raftexample 。

raftexample

这个模块旨在通过一个极简的例子帮助大家理解 raft 模块在 etcd 中的使用姿势。理解 raft 的模块对理解 etcd 整体流程有非常大的帮助。

所以,奇伢第一步就是去撸一遍 raft 相关的代码,etcd 的业务从抽象模块来讲运行在 raft 模块,只要把 raft 相关模块理解了,etcd 就算理解 50 % ,剩下的基本都是业务功能。

raftexample 的目录位于 etcd/contrib/raftexample/ ,这个目录是一个完整的 package,实现了一个极简的 kv 存储,就是为了专门理解 raft 的。

➜  raftexample git:(master) ✗ tree -L 1
.
├── httpapi.go      *# 最上层的模块,对外提供 http api,我们命名为 api 层;*
├── kvstore.go      *# kv 存储的实现,业务层的实现,我们命名为 service 层;*
├── raft.go         *# 承上启下的模块,对上对接 service 模块,对下对接 raft 状态机模块;*
├── listener.go     *# 和 raftNode 是一起的;*
├── main.go         *# 入口文件*

0 directories, 10 files

具体的模块划分为四层: raftexample的四层结构.png 下一步,开始。。。

编译 raftexample

克隆仓库下来: git clone https://github.com/liqingqiya/readcode-etcd-v3.4.10.git 编译(可以把一些依赖包下载下来):

root@ubuntu:~/code/github/readcode-etcd-v3.4.10/src/go.etcd.io/etcd*# make*
....

go.etcd.io/etcd/etcdctl
./bin/etcd --version
etcd Version: 3.4.10
Git SHA: b516720
Go Version: go1.14.4
Go OS/Arch: linux/amd64
./bin/etcdctl version
etcdctl version: 3.4.10
API version: 3.4

这样需要的一些依赖包自动就下载下来了。然后,我们就去编译 raftexample 这个程序。

root@ubuntu:~/code/github/readcode-etcd-v3.4.10/src/go.etcd.io/etcd/contrib/raftexample*# go build -gcflags=all="-N -l"*

编译出 raftexample 的二进制就代表成功了,具有了学习的素材。按照 README 的提示,我们用 goreman 来启动程序: goreman start goreman 读的是当前目录的 Procfile 文件,这个文件是这样写的:


root@ubuntu:~/code/github/readcode-etcd-v3.4.10/src/go.etcd.io/etcd/contrib/raftexample*# cat Procfile *

*# Use goreman to run `go get github.com/mattn/goreman`*

raftexample1: ./raftexample --id 1 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --port 12380

raftexample2: ./raftexample --id 2 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --port 22380

raftexample3: ./raftexample --id 3 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --port 32380

也就是启动 3 个 raftexample 进程,分别使用了不同的端口,这样就省去了我们人为的键入,方便一些。 goreman start 执行,你就会发现拉起了 3 个进程,这组成了一个最简单的 raft 集群。 ef073f0b-bfee-417a-b11b-046c4aa159a4.png 接下来,我们就调试+阅读这个程序的代码,来理解 raft 是怎么回事,敬请期待。

奇伢梳理了一系列 etcd 的深度剖析文章,以后整理出来分享,先分享一张简单图片,用于描述 业务层 -> raftNode -> raft 状态机 这三层的交互关系:

raftexample 架构图.png etcd 系列深度分享,敬请期待