背景
下面我将以这个思维导图来总结一下etcd,包括etcd的介绍,Raft协议,etcd集群,可能遇到的问题,etcdctl。
简介
etcd是Core OS基于Raft协议开放的分布式key- value存储,可用于服务发现,共享配置以及一致性保障(数据库选主,分布式锁等)。
简单:使用Rest API,可以使用curl来调用
安全:可选的SSL 客户端证书认证;
快速:单实例每秒1000次写操作,2000+次读操作;
可靠:使用 Raft 算法保证一致性。
主要功能
key-value存储
监听机制
key 的过期及续约机制,用于监控和服务发现
原子 Compare And Swap 和Compare And Delete,用于分布式锁和 leader 选举。(对key设置或者删除需要满足一定的条件才能执行,条件如下)
- prevExist: key当前赋值前是否存在
- prevvalue: key 当前赋值前的值
- prevlndex: key Index
使用场景
key-value存储,应用程序可以读取和写入 etcd 中的数据
- 采用 KV 型数据存储,一般情况下比关系型数据库快。
- 支持动态存储(内存)以及静态存储(磁盘)。
- 分布式存储,可集成为多节点集群。
- 存储方式,采用类似目录结构(B+tree)。
只有叶子节点才能真正存储数据,相当于文件。
叶子节点的父节点一定是目录,目录不能存储数据。
服务注册与发现(使用较多)
- 强一致性、高可用的服务存储目录。
- 可以对注册的key设置ttl,到期后key会自动删除,定时保持服务的心跳以达到健康检查的效果
基于监听机制的分布式异步系统
- 在分布式系统中,最常用的一种组件间通信方式就是消息发布与订阅。
- 创建一个消息中心,生产者在这个消息中心发布消息,消费者订阅他们关心的主题,一旦主题有消息发布,就会实时通知订阅者。
- 应用在启动的时候主动从 etcd 获取一次配置信息,同时,在 etcd 节点上注册一个Watcher 并等待,以后每次配置有更新的时候,etcd 都会实时通知订阅者,以此达到获取最新配置信息的目的。
安装使用
安装链接:https://github.com/etcd-io/etcd/releases
第三方库和客户端工具
命令行客户端:etcdctl(后文会介绍)
Go 客户端:go-etcd
Java 客户端:jetcd
Python 客户端:python-etcd
Raft协议
简介
Raft 协议基于 quorum 机制,即大多数同意原则,任何的变更都需超过半数的成员确认。
客户端发送请求给服务端,服务端有一个一致性模块,他会把变更请求发给所有其他peer,同时把变更请求写入到自己的日志模块中,当其他的成员处理了这个变更请求后,把处理结果返回给发起方(leader),发起方会发起确认,只要多数人确认了这个变更,发起方才会把这个变更写入到状态机里面。读取数据也是从状态机里面读取。
有一个网站可以很直观的理解Raft协议。
http://thesecretlivesofdata.com/raft/
节点有三种角色:
follower,candidate,leader。
选举过程中有两个超时时间:
election timeout,heartbeat timeout。
节点是偶数的情况:
会导致多次无效选举,由于选举过程中集群时不能写入数据的,所以会导致etcd集群写入性能降低。
第四种角色 learner
- 当etcd集群需要增加节点时,新节点与 Leader 的数据差异较大,需要较多数据同步才能跟上 Leader 的最新数据。
- 由于同步数据,leader的网络带宽很可能被用尽,进而使得 leader 无法正常保持心跳。
- 进而导致 follower 重新发起投票,其他节点的commitid都比他大,就会导致无效投票。
- learner 角色只接收数据而不参与投票,因此增加 learner 节点时,集群的quorum保持不变。
etcd如何实现Raft
1.选举方法
- 初始启动时,节点处于 Follower 状态并被设定一个 election timeout,如果在这段时间内没有收到来自 Leader 的heartbeat,节点将发起选举。将自己切换为candidate 之后,向集群中其它 Follower 节点发送投票,询问其是否选举自己成为 Leader。
- 当收到来自集群中过半数节点的投票后,节点即成为 Leader,开始接收保存 client 的数据并向其它的Follower 节点同步日志。如果没有达成一致,则 candidate 随机选择一个election timeout(150ms~ 300ms)再次发起投票,直到某个Follower成为 Leader。
- Leader 节点会定时向 Follower 发送 heartbeat 来保持其地位。
- 如果其它 Follower 在election timeout 期间都没有收到来自 Leader 的 heartbeat,那么会将自己的状态切换为 candidate 并发起选举。每成功选举一次,新 Leader 的任期(Term)都会比之前 Leader 的任期大1。
2.日志复制
- client会和 Leader 进行交互。当 Leader 接收到client的请求后先把该请求追加到本地的 Log 中,然后通过 heartbeat 把该 Entry 同步给其他 Follower。
- Follower 接收到日志后记录日志然后向 Leader 发送ACK,当Leader 收到大多数 Follower 的ACK信息后将该日志设置为已提交并追加到本地磁盘中,并通知客户端。
- 然后在下个 heartbeat 中 Leader 将通知所有的 Follower 将该日志存储在自己的本地磁盘中。
3.安全性
用于保证每个节点都执行相同序列的安全机制。
如当某个 Follower 在当前 Leader commit Log 时变得不可用了,稍后可能该 Follower 又会被选举为 Leader,这时新 Leader 可能会用新的 Log 覆盖先前已 committed 的 Log,这就会导致
节点执行不同序列。
Safety 就是用于保证选举出来的 Leader 一定包含先前committed Log 的机制。
选举安全性(Election Safety):
每个任期(Term)只能选举出一个 Leader。
Leader 完整性(Leader Completeness):
指 Leader 日志的完整性,当 Log 在Term1被 Commit 后,那么以后Term2、Term3 等的 Leader 也必须包含该 Log。
Raft 在选举阶段就使用 Term 的判断用于保证完整性:当 Candidate 的Term 较大或 Term 相同 Index 更大则投票,否则拒绝该请求。
4.失效处理
Leader 失效
当 Leader 挂了之后,其他没有收到 heartbeat 的 Follower 节点会发起新的选举,而当 Leader 恢复后由于步进数小会自动成为 Follower (日志也会被新 Leader 的日志覆盖)。
Follower 节点不可用
集群中的日志内容始终是从 Leader节点同步的,只要这一节点再次加入集群时重新从 Leader 节点处复制日志即可。
多个 candidate
如果有多个candidate ,将随机选择一个等待间隔(150ms~ 300ms)再次发起投票,直到 Follower 成为 Leader。
5.wal 日志 (write ahead log)
wal 日志是二进制的,解析出来后是数据结构 LogEntry。
- type,一种是0表示 Normal,1表示 ConfChange(ConfChange 表示etcd 本身的配置变更同步,比如有新的节点加入等)。
- term,代表Leader的任期。
- index,这个序号是严格有序递增的,代表变更序号。
- 二进制的 data,保存 request 对象的 pb 结构。比如请求key=value,会把整个请求当作data。
etcd 源码下有个 tools/etcd-dump-logs,可以将 wal 日志 dump 成文本查看,可以协助分析 Raft 协议。
https://github.com/etcd-io/etcd/tree/main/tools/etcd-dump-logs
下载下来后,执行
go install -v ./etcd-dump-logs/
这里报错的话请跳转:https://wghdr.top/archives/277
cp $GOPATH/bin/etcd-dump-logs /usr/sbin
mkdir /root/etcd-dump
cp /var/lib/etcd/member /root/etcd-dump
etcd-dump-logs etcd-dump
注意这里需要直接指定目录,不能指定wal文件。可以看到wal的 metadata和entries。其中entries包括lastIndex,term,index,type,data。
Raft 协议本身不关心应用数据,也就是 data 中的部分,一致性都通过同步wal 日志来实现,每个节点将从主节点收到的data append 到本地的存储。
Raft 只关心日志的同步状态,如果本地存储实现的有 bug,比如没有正确地将 data app 到本地,也可能会导致数据不一致。
6.存储机制
etcd v3 store分为两部分:
一部分是内存中的KVStore,包括了索引kvindex。
另外一部分是后端存储。backend 可以对接多种存储,当前使用的是boltdb。
boltdb 是一个单机的支持事务的 KV 存储,etcd 的事务是基于 boltdb 的事务实现的。
当一个数据要存储在etcd中,会同时会存索引和落盘,就是通过boltdb落盘。
etcd本身是支持监听的,监听的对象就是存在WatchableStore。
etcd 在boltdb 中存储的 key 是reversion,value 是etcd 自己的key-value 组合,也就是说 etcd 会在 boltdb 中把每个版本都保存下,从而实现了多版本机制。(MVCC)
etcd 提供了命令和设置选项来控制 compact,同时支持 put 操作的参数来精确控制某个 key 的历史版本数。
内存kvindex 保存的就是key 和 reversion 之间的映射关系,用来加速查询。
reversion主要由两部分组成,第一部分 main rev,每次事务进行加一,第二部分 sub rev,同一个事务中的每次操作加一。
get key -wjson,就可以看到key的revision和mod_revision。k8s中对象有resource version,当key发生变化时,mod_revision也会发生变化,对象里面有个属性会记录mod_revision,k8s在读取这个对象的时候,就会把mod_revision当作它的resource version。
resource version:
mod_revision:
数据存储过程:
1.假设集群已经选好了leader,客户端发起一个写请求。
- 如果请求发到了follower上,首先会经过预检查,kvserver,转发到一致性模块。一致性模块会判断请求是不是leader,不是的话转发到leader。
- 如果请求发到了leader上,经过预检查,kvserver,转发到一致性模块,leader直接处理。
预检查:
- 配额:etcd处理数据的大小是有限制的。
- 限速:如果客户端频繁的来写操作,会压垮服务端,所以需要限速。
- 鉴权:认证和鉴权。
- 包大小:etcd会看数据包大小是否超过1.5M。如果超过了,etcd会反复去确认数据,包括数据同步,做索引,查询等,会导致etcd的开销非常大,集群性能直线下降。
2.预检查通过后,数据会被发送到主模块KVServer,假如这里KVServer接收到了y=9的写请求,通过propose的方法(有一个MsgProp的入参)发送到一致性模块。
3.一致性模块会做两件事情:选主和日志复制。日志复制首先会在etcd的内存中构建raftlog(storage的数据结构),接着把y=9这个信息写入到unstable中。
4.同时会把这个信息通过日志模块写入到wal log。日志是需要落盘的,不能每写一次就落一次,效率太低了,所以先写入到buffer里面,再通过fsync周期性的去落盘。
写wal log的同时,还会有一个goroutine把同样的message发送(MsgApp/message append)到所有follower。follower收到请求后,也会写wal log,并且写完会回复(MsgAppResp/message append response)给leader。
5.leader收到半数以上的确认,认为数据已经commit了,它会更新MatchIndex,因为wal log中的index是自增长的。并且会把raft log中unstable的值写入到commited中,接着会请求整个状态机来记录这条数据。
状态机是通过mvcc模块实现的,包括了kvIndex和boltdb。kvindex会记录key就是写入的key即y,value是这个key所有关联的版本信息。value包括了modified(mod_revision),generation(这个key可能经过多次变更,即更新了多少代,每次变更的的revision是多少)
boltdb的key是revision,value是这个y=9的完整信息。
7.etcd 的数据一致性
wal log的index是自增的,leader也有一个Match Index,代表leader和哪个index保持一致了。超过半数确认的日志即当作commit,即图中MatchIndex=index=7。当leader挂了,c因为log的index比之前的commit index小,就不能当作leader。
8.watch机制
etcd V3 的 Watch 机制支持Watch 某个固定的 key,也支持Watch 一个范围(可以用于模拟目录的Watch),所以watchGroup 包含两种 watcher,一种是 key watchers,数据结构是每个 key 对应一组 watcher,另外一种是range watchers,数据结构是一个 IntervalTree,方便通过区间查找到对应的 watcher。
同时,每个 WatchableStore 包含两种 watcherGroup,一种是synced,一种是 unsynced,前者表示该 group 的 watcher 数据都已经同步完毕,在等待新的变更,后者表示该 group 的watcher 数据同步落后于当前最新变更,还在追赶。
当 etcd 收到客户端的 watch 请求,如果请求携带了 revision参数,则比较请求的revision和存储中当前的revision,如果大于当前 revision,则放入 synced 组中,否则放入 unsynced组。
同时,etcd 会启动一个后台的 goroutine 持续同步 unsynced 的 watcher,然后将其迁移到synced。
在这种机制下,etcd V3 支持从任意版本开始 watch,没有 V2 的1000条历史 event 表限制的问题(当然这是指没有压缩compact的情况下)。
etcd集群
高可用解决方案
1.etcd-opreator
https://github.com/coreos/etcd-operator
2.helm chart(bitnami或者其他)
3.堆叠式
这种拓扑将相同节点上的apiserver和 etcd 成员耦合在一起。优点在于建立起来非常容易,并且对副本的管理也更容易。
但是,堆叠式存在耦合失败的风险。如果一个节点发生故障,则 etcd成员和控制平面实例都会丢失,并且集群冗余也会受到损害。可以通过添加更多apiserver来减轻这种风险。因此为实现集群高可用应该至少运行三个堆叠的 Master 节点。
4.外部 etcd 集群的高可用拓扑
该拓扑将控制平面和 etcd 成员解耦。如果丢失一个 Master 节点,对etcd 成员的影响较小,并且不会像堆叠式拓扑那样对集群冗余产生太大影响。
但是,此拓扑所需的主机数量是堆叠式拓扑的两倍。至少需要三个主机用于apiserver,外部etcd集群三个主机用于etcd 集群。
k8s中如何使用etcd
etcd配置文件
cat etcd.yaml
#成员相关参数
--name 'default'
Human-readable name for this member.
--data-dir '${name}.etcd'
Path to the data directory.
--listen-peer-urls 'http://localhost:2380'
List of URLS to listen on for peer traffic.
--listen-client-urls 'http://localhost:2379'
List of URLS to listen on for client traffic.
#集群相关参数
--initial-advertise-peer-urls 'http://localhost:2380'
List of this member's peer URLS to advertise to the rest of the cluster.
--initial-cluster 'default=http://localhost:2380'
Initial cluster configuration for bootstrapping.
--initial-cluster-state 'new'
Initial cluster state ('new' or 'existing').
--initial-cluster-token 'etcd-cluster'
Initial cluster token for the etcd cluster during bootstrap.
--advertise-client-urls 'http://localhost:2379'
List of this members client URLS to advertise to the public.
#安全相关参数
--cert-file ""
Path to the client server TLS cert file.
--key-file ""
Path to the client server TLS key file.
--client-crl-file ""
Path to the client certificate revocation list file.
--trusted-ca-file ""
Path to the client server TLS trusted CA cert file.
--peer-cert-file ""
Path to the peer server TLS cert file.
--peer-key-file""
Path to the peer server TLS key file.
--peer-trusted-ca-file ""
Path to the peer server TLS trusted CA file.
apiserver配置文件
cat apiserver.yaml
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
k8s的对象在etcd中的存储路径
etcdctl get --prefix --keys-only /
etcd在集群中的位置
etcd 是Kubernetes 的后端存储。只和apiserver进行交互。
对于每一个 Kubernetes Object,都有对应的storage.go 负责对象的存储操作。
https://github.com/kubernetes/kubernetes/blob/master/pkg/registry/core/pod/storage/storage.go
实践
1.如何实现etcd集群高可用
-
多少个 master 最适合?1个?3个?5个?要看时效性的要求。
如果是1个master,性能最高,数据安全性没法保证。
如果是3个master,数据有冗余,但是对运维要求很高,如果坏了一个master,集群仍可以正常工作,但是需要立即修复。
如果是5个master,性能最差,需要的机器也更多,但是对运维的压力要小。- 保证高可用是首要目标。
- 所有写操作都要经过 leader。
- peer 多了是否能提升集群并读操作的并发能力?(不会,因为peer越多,数据写入时需要确认的人就越多)
- apiserver 的配置只连本地的 etcd peer。
- apiserver 的配置指定所有etcd peers,但只有当前连接的 etcd member 异常,apiserver 才会换目标。
- 需要动态flex up(扩缩容)吗?etcd的动态扩缩容是很麻烦的,因为需要基于配置,除非到每个member中都刷一下配置。
-
保证 apiserver 和 etcd 之间的高效性通讯
- apiserver 和 etcd 部署在同一节点
- apiserver 和 etcd 之间的通讯基于 gRPC(连接复用)
- 对于同一个group的所有对象,都是共用一个TCP连接。
- HTTP/2的特性
- Stream quota(定义了一个TCP连接里面支持多少个并发的HTTP/2请求)
- 带来的问题?对于大规模集群,会造成链路阻塞,node无法上报状态信息,变成unknown状态,node上的pod被control manager的pod驱逐器驱逐,导致大规模的pod搬迁。
- 10000个 pod,一次 list 操作需要返回的数据可能超过100M
2.etcd 存储规划
- 本地 vs 远程?
- Remote Storage
- 优势是假设永远可用,但是基于网络,网络可能会抖动,etcd可能会出问题。
- 假如多数用local,少数用remote,也不行。因为local的IO很快,remote的IO慢,跟不上locla的写入速度,慢慢的就掉队了,集群状态有问题。
- 最佳实践:
- local SSD + remote backup
- 利用 local volume 分配空间,让这块盘只给etcd使用。
- Remote Storage
- 多少空间?
- 与集群规模相关。思考:为什么每个 member 的 DB size 不一致?和compact和defrag有关。
etcd安全性
- peer和 peer 之间的通讯加密
- TLS 的额外开销
- 运营复杂度增加
- 数据加密
- 是否有需求
- Kubernetes 提供了针对 secret 的加密
https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
etcd事件分离
对于大规模集群,大量的事件会对 etcd 造成压力。event是用来做审计的,可以再单独起一个etcd,专门用来记录event。集群的性能会更高。
可以在apiserver 启动脚本中配置:
cat apiserver.yaml
--etcd-servers-overrides=/events#https://localhost:4002
如何减少网络延迟
数据中心内的 RTT 大概是数毫秒,国内的典型 RTT 约为50ms,两大洲之间的 RTT 可能慢至400ms。因此建议 etcd 集群尽量同地域部署。(heartbeat timeout,election timeout)
当客户端到 Leader 的并发连接数过多,可能会导致其他 Follower 节点发往 Leader 的请求因为网络拥塞而被延迟处理。在 Follower节点上,可能会看到这样的错误:
dropped MsgProp to 247ae21ff9436b2d since streamMsg's sending buffer is full
可以在节点上通过流量控制工具(Traffic Control)提高 etcd 成员之间发送数据的优先级来避免。
如何减少磁盘IO延迟
对于磁盘IO延迟,一般写延迟约为10毫秒。对于SSD延迟通常低于1毫秒。HDD或者网盘在大量数据读写操作的情况下延时会不稳定。因此强烈建议使用 SSD。
同时为了降低其他应用程序的IO操作对 etcd 的干扰,建议将 etcd 的数据存放在单独的磁盘内。
也可以将不同类型的对象存储在不同的若干个 etcd 集群中,比如将频繁变更的 event 对象从主etcd 集群中分离出来,以保证主集群的高性能。在 apiserver 中是可以通过参数配置的。这些etcd集群最好也分别能有一块单独的存储磁盘。
如果不可避免地,etcd 和其他的业务共享存储磁盘,那么就需要通过下面 ionice 命令对 etcd 服务设置更高的磁盘IO优先级,尽可能避免其他进程的影响。
$ ionice -c2 -n0 -p 'pgrep etcd'
保持合理的日志文件大小
etcd 以日志的形式保存数据,无论是数据创建还是修改,它都将操作追加到日志文件,因此日志文件大小会随着数据修改而线性增长。
当Kubernetes 集群规模较大时,对 etcd 集群中的数据更改也会很频繁,集群日志文件会迅速增长。
为了有效降低日志文件大小,etcd 会以固定周期创建快照保存系统的当前状态,并移除旧的日志文件。另外当修改次数累积到一定的数量(默认是10000,通过参数“–snapshot-count”指定)。etcd 也会创建快照文件。
如果 etcd 的内存使用和磁盘使用过高,可以先分析是否数据写入频度过大导致快照频度过高,找到是哪个bad user写入导致。确认后可通过调低快照触发的國值来降低其对内存和磁盘的使用。
设置合理的存储配额
存储空间的配额用于控制 etcd 数据空间的大小。
合理的存储配额可保证集群操作的可靠性。一般是8G。
如果没有存储配额,etcd 就可以利用整个磁盘空间,etcd 的性能会因为存储空间的持续增长而严重下降,甚至可能导致机器宕机。
如果设置的存储配额太小,一旦其中一个节点的存储空间超出了存储配额,etcd 就会有alarm告警,并且集群将处于只读模式。只有在释放足够的空间、消除后端数据库的碎片和清除存储配额告警之后,集群才能恢复正常。
自动压缩历史版本
etcd 会为每个键都保存了历史版本。
为了避免出现性能问题或存储空间消耗完导致写不进去的问题,这些历史版本需要进行周期性地压缩。
压缩历史版本就是丢弃该键给定版本之前的所有信息,节省出来的空间可以用于后续的写操作。
etcd 支持自动压缩历史版本。在启动参数中指定参数“–auto-compaction",其值以小时为单位。也就是etcd 会自动压缩该值设置的时间窗口之前的历史版本。
保留1小时到历史
etcd --auto-compaction-retention=1
压缩版本到3
etcdctl compact 3
定期消除碎片化
压缩历史版本,虽然抹去了 etcd 存储空间的数据,但是etcd 存储空间中会出现碎片。这些碎片无法被后台存储使用,却仍占据节点的存储空间。因此定期消除存储碎片,将释放碎片化的存储空间,重新调整整个存储空间。
清理碎片
etcdctl defrag
优化运行参数
当网络延迟和磁盘IO延迟固定的情况下,可以优化 etcd 运行参数来提升集群的工作效率。
etcd 基于 Raft 协议进行 Leader 选举,当 Leader 选举以后才能开始数据读写操作,因此频繁的 Leader 选举会导致数据读写性能显著降低。可以通过调整心跳周期(Heatbeat Interval)和选举超时时间(Election Timeout),来降低 Leader 选举的可能性。
心跳周期是控制 Leader 以何种频度向 Follower 发起心跳通知。
心跳通知除表明 Leader 活跃状态之外,还带有待写入数据信息,Follower 依据心跳信息进行数据写入,默认心跳周期是100ms。
选举超时时间定义了当 Follower 多久没有收到 Leader 心跳,则重新发起选举,该参数的默认设置是1000ms,即Heatbeat Interval的10倍。
如果 etcd 集群的不同实例部署在延迟较低的相同数据中心,通常使用默认配置即可。
如果不同实例部署在多数据中心或者网络延迟较高的集群环境,则需要对心跳周期和选举超时时间进行调整。
建议心跳周期参数推荐设置为接近 etcd 多个成员之间平均数据往返周期的最大值,一般是平均 RTT 的0.55-1.5倍。
如果心跳周期设置得过低,etcd 会发送很多不必要的心跳信息,从而增加 CPU 和网络的负担。如果设置得过高,则会导致选举频繁超时。
选举超时时间也需要根据 etcd 成员之间的平均 RTT时间来设置。选举超时时间最少设置为 etcd 成员之间 RTT 时间的10倍,以便对网络波动。
心跳间隔和选举超时时间的值必须对同一个 etcd 集群的所有节点都生效,如果各个节点配置不同,就会导致集群成员之间协商结果不可预知而不稳定。
备份
etcd 的默认工作目录下会生成两个子目录:wal 和snap。
wal 是增量的日志。所有数据的修改在提交前,都要先写入 wal 中。
snap 是用于存放快照数据。为防止 wal 文件过多,etcd 会定期(当 wal 中数据超过10000条记录时,由参数“-snapshot-count”设置)创建快照。当快照生成后,wal 中数据就可以被删除了。
如果数据遭到破坏或错误修改需要回滚到之前某个状态时,方法就有两个:一是从快照中恢复,但是未被拍入快照的数据会丢失;是执行所有 wal 中记录的修改操作,从最原始的数据恢复到数据损坏之前的状态,但恢复的时间较长。
备份方案实践
官方推荐 etcd 集群的备份方式是定期创建快照。
根据集群对 etcd 备份粒度的要求,可适当调节备份的周期。在生产环境中实测,拍摄快照通常会影响集群当时的性能,因此不建议频繁创建快照。
但是备份周期太长,就可能导致大量数据的丢失。这里可以使用增量备份的方式。
etcd集群中有一个备份的客户端,是以sidecar形式。sidecar会watch leader,30分钟做一次snapshot,从快照结束的revision开始监听etcd事件,接着把event每10秒一次写入到磁盘。
创建快照
etcdctl snapshot save /root/etcd$(date +%Y%m%d_%H%M%S)_snapshot.db
查看快照状态
etcdctl snapshot status etcd20220215_001358_snapshot.db -w table
恢复快照
etcdctl snapshot restore etcd20220215_001358_snapshot.db
常见问题
1.数据备份
备份方案
- etcd 备份:备份完整的集群信息,灾难恢复。会占用磁盘空间。
- Kubernetes event
- 增强版 减少了snapshot频次,保证了数据实效性
etcd-backup-opreator,每30分钟创建一次snapshot,在backup的sidecar里面添加etcd watch client,监听备份revision后面的所有事件,把事件每1分钟以增量的方式写入到remote volume中。
当restore时,先去snapshot里拉取30分钟的快照,再去remote volume中拉取这一段时间的增量,再snapshot基础上把增量reply出来。
频度
- 时间间隔太长:
- 是否允许user data lost?
- 如果有外部资源配置,如负载均衡等,能否接受数据丢失导致的leak?
- 时间间隔太短:
- 对 etcd 的影响:
- 做 snapshot的时候,etcd 会锁住当前数据。
- 并发的写操作需要开辟新的空间进行增量写,导致磁盘空间增长。
- 对 etcd 的影响:
如何保证备份的时效性,同时防止磁盘爆掉
备份会产生磁盘碎片,Auto defrag?
2.查询apiserver
比如查询集群中所有的pod时,如果pod数量过多,可以采用分页查询的方式。
第一次查询会返回一个continue_token,第二次查询加上这个token就可以接者上次继续查询。
resourceVersion
- 单个对象的resourceVersion:对象的最后修改 resourceVersion
假如两个client要修改同一个数据,client拿到了这个数据的resourceVersion,也就是mod_revision,第一个修改成功了,mod_revision和resourceVersion发生变化,第二个再去修改,etcd会判断请求不是最新的,会返回400。 - List 对象的 resourceVersion:生成 list response 时的 resourceVersion
- List 行为
- List 对象时,如果不加 resourceVersion,意味着不相信apiserver的catch,需要最新的数据,请求会击穿APIServer缓存,直接发送至 etcd。
- APIServer 通过 Label 过滤对象查询时,过滤动作是在APIServer 端,etcd是没有过滤能力的,APIServer 需要向 etcd 发起全量查询请求。
3.遭遇到的陷阱
频繁的 leader election
etcd 分裂(member挂载remote volume)
etcd 不响应
与 apierver 之间的链路阻塞(daemonset中有list pod的行为)
磁盘暴涨
4.少数 etcd 成员 down
下图中,1和2的etcd节点不响应了,apiserevr判断etcd的2379端口通就认为etcd是正常的,但实际上这两个etcd已经503了,只有3,4,5的peer是正常工作的。apiserver前有slb,发送到1和2的流量无法写入到etcd中,就会导致node离线,control manager就会把这40%的pod驱逐掉。
5.Master 节点出现网络分区
Group1: master-1,master-2
Group2: master-3,master-4,master-5
- 第一种老的leader在group1,无法得到多半数的确认,数据无法commit。
- 第二种老的leader在group2,group1就是无主的状态,数据的写入都是 no response,但apiserver是正常的,就会导致节点掉线,node-lifecycle-controller就会把这些pod驱逐掉。
解决:在apiserver上增加health check,不仅对etcd的端口进行探活,还要看etcd是否真实的活着。如果etcd无响应了,apiserver自杀,连接到这个apiserver的pod就会自动连到别的apiserver。
etcdctl
主要包括键值对的操作、租期、Watch 监测键值,集群状态,认证,用户,角色等。
[root@master ~]# etcdctl -h
NAME:
etcdctl - A simple command line client for etcd3.
USAGE:
etcdctl [flags]
VERSION:
3.5.2
API VERSION:
3.5
COMMANDS:
alarm disarm Disarms all alarms
alarm list Lists all alarms
auth disable Disables authentication
auth enable Enables authentication
auth status Returns authentication status
check datascale Check the memory usage of holding data for different workloads on a given server endpoint.
check perf Check the performance of the etcd cluster
compaction Compacts the event history in etcd
defrag Defragments the storage of the etcd members with given endpoints
del Removes the specified key or range of keys [key, range_end)
elect Observes and participates in leader election
endpoint hashkv Prints the KV history hash for each endpoint in --endpoints
endpoint health Checks the healthiness of endpoints specified in `--endpoints` flag
endpoint status Prints out the status of endpoints specified in `--endpoints` flag
get Gets the key or a range of keys
help Help about any command
lease grant Creates leases
lease keep-alive Keeps leases alive (renew)
lease list List all active leases
lease revoke Revokes leases
lease timetolive Get lease information
lock Acquires a named lock
make-mirror Makes a mirror at the destination etcd cluster
member add Adds a member into the cluster
member list Lists all members in the cluster
member promote Promotes a non-voting member in the cluster
member remove Removes a member from the cluster
member update Updates a member in the cluster
move-leader Transfers leadership to another etcd cluster member.
put Puts the given key into the store
role add Adds a new role
role delete Deletes a role
role get Gets detailed information of a role
role grant-permission Grants a key to a role
role list Lists all roles
role revoke-permission Revokes a key from a role
snapshot restore Restores an etcd member snapshot to an etcd directory
snapshot save Stores an etcd node backend snapshot to a given file
snapshot status [deprecated] Gets backend snapshot status of a given file
txn Txn processes all the requests in one transaction
user add Adds a new user
user delete Deletes a user
user get Gets detailed information of a user
user grant-role Grants a role to a user
user list Lists all users
user passwd Changes password of user
user revoke-role Revokes a role from a user
version Prints the version of etcdctl
watch Watches events stream on keys or prefixes
OPTIONS:
--cacert="" verify certificates of TLS-enabled secure servers using this CA bundle
--cert="" identify secure client using this TLS certificate file
--command-timeout=5s timeout for short running command (excluding dial timeout)
--debug[=false] enable client-side debug logging
--dial-timeout=2s dial timeout for client connections
-d, --discovery-srv="" domain name to query for SRV records describing cluster endpoints
--discovery-srv-name="" service name to query when using DNS discovery
--endpoints=[127.0.0.1:2379] gRPC endpoints
-h, --help[=false] help for etcdctl
--hex[=false] print byte strings as hex encoded strings
--insecure-discovery[=true] accept insecure SRV records describing cluster endpoints
--insecure-skip-tls-verify[=false] skip server certificate verification (CAUTION: this option should be enabled only for testing purposes)
--insecure-transport[=true] disable transport security for client connections
--keepalive-time=2s keepalive time for client connections
--keepalive-timeout=6s keepalive timeout for client connections
--key="" identify secure client using this TLS key file
--password="" password for authentication (if this option is used, --user option shouldn't include password)
--user="" username[:password] for authentication (prompt if password is not supplied)
-w, --write-out="simple" set the output format (fields, json, protobuf, simple, table)