目录
一. 什么是etcd
- etcd 是一款用go语言编写的分布式键值存储中间件,采用Raft算法保证分布式系统数据的强一致性,通过分布式锁、leader选举和写屏障(write barriers),来实现可靠的分布式协作,通过更好的支持watch机制,并且每秒并发支持1万左右
- 应用场景
- 服务发现(Service Discovery)
- 消息发布与订阅
- 负载均衡
- 分布式通知与协调
- 分布式锁、分布式队列
- 集群监控与Leader竞选
- etcd满足了cap理论中的cp指标(c:一致性, a可用性, p分区容错性)
- 注意etcd目前有v2与v3两个版本,有很大区别,接口不一样,存储不一样
- ectd与zookeeper两者的区别
- 在数据存在方式上: Etcd 采用简单的键值存储模型, ZooKeeper 则采用树形存储模型,每个节点都可以包含多个子节点
- 一致性角度: Etcd 基于 Raft 算法实现数据一致性,ZooKeeper 则使用 ZAB(ZooKeeper Atomic Broadcast)协议来保证数据一致性。Raft 算法具有较好的可理解性和易实现性,而 ZAB 协议则具有较好的性能和可扩展性。
- 性能方面:Etcd 的读写性能要比 ZooKeeper 快,因为它采用了更简单的数据模型和复制算法,并采用了更高效的网络传输协议
- API:Etcd 提供了 REST API 和 gRPC API,而 ZooKeeper 只支持基于 TCP 的自定义协议。
- 生态系统:由于 Etcd 支持 REST API,因此它更易于与其他开发语言和框架进行集成。而 ZooKeeper 的生态系统更加丰富,拥有更多的客户端和开源工具支持。
安装
1. docker 安装运行etcd
- 此处通过docker安装
docker run -d --name Etcd-server --network app-tier --publish 2379:2379 --publish 2380:2380 --env ALLOW_NONE_AUTHENTICATION=yes --env ETCD_ADVERTISE_CLIENT_URLS=http://etcd-server:2379 bitnami/etcd:latest
- 但是windows版本docker执行上方命令时可能会报"Error response from daemon: network xxxx not found"异常,因为在windows docker下自定义的网络类型在计算机重启之后会被注销,重启之后找不到了
- 此处手动点击docker工具运行etcd,可能需要设置ALLOW_NONE_AUTHENTICATION=yes环境变量
2. windows 安装 etcd
- 选择指定版本,下载etcd安装包github下载地址, 解压后的目录如下:(其中etcd.exe是服务端,etcdctl.exe是客户端,如果不需要特殊处理双击etcd.exe打开即可)
- etcd默认使用2379端口
- etcd启动如果需要自定义参数的话,需要指定etcd.conf配置文件,并且配置文件的内容需要是json格式,例如配置监听的端口{“listen-client-urls”:“http://localhost:12379”}
- 设置etcd使用指定配置文件启动运行: “etcd.exe --config-file etcd.conf”
- cmd控制台的几个命令
//查看当前安装版本校验是否安装启动成功
etcdctl --version
//设置API version的版本设为3(官方建议,并且3和2命令和功能方面有不少的差别)
set ETCDCTL_API=3
//通过put存储(下方表示存储了一个key为hello,值为world,存储成功显示ok)
etcdctl put hello world
//get取值
etcdctl get hello
etcd 的核心术语解释
Raft:etcd 所采用的保证分布式系统数据强一致性的算法。
Cluster:指etcd集群,由多个 Member 构成可以协同工作的 etcd 集群。
Leader:主节点用于处理所有数据提交的节点,用来协调整个集群,Raft 算法中通过竞选而产生的
Follower:从节点,Raft 算法中竞选失败的节点作为从属节点,为算法提供强一致性保证。
Candidate:候选节点,当Follower超过一定时间接收不到Leader的心跳时认为 Leader 发生了故障,会变为Candidate开始竞选
Term:指任期, 某个节点成为 Leader 到下一次竞选时间,称为一个 Term。
Node:一个 Raft 状态机实例。
Member:一个 etcd 实例,它管理着一个 Node,可用于处理客户端请求
Peer:指同一个 etcd 集群中另外一个 Member 的称呼。
Client:向 etcd 集群发送 HTTP 请求的客户端。
Lease: 租期,过期会删除
Watch: 检测机制,监控指定键值的变化
WAL:预写式日志,etcd 用于持久化存储的日志格式。
Snapshot:etcd 防止 WAL 文件过多而设置的快照,存储 etcd 数据状态。
Entry:Raft 算法中的日志的一个条目。
Proxy:etcd 的一种模式,为 etcd 集群提供反向代理服务。
Vote:选举时的一张投票。
Index:数据项编号,Raft 中通过 Term 和 Index 来定位数据。
Commit:一个提交,持久化数据写入到日志中。
Propose:一个提议,请求大部分 Node 同意数据写入。
etcd内部组成模块划分
- etcd内部分为一下模块
- etcdServer: 用于对外接收处理客户端请求
- gRpcServer: 用来处理etcd节点间的通信与信息同步
- MVCC: etcd的存储模块,提供了多版本并发控制,键值对的每次操作都会被记录到底层的BoltDB数据库中
- WAL: 预写式日志,
- Snapshot: 防止WAL日志文件过大,存储etcd中指定时刻的全量数据快照,通过WAL+Snapshot实现了故障修复能力
- Raft: 算法模块
Raft 选举过程
1. 如何选举 Leader 节点
- 在etcd集群中,每个节点内部都会运行一个随机的Timer计时器,是随机的,也就是每个节点的任期不同,假设其中一个节点任期截止优先发起选举,此时就会先一步给其它节点发送成为主节点的请求,其它节点接收到请求后就需要投票,
- 成为 Leader 后,该节点会以固定时间间隔向其他 Node 发送通知,确保自己仍是 Leader, 如果其它从节点接收不到续约通知,当到达任期后,会再次发起投票请求,再次选举
2. Raft 状态机是怎样切换的
- 在etcd集群时,内部Node 默认进入 Follower 状态,等待 Leader 发来心跳信息。若等待超时,则状态由 Follower 切换到 Candidate 候选节点状态,进入下一轮 Term 发起竞选,等到收到 Cluster 的 “多数节点” 的投票时,该 Node 转变为 Leader。Leader 有可能出现网络等故障,导致别的 Nodes 发起投票成为新 Term 的 Leader,此时原先的 Old Leader 会切换为 Follower。Candidate 在等待其它 Nodes 投票的过程中如果发现已经竞选成功了一个 Leader,那么也会切换为 Follower
3. 如何保证最短时间内竞选出 Leader防止竞选冲突
在 Candidate 候选节点状态时, 有一个随机值 times out,每个 Node 成为 Candidate 以后,times out 发起新一轮竞选的时间是各不相同的,这就会出现一个时间差。在时间差内,如果 Candidate1 收到的竞选信息比自己发起的竞选信息 Term Value 大(即对方为新一轮 Term),并且在新一轮想要成为 Leader 的 Candidate2 包含了所有提交的数据,那么 Candidate1 就会投票给 Candidate2。这样就保证了只有很小的概率会出现竞选冲突
4. Raft 某个节点宕机后如何处理
- 如果 Follower 宕机,并且剩余可用节点数量超过半数时,整个Cluster集群几乎没有影响
- 如果 Leader 宕机, Follower收不到心跳,进而发起竞选获得投票,选举新的 Leader提供服务
- 注意的是: 当etcd集群中的某个节点宕机后,不会自动调整节点总数,还是基于最初的节点总数计算选举
- 在etcd集群+Raft算法选举时,2n+1个节点正常就可以对外提供服务,etcd 在竞选前需要告诉别的 Node 自身的 Term 编号以及前一轮 Term 最终结束时的 Index 值,这些数据都是准确的,其他 Node 可以根据这些值决定是否投票。另外,etcd 严格限制 Leader 到 Follower 这样的数据流向保证数据一致不会出错
客户端连接etcd集群操作数据流程
1. 读写流程
- 操作数据流程分为读数据跟写
- 在读数据时可以从任意 Node 进行读取,因为每个节点保存的数据是强一致的
- 在写数据时有两种情况:
- 请求直接打到Leader主节点时,主节点直接写入数据,然后通知所有子节点更新,其它节点更新完毕后会响应主节点,当 “大多数节点” 响应成功,Leader才会认为成功,然后一次性提交
- 如果写请求打到从节点上,从节点会将请求向上委托为主节点,然后就是上面的步骤,由主节点将请求同步给所有从节点…
2. 如何保证数据一致性
- 也就是上面的读写流程,etcd 使用 Raft 协议来维护集群内各个节点状态的一致性, 也就是写流程始终通过Leader节点发起
3. 如何判断写入是否成功?
- 写请求到达 Leader后, 由Leader分发给其它节点, 其它节点处理完毕后会响应,当主节点接收到“多数节点” 响应后才算一次写成功, 多数节点=N/2+1,N 为总结点数
日志相关WAL/Snapshot
1. WAL预写日志
- etcd 的数据存储分为两个部分
- 内存存储:内存中的存储除了顺序化的记录下所有用户对节点数据变更的记录外,还会对用户数据进行索引、建堆等方便查询的操作。
- 持久化硬盘存储:持久化则使用 WAL(Write Ahead Log,预写式日志)进行记录存储
- WAL 日志是二进制的,是一个 LogEntry结构体,内部包含以下几个属性
- type: 有1跟0两个枚举值, 0 表示 Normal, 1 表示 ConfChange,ConfChange 表示 etcd 本身的配置变更同步,比如有新的节点加入等
- term: 任期是随机的, 每个 term 代表一个 Leader 的任期,每次 Leader 变更 term 就会变化
- index: 序号,有序递增的,代表变更序号
- data: 二进制的,用来保存= Raft Request 对象的 pb 结构
- 数据一致性都是通过同步 WAL 日志来实现的,每个 Node 将从 Leader 收到的 data apply 存储到本地
- WAL最大的作用是记录了整个数据变化的全部历程,通过WAL可以提供
- 故障快速恢复: 可以通过执行所有 WAL 中记录的修改操作,快速从最原始的数据恢复到数据损坏前的状态。
- 数据回滚undo或重做redo:因为所有的修改操作都被记录在 WAL 中,需要回滚或重做,只需要反向或正向执行日志重放即可
2. Snapshot 快照
- 在使用WAL预写日志时,随着使用时间WAL文件会越来越大,防止磁盘爆满,etcd默认每10000 条记录做一次 Snapshot快照,拿到快照后,删除WAL文件即可
etcd的watch机制
- Etcd 实现 watch 机制的原理是通过长轮询的方式来实时监视指定的键是否发生变化实现的,实现步骤:
- 客户端通过 Etcd 的 Watch API 发起一个 watch 请求,指定要监视的键。
- Etcd 服务器接收到 watch 请求后,会将客户端的请求信息保存在内部的 watch 列表中,并等待键发生变化或超时。
- 当某个键发生变化时,Etcd 服务器会检查内部的 watch 列表,找到对应的 watch 请求,并将变化的信息返回给客户端。
- 客户端收到变化的信息后,可以根据需要进行相应的处理,例如更新缓存、重新加载配置等操作。
- 客户端收到变化的信息后,可以选择继续保持 watch 的状态,再次发起 watch 请求,以便持续监视键的变化。
- 注意Etcd 的 watch 机制是基于长轮询实现的,当客户端发起 watch 请求后,如果指定的键没有发生变化,Etcd 服务器会一直等待直到超时,然后返回一个空响应
etcd gateway
1. etcd gateway
- etcd 网关是一个简单的 TCP 代理,转发网络数据到 etcd 集群
- 网关支持多个 etcd 服务器端点,并采用简单的轮训策略(round-robin policy)。它只路由到可用的端点并对其客户端隐藏故障。未来可能会支持其他重试策略,例如加权轮询
- 使用etcd网关的原因: 在使用etcd 集群时,若集群中的节点地址变更,在没有网关时每个连接etcd的服务都需要更改配置,在大规模集群环境下,重新配置的操作既造成了重复又容易出错, 当用来etcd网关后,服务连接网关,由网关转发网络数据到 etcd 集群
- 高级集群管理系统比如 Kubernetes 原生支持服务发现。应用可以访问使用 DNS 名称 或者系统管理的虚拟IP地址来访问 etcd 集群,例如kube-proxy,就可以不使用etcd网关
- 开启etcd 网关
//1. --endpoints:是以逗号分隔的、用于转发客户端连接的 etcd 服务器目标列表
//默认值为127.0.0.1:2379,url不支持https,因为网关并不能决定 TLS。
//2. --discovery-srv:使用DNS服务发现
//3. --listen-addr :绑定的接口和端口,用于接受客户端请求,默认配置为127.0.0.1:23790
//4. --retry-delay: 重试连接到失败的端点延迟时间,默认为 1m0s
//5. --insecure-discovery :接受不安全或容易受到中间人攻击的 SRV 记录,默认为 false。
//6. --trusted-ca-file: etcd群集客户端TLS CA文件的路径,
//用于验证从SRV发现返回的端点。请注意,它仅用于验证发现的端点,
//而不是创建用于数据传输的连接。网关从不终止TLS连接或代表其客户端创建TLS连接
etcd gateway start --endpoints=http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --listen-addr=192.168.70.100:23790
2. gRPC-Gateway
- 除了etcdGateway外,etcd v3 使用 gRPC作为其消息传递协议,内部包含一个基于 gRPC 的 Go 客户端和一个命令行实用程序 etcdctl,用于通过 gRPC 与 etcd 集群通信
- 对于不支持 gRPC 的语言,etcd 提供了一个 JSON gRPC 网关,此网关提供 RESTful 代理,将 HTTP/JSON 请求转换为 gRPC 消息
- 注意在 HTTP 请求体中的 JSON 对象的 key 和 value 字段都被定义成了 byte 数组,因此必须在 JSON 对象中,使用 base64 编码对内容进行处理
- 添加示例:
- range示例
- watch示例
curl -N http://192.168.70.100:23790/v3/watch -X POST -d '{"create_request": {"key":"dGVzdDE="} }' &
- gRPC-gateway 不支持使用 TLS 公用名进行身份验证,考虑安全问题gRPC-Gateway 中通过authAPI 接口提供了角色权限的相关认证,通过 /v3/auth 接口设置认证,流程: 创建用户–>创建角色–>设置角色–>开启认证,例如:
gRpc proxy
1. 基本解释
- gRPC proxy 是是etcd的代理, 是对 etcd client 的封装,不同客户端请求复用同一个client,用来减少etcd server的负载
- gRPC proxy 合并了监视和 Lease API 请求,并且实现了键值对的读请求缓存
- gRPC代理支持多个etcd服务器端点,启动后会随机选择一个etcd服务器端点来使用,服务于所有请求,如果gRPC代理检测到端点故障会切换到另一个端点,所以对其客户端隐藏故障。将来可能会支持其他重试策略,例如加权循环
- 使用etcd grpc-proxy start的命令开启 etcd 的 gRPC proxy 模式
etcd grpc-proxy start --endpoints=http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --listen-addr=192.168.70.100:2379
//etcd gRPC代理在端口2379上启动并侦听,它将客户端请求转发到上面提供的三个端点之一
//通过代理发送请求
[root@localhost etcd-cluster]# ETCDCTL_API=3 etcdctl --endpoints=192.000.80.100:2379 put foo bar
OK
[root@localhost etcd-cluster]# ETCDCTL_API=3 etcdctl --endpoints=190.000.80.100:2379 get foo
foo
bar
2. 客户端端点同步和名称解析
- gRPC 代理支持在启动时通过写入相同的前缀端点名称进行注册。这样可以使客户端将其端点与具有一组相同前缀端点名的代理端点同步,进而实现高可用性
etcd grpc-proxy start --endpoints=127.0.0.1:12379 --listen-addr=192.000.80.100:2379 --advertise-client-url=192.000.80.100:2379 --resolver-prefix="___grpc_proxy_endpoint" --resolver-ttl=60
etcd grpc-proxy start --endpoints=127.0.0.1:12379 --listen-addr=192.000.80.100:23791 --advertise-client-url=192.000.80.100:23791 --resolver-prefix="___grpc_proxy_endpoint" --resolver-ttl=60
- 在上面的启动命令中,将需要加入的自定义端点–resolver-prefix设置为___grpc_proxy_endpoint。启动成功之后,我们来验证下,gRPC 代理在查询成员时是否列出其所有成员作为成员列表,执行如下的命令
ETCDCTL_API=3 etcdctl --endpoints=192.000.80.100:2379 member list --write-out table
3. 可伸缩的 watch API
- 如果客户端监视同一键或某一范围内的键,gRPC 代理可以将这些客户端监视程序c-watcher合并为连接到 etcd 服务器的单个监视程序s-watcher,当 watch 事件发生时,代理将所有事件从 s-watcher 广播到其 c-watcher
也就是: 假设 N 个客户端监视相同的 key,则 gRPC 代理可以将 etcd 服务器上的监视负载从 N 减少到 1
- 用户可以部署多个 gRPC 代理,进一步分配服务器负载。
- 注意:
- 由于网络延迟或缓冲的未传递事件,合并的 s-watcher 可能与 etcd 服务器不同步。
- 如果没有指定监视版本,gRPC 代理将不能保证 c-watcher 从最近的存储修订版本开始监视。例如,客户端从修订版本为 1000 的 etcd 服务器监视,则该监视者将从修订版本 1000 开始。如果客户端从 gRPC 代理监视,则可能从修订版本 990 开始监视,类似的限制也适用于取消。取消 watch 后,etcd 服务器的修订版可能大于取消响应修订版
4. 可伸缩的 lease API
- gRPC 代理支持将 lease 流合并, 假设有 N 个客户端正在更新租约,则单个 gRPC 代理将 etcd 服务器上的流负载从 N 减少到 1。在部署的过程中,可能还有其他 gRPC 代理,进一步在多个代理之间分配流。
5. 命名空间的实现
- 为了实现etcd保存的数据共享并且不相互干扰地运行,代理可以对etcd 键空间进行分区
- 代理提供"–namespace"标志时,所有进入代理的请求都会转换为在键上具有用户定义的前缀。普通的请求对 etcd 集群的访问将会在我们指定的前缀即 --namespace 下,而来自代理的响应将删除该前缀;而这个操作对于客户端来说是透明的,根本察觉不到前缀
6. 其它功能
- 监控检查: gRPC 代理为–endpoints定义的 etcd 成员提供了实现监控检查的/health和 Prometheus 的/metrics接口
- TLS 加密的代理: gRPC 代理可以实现客户端与 gRPC 代理之间通过 HTTP 方式通信,gRPC 代理与 etcd 集群之间通过 TLS 加密通信
etcd的优点
- 部署简单,提供了httpApi使用简单
- 存储,数据分层存储在文件目录中
- 支持Watch机制
- 安全支持SSL证书验证
- 高性能, 单实例支持每秒2k并发请求
- 一致性,可靠性,支持集群搭建,基于Raft算法选举节点选举,保证了cp一致性和分区容错性
如何保证数据一致性?
- etcd 使用 Raft 协议来维护 Cluster 内各个 Nodes 状态的一致性。简单的说,etcd Cluster 是一个分布式系统,由多个 Nodes 相互通信构成整体对外服务,每个 Node 都存储了完整的数据,并且通过 Raft 协议保证每个 Node 维护的数据是一致的
etcd Cluster 中的每个 Node 都维护了一个状态机,并且任意时刻,Cluster 中至多存在一个有效的主节点,即:Leader Node。由 Leader 处理所有来自客户端写操作,通过 Raft 协议保证写操作对状态机的改动会可靠的同步到其他 Follower Nodes
什么是Raft 算法
etcdctl客户端常用操作指令
watch: 检测指定键或前缀的事件流
etcd 租约lease
- lease为租约,类似与redis的ttl,etcd的键值对可以绑定到设置的租约上,实现存活周期控制,常用来服务的心跳,为了保持服务状态,服务会定时刷新租约,一担租约到期就会删除对应服务的数据
- 通过租约id可以撤销租约,撤销租约将会删除所有附带的key
- 刷新租约
- 查询租约
与z区别
- 为什么不使用zookeeper添加链接描述
- 部署简单, 支持数据持久化,支持ssl客户端安全认证