ETCD使用手册
etcd简介
etcd是一个分布式、开源的key-value键值对数据存储系统,具有一致性、支持高并发、高可用性。
- 所属开发公司:CoreOS
- 开发语言:Golang
- 项目源代码:(开源)github源码地址:https://github.com/coreos/etcd
- 官网: https://coreos.com/etcd/
- 中文官方文档
- 同类产品:zookeeper
- 使用案例:etcd作为kubernetes的配置管理组件
相比zookeeper特点
zookeeper可以实现的功能etcd均可以实现,etcd从zookeeper得到启发作为后起之秀,弥补了zookeeper的一些不足,有不少公司逐步用etcd替代zookeeper,随着k8s的兴起etcd越来越受到关注。
- 语言优势:zookeeper采用java编写,部署zookeeper实例需要依赖响应版本的jdk环境,软件目录结构复杂繁琐;而etcd使用go开发编写,源码编译生成的二进制文件可灵活部署,配置文件简介。zookeeper启动的java运行需要设置jvm相关运行参数进行调优,所以维护复杂,并且java应用相对重型;而etcd继承了go语言特点轻巧灵活。
- 高并发性:etcd更适合高并发场景,可支持百万级并发请求,相对于java的zookeeper有很大优势。
- 接口:etcd客户端访问接口api和自身节点同步机制都采用了HTTP协议和Resful风格,用户可以采用curl、postman等工具访问。相对zookeeper的api只支持java和c语言的客户端更加灵活。
- 一致性性算法:etcd采用了raft算法可以维护分布式节点的数据一致性,相比zookeeper的Paxos强一致性算法理解更加简单,zookeeper一致性算法业界出名难以理解。
- 更好的安全性:zookeeper默认没有开启用户认证机制,如果要开启需要配置acl认证权限,而etcd有更加规范的认证和权限体系,采用用户和角色机制控制节点访问权限,支持用户对每个节点的读、写、读写权限。
采用SSL认证加密体系,支持HTTPS访问协议,数据传输安全有保证。 - 数据结构:etcd的数据结构和zookeeper类似采用目录节点形式,但etcd支持对每个节点的value的多版本机制,而且可以是设置节点过期机制。
使用场景
以下总结etcd组件在各大企业常见的使用场景,基本和zookeeper使用场景一致。
- 服务发现和注册:应用微服务兴起,大型的应用按功能被分为各个微服务。各种服务之间相互调用通过服务注册和发现的方式实现。服务注册组件有zookeeper/etcd/eureka。
服务提供方:在etcd中注册服务,将服务在etcd中创建目录,并且把服务每个节点ip,在该目录下添加。当服务提供方有变化随时更新状态。
服务调用方:从etcd中获取服务提供方信息,ip和端口等信息,然后直连服务,同时watch服务节点变化,如果服务新增、挂掉,ttl失效等,客户端及时添加服务或踢掉服务。
这种服务调用方式可设置策略权重,实现负载均衡。 - 配置中心:etcd和zookeeper类似可以作为分布式配置中心。服务运行依赖的数据库连接信息、redis连接信息等可以写入etcd集群中,服务启动时从etcd中获取配置,同时利用watch机制实现类似消息发布订阅原理,实现配置动态改变。同时分布式特性免去了新建多分配置文件的操作。
- 分布式锁(分布式消息队列):etcd组件特性可以很方便实现一个分布式锁的逻辑。实现原理和zookeeper类似,zk是利用了自身的临时节点特性。
分布式锁实现的效果:当多个客户端请求过来的时候,只能有一个客户端去访问资源或者执行,并上锁,其余客户端等待释放锁,再竞争资源,是一种悲观锁。
利用ETCD实现分布式锁的思路: - 定义一个锁 /lock/business_lock这个前缀命名的一个全局唯一key为我们的锁。
- 设计客户端连接的session 每个客户端连接请求过来从etcd申请锁的时候,在锁下面定义一个key,例如客户端1,/lock/business_lock/client_uuid1,第二个客户端为/lock/business_lock/client_uuid2。每个客户端的锁要设置租约,例如为10s,这样如果客户端奔溃,key到期自动删除,相当于退出申请锁竞争,而且客户端每秒要设置“心跳”,租约到期客户端还要等待锁,则更新租约不要过期。
- 获取锁的逻辑实现etcd的中有一个维护全局事务的ID为revision,每次对key的操作,这个全局id就会加一。当每个申请锁的请求过来后,完成维护自己的key之后,要为自己创建的key来put一个值,并且获取操作的revision。同时客户端获取/lock/business_lock/前缀下面所有的key列表,其中包含每个key的revision信息,每个客户端比较
自己的revision是否为最小的,最小的客户端定义为获取锁,这个客户端可以执行自己的业务代码。同时其他客户端监听最小的revision对应的key,获取锁的客户端执行业务代码之后会释放锁,(如果客户端奔溃,租约到期也会释放锁),监听到释放锁则进行每个客户端再次比较是否获取锁,依次类推。
原理
raft协议
- etcd工作原理推荐:《ETCD技术内幕》百里燊
raft协议是etcd维护分布式集群一致性的协议。主要内容为以下几个方面:
ldeader节点选举
相关参数:
- heartbeat timeout: 心跳超时时间,也称之为广播时间,50ms。
- election timeout: 选举超时时间,每个节点为150ms ~ 300ms的随机数
- Term: 任期,从开始选举新的leader节点为下一个任期开始,到接收不到leader节点心跳时间到了election timeout。
etcd集群是一主多从的结构。etcd集群节点分为leader,follower,candidate(竞选状态)三个状态。
etcd之间的角色转换过程:集群初始化后所有节点状态为follower节点状态。leader节点会发出心跳信息到follower节点。初始化节点没有leader节点,其中一个follower节点率先等到election timeout后,角色转换为cadidate节点,Term为过期状态,开始进入下一个任期,重置election timer。
角色为cadidate节点会发起leader选举,vote for,其他followe节点收到term较大的任期投票,会投票给cadidate节点当选为leader节点,并更新election timer,更新term。cadidate节点收到一般以上的投票更新为leader节点,广播其他follower节点心跳信息。
cadidate节点未能选为leader节点或者发现leeder节点会转换为follower。
如果故障leader节点恢复后接收到任期较高的leader节点的心跳广播信息后会更新为follower节点。
如果网络分区少数节点部分没有主节点,发起投票时,preVote检查不足半数节点则不会出现无限投票状况。
etcd写入过程
leader节点处理每一次更新操作,都会记录一个全局的reversion记录。
etcd主节点接受用户请求,其他节点收到写入请求重定向到主节点。
leader节点处理每一次更新操作,都会记录一个全局的reversion。
相关参数:
每个节点本地都有日志文件,记录写入操作。
- commitIndex #本地日志提交的最大索引
- lastAppend #应用到状态机最大索引
- nextIndex[] #数组,主节点记录向follower节点发送吓一条消息的索引。
- matchIndex[] #数组,主节点已经向follower节点发送消息的最大索引。
主节点收到更新操作,写入本地日志,向其他节点广播append entries消息。收到消息的follower节点,写入本地日志,返回消息,leader节点收到半数以上节点返回,将日志记录为commit状态,并应用自身状态机,返回客户端消息,并广播消息提交信息,其他follower节点更新状态机。
综上:flowwer节点本地日志最大索引大于等于nextInex,leader节点向follower同步消息时,如果返回失败,减小nextIndex,知道匹配成功。如果leader节点切换会丢失nextIndex/matchIndex数据,新leader会从当前日志索引以上方法重试matchIndex重置为0。
综上:选举投票时,cadidate的日志索引没有自己的大则拒绝投票。
raft的linearizable语义:客户端为每个请求进行唯一编号,服务端为每个客户端设置唯一session,如果请求在集群中被执行,没有响应客户端则客户端重试请求带相同的编号,服务端不会重新执行,并返回客户端。
快照和日志持久化策略
和zookeeper类似,etcd持久化采用快照日志策略,每个节点维护自身快照和日志。
日志生成快照策略详见配置。
如果follower节点宕机比较久,leader节点会发送快照。
只读请求
leader节点处理只读请求,leader节点处理请求之前检查自己是否时最新leader节点,如果不是交付最新leader节点。如果时新任期的leader节点则提交一条空记录使之前日志全部提交,再相应只读请求。
ETCD客户端访问
ETCDCTL
etcdctl是etcd自带的客户的那工具,支持v2,v3的api
- github详细记录用法
- 或者参考中文官方文档
key操作
- 连接
开启https,需要ca证书/证书/公钥,(客户端/服务端都可以)
开启认证:用户
endpoints会连接机器列表,如果不通选择下一个。默认http协议/2379端口。
etcdctl --cacert=ca.pem --cert=client.pem --key=client-key.pem --endpoints=ip1:2379,ip2:2379,ip3:2379 --user root:123456
- 版本:v3,如果报错请检查是否添加版本环境变量。
export ETCDCTL_API=3
- 查询
get --help显示所有查询参数,下面写常用的
etcdctl get /example
--prefix #按前缀查所有key以及值,默认精确匹配
--limit 10 #显示10个
--keys-only #只显示key
--print-value-only #只显示值
-w json #可以显示key的创建reversion/修改reversion等信息。
- 信息显示:
etcdctl member list #成员列表
etcdctl endpoint status #集群状态/数据量等 -w json 可以显示当前reversion
etcdctl endpoint health #集群健康
ETCD配置
详细参考github/中文官网手册。
集群配置要保证每个节点一致。
配置可盈使用命令行/环境变量/配置文件
ETCD的搭建
生成pem的安全证书
ETCD支持https协议需要生成pem安全证书,自签TLS证书。
https协议也是是在tcp协议和http协议增加ssl协议加密http内容,是由一对公私钥组成。公钥交给公开的有资质证书管理机构,由客户端获取,私钥服务端保留。公约加密只能由私钥解密,私钥加密只能由公钥解密。也可以自签ca证书,不用交给证书管理机构。
生成证书过程参考:
生成证书
1.下载cfssl工具:
mkdir ~/bin
curl -s -L -o ~/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
curl -s -L -o ~/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
chmod +x ~/bin/{cfssl,cfssljson}
export PATH=$PATH:~/bin
2.初始化证书颁发机构:
(1)生成默认ca配置:
mkdir ~/cfssl
cd ~/cfssl
cfssl print-defaults config > ca-config.json
cfssl print-defaults csr > ca-csr.json
(2) 修改ca证书配置:
ca-config.json,注意超时时间默认为8760h(365天),添加证书类型:server/client/peer
{
"signing": {
"default": {
"expiry": "43800h"
},
"profiles": {
"server": {
"expiry": "43800h",
"usages": [
"signing",
"key encipherment",
"server auth"
]
},
"client": {
"expiry": "43800h",
"usages": [
"signing",
"key encipherment",
"client auth"
]
},
"peer": {
"expiry": "43800h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
ca-csr.json,C国家,L城市,O公司,OU组织/部门,采用rsa加密方式。
{
"CN": "My own CA",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "SH",
"O": "My Company Name",
"ST": "Shanghai",
"OU": "Org Unit 1",
"OU": "Org Unit 2"
}
]
}
生成ca, ca.pem/ca.csr为证书,ca-key为私钥。
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
ls -rlt
ca-key.pem
ca.csr
ca.pem
(3) 生成服务端证书和私钥
生成默认配置文件并修改:注意访问服务端的所有hosts和命名CN
cfssl print-defaults csr > server.json
...
"CN": "etcd cluster name",
"hosts": [
"192.168.128.96",
"192.168.128.97",
"192.168.128.98",
"node1.example.com",
"node2.example.com",
"node3.example.com",
"127.0.0.1",
"localhost"
]
...
生成server端证书和私钥(peer)
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer server.json | cfssljson -bare server
或者一步生成
echo '{"CN":"coreos1","hosts":[""],"key":{"algo":"rsa","size":2048}}' | cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server -hostname="192.168.122.68,ext.example.com,coreos1.local,coreos1" - | cfssljson -bare server
文件内容
server-key.pem
server.csr
server.pem
(4)生成客户端证书:客户端证书不需要hosts
cfssl print-defaults csr > client.json
...
"CN": "etcd cluster name",
"hosts": [""],
...
生成证书:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client
或者一步生成
echo '{"CN":"client","hosts":[""],"key":{"algo":"rsa","size":2048}}' | cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client - | cfssljson -bare client
#
client-key.pem
client.csr
client.pem
验证证书:
openssl x509 -in ca.pem -text -noout
openssl x509 -in server.pem -text -noout
openssl x509 -in client.pem -text -noout
启动etcd服务端
go程序直接启动etcd,加载配置可以通过环境变量/命令行/配置文件。建议用systemd管理。
etcd --config-file=/etc/etcd/etcd.conf.yml