你想了解的关于ETCD都在这了
k8s etcd中间件原理介绍
1.简介
k8s集群的数据中心,用于存放集群的配置以及状态信息,非常重要,如果数据丢失那么集群将无法恢复;因此高可用集群部署首先就是etcd是高可用集群;
从etcd的架构图中我们可以看到,etcd主要分为四个部分:
- HTTP Server:用于处理用户发送的API请求以及其它etcd节点的同步与心跳信息请求。
- Store:用于处理etcd支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等等,是etcd对用户提供的大多数API功能的具体实现。
- Raft:Raft强一致性算法的具体实现,是etcd的核心。
- WAL:Write Ahead Log(预写式日志),是etcd的数据存储方式。除了在内存中存有所有数据的状态以及节点的索引以外,etcd就通过WAL进行持久化存储。WAL中,所有的数据提交前都会事先记录日志。Snapshot是为了防止数据过多而进行的状态快照;Entry表示存储的具体日志内容。
2.K8S架构图
K8s系统默认就采用etcd存储数据,看看下面比较常见的高可用部署方案:
说明:
1.以上两种方案的区别是:一种用docker方式部署etcd,一种是独立部署etcd(官方更推荐用这种独立部署的方式)。
2.使用第二种方案的时候Api-Server与etcd交互会有2中方案,第一我们可以在etcd集群前面放一个负载均衡器用来调度etcd集群;第二种我们可以在Api-Server的配置文件中指定多个etcd地址,Api-Server会依次连接,配置方式为--etcd-servers=$IP1:2379,$IP2:2379,$IP3:2379,$IP4:2379,$IP5:2379
。
3.3个etcd作为一个及群组出现,互相会有数据同步及探活等数据交互。
4.每个Api-Server会在kube-apiserver.yaml中指定一个etcd服务,关于etcd之间是如何进行数据同步及选举的见第4章节。
3.性能介绍
3.1理解 etcd 的性能
决定 etcd 性能的关键因素,包括:
- 延迟(latency):延迟是完成操作的时间。
- 吞吐量(throughput):吞吐量是在某个时间期间之内完成操作的总数量。 当 etcd 接收并发客户端请求时,通常平均延迟随着总体吞吐量增加而增加。
延迟是完成操作的时间。吞吐量是在某个时间期间之内完成操作的总数量。
etcd 使用 Raft 一致性算法来在成员之间复制请求并达成一致。一致性性能,特别是提交延迟,受限于两个物理约束:网络IO延迟
和磁盘IO延迟
。完成一个etcd请求的最小时间是成员之间的网络往返时延(Round Trip Time / RTT),加需要提交数据到持久化存储的 fdatasync
时间。为了提高吞吐量, etcd 将多个请求打包在一起并提交给 Raft。这个批量策略让 etcd 在重负载试获得高吞吐量。
如下是摘抄自网络上的一些测试评估报告:
Server 数量 | CPU | 内存 | Disk Type | Version | 线程数 | key总数 | 模式 | 用时ms | QPS | 备注 |
---|---|---|---|---|---|---|---|---|---|---|
3 | 4核 | 2GB | 机械硬盘 | 3.3.11 | 1 | 100 | Write | 25356 | 3.94 | |
3 | 4核 | 2GB | 机械硬盘 | 3.3.11 | 1 | 100 | Read | 179 | 558 | |
3 | 4核 | 2GB | 机械硬盘 | 3.3.11 | 1 | 10000 | Write | 7000 | 1428 | |
3 | 4核 | 2GB | 机械硬盘 | 3.3.11 | 1 | 10000 | Read | 1058 | 9451 | |
3 | 4核 | 2GB | 机械硬盘 | 3.3.11 | 100 | 10000 | Write | 407 | 24570 | |
3 | 4核 | 2GB | 机械硬盘 | 3.3.11 | 100 | 10000 | Read | 519 | 19267 |
3.2 etcd对服务器的要求
以下根据etcd官方文档对服务器资源要求做了简要说明。
3.2.1 CPUs
很少有etcd部署需要大量的CPU资源。典型的etcd部署节点,需要2-4个CPU就可以顺利运行。负载很高的etcd集群,比如上千客户端或者每秒超过上万请求,倾向于CPU绑定,可以直接从内存获取请求。即便这样重的负载,通常需要8-16个CPU就可以了。
3.2.2 内存
etcd占用的内存相对比较小,但是etcd性能仍然取决于是否拥有足够的内存。一个etcd服务器会积极的缓存key-value数据和大部分的跟踪watcher。通常8GB内存就够了,对于负载高的集群,比如有上千watcher和超过百万的keys,相应的就需要16GB-64GB内存。
3.2.3 磁盘
高速磁盘是保证etcd部署性能和稳定性的关键因素。
慢磁盘会增加etcd的请求延时,潜在影响集群的稳定性。因为etcd一致性协议依赖于元数据写入持久化日志,并且要求集群大多数成员将请求写入磁盘。此外,etcd还会到磁盘上增量检查集群的状态,从而可以截断日志。如果这些写操作花费太长的时间,心跳就可能会超时,触发选举操作,从而破坏集群的稳定性。
etcd对磁盘写入延时非常敏感。通常稳定达到50 IOPS(比如:一个7200转的磁盘)是必须的,对于负载很高的集群,推荐能稳定达到500 IOPS(比如:一个典型的本地SSD盘或者高性能的虚拟块设备盘)。
3.2.4 网络
多节点部署的etcd集群会受益于快速和可靠的网络。为了满足etcd集群的一致性和分区容忍,一个不可靠网络出现网络分区会导致部分节点无效。低延时可以保证etcd成员之间快速通信,高带宽可以减少etcd故障节点的恢复时间。1Gb网络就可以满足常见的etcd部署场景,对于大型etcd集群,使用10Gb网络可以减少平均故障恢复时间。
3.2.5 示例硬件配置
下面以AWS云服务器为例子,列举了不同规模的集群对ETCD服务器的需求
K8S规模 | K8S节点数 | 数据规模 | 数据并发量 | vCPUs | 内存 (GB) | 磁盘带宽 (MB/s) |
---|---|---|---|---|---|---|
小型集群 | 50 | < 100MB | < 200 | 2 | 8 | 56.25 |
中型集群 | 250 | < 500MB | < 1000 | 4 | 16 | 93.75 |
大型集群 | 1000 | < 1GB | < 10000 | 8 | 32 | 125 |
超大型集群 | 3000 | > 1GB | > 10000 | 16 | 64 | 250 |
3.3
4.Raft
4.1 Raft简介
在每一个分布式系统中,etcd 往往都扮演了非常重要的地位,由于很多服务配置发现以及配置的信息都存储在 etcd 中,所以整个集群可用性的上限往往就是 etcd 的可用性,而使用 3 ~ 5 个 etcd 节点构成高可用的集群往往都是常规操作。
4.2 Raft选举
Etcd所有服务器只可能处于 Leader、Follower 以及 Candidate 三种状态;在处于正常的状态时,集群中只会存在一个 Leader,其余的服务器都是 Follower。
所有的 Follower 节点都是被动的,它们不会主动发出任何的请求,只会响应 Leader 和 Candidate 发出的请求,对于每一个用户的可变操作,都会被路由给 Leader 节点进行处理,除了 Leader 和 Follower 节点之外,Candidate 节点其实只是集群运行过程中的一个临时状态。
步骤①:集群启动后所有节点默认均为Follower。
步骤②:节点1探测leader超时, 变为Candidate,开始选举广播, 发起投票请求。
步骤③:节点2和节点3同意投票给节点1,节点1变为Leader。
说明:
为了确保选举的准确性,在每一任期内,最多允许一个服务被选举为leader,在一个任期内,一个服务只能投一票,只有获得大多数投票才能作为leader;如果有多个candidate,最终一定会有一个被选举为leader,如果多个candidate同时发起了选举,导致都没有获得大多数选票时,每一个candidate会随机等待一段时间后重新发起新一轮投票(一般是随机等待150-300ms)
4.3 Raft任期
Raft 集群中的时间也被切分成了不同的几个任期(Term),每一个任期都会由 Leader 的选举开始,选举结束后就会进入正常操作的阶段,直到 Leader 节点出现问题才会开始新一轮的选择。
每一个服务器都会存储当前集群的最新任期,它就像是一个单调递增的逻辑时钟,能够同步各个节点之间的状态,当前节点持有的任期会随着每一个请求被传递到其他的节点上。
Raft 协议在每一个任期的开始时都会从一个集群中选出一个节点作为集群的 Leader 节点,这个节点会负责集群中的日志的复制以及管理工作。
4.4 Raft数据同步
4.4.1 Leader主动存储数据
步骤①:Client发起数据更新请求,存储数据444。
步骤②:etcd-Leader首先会更新自身日志数据。
步骤③:etcd-Leade通知Follower节点也更新日志。
步骤④:Follower节点更新自身日志数据。
步骤⑤:当Follower节点更新日志成功后,会返回成功通知给etcd-Leade,至此完成了“提交”操作。
步骤⑥:当领etcd-Leader收到通知后,会更新本地数据。
步骤⑦:etcd-Leade更新本地数据成功后,会通知Follower节点更新自身数据。
步骤⑧:Follower节点进行更新自身数据。
步骤⑨:Follower节点更新成功后,把更新结果反馈给etcdLeader。
4.4.2 Leader被动存储数据
说明:
这里省去了存储数据日志的过程,默认数据日志存储完成,进行存储更改数据。
步骤①:当前集群所有节点数据一致,存有111,222,333三组数据,现在Follower节点收到存储数据444的请求
步骤②:Follower把存储请求转发给当前集群的Leader
步骤③:Leader将收到存储数据444的请求并且已经把数据和log存储成功
步骤④:多数follower成功写入log后,leader会将该数据提交到状态机,leader 把数据提交后,返回给client结果,在下一个心跳中,leader 通知follower更新已经提交的数据
5. 存储模式介绍
5.1 源码地址
https://github.com/etcd-io/etcd
5.2 etcd3和etcd2的区别
5.2.1 版本说明
目前etcd主要经历了3个大的版本,分别为etcd 0.4版本、etcd 2.0版本和etcd 3.0版本。对于etcd 2.0版本,已经可以很好满足etcd的初步需求,主要包括:
- 专注于key-value存储,而不是一个完整的数据库;
- 通过HTTP + JSON的方式暴露给外部API;
- watch机制提供持续监听某个key变化的功能,以及基于TTL的key的自动过期机制。
但是在实际过程中,我们也发现了一些问题,比如客户端需要频繁地与服务端进行通信,集群在空间和时间上都需要承受较大的压力,以及垃圾回收key的时间不稳定等,同时“微服务”架构要求etcd能够单集群支撑更大规模的并发,因此诞生了etcd 3.0版本,主要对HTTP + JSON的通信方式、key的自动过期机制、watch机制、数据持久化等进行了优化,下面我们看看etcd 3.0版本对每个模块都做了哪些优化。
5.2.2 客户端通信方式
gRPC是Google开源的 个高性能、跨语言的RPC框架,基于HTTP/2协议实现。它使用protobuf作为序列化和反序列化协议,即基于 protobuf 来声明数据模型和RPC接口服务。protobuf的效率远高于JSON,尽管etcd v2的客户端已经对JSON的序列号和反序列化进行了大量的优化,但是etcd v3的gRPC序列号和反序列化的速度依旧是etcd v2的两倍多。
etcdv3的客户端使用gRPC与server进行通信,通信的消息协议使用protobuf进行约定,代替了v2版本的HTTP+JSON格式,使用二进制替代文本,更加节省空间。同时gRPC使用的是HTTP/2协议,同一个连接可以同时处理多个请求,不必像HTTP1.1协议中,多个请求需要建立多个连接。同时,HTTP/2会对请求的Header和请求数据进行压缩编码,常见的有Header帧,用于传输Header内容,另外就是Data帧,来传输正文实体。客户端可以将多个请求放到不同的流中,然后将这些流拆分成帧的形式进行二进制传输,传输的帧也会有一个编号,因此在一个连接中客户端可以发送多个请求,减少了连接数,降低了对服务器的压力,二进制的数据传输格式也会是传输速度更快。
总结一下,其实这里主要进行2点优化:
- 二进制取代字符串:通过gRPC进行通信,代替了v2版本的HTTP+JSON格式;
- 减少TCP连接:使用HTTP/2协议,同一个连接可以同时处理多个请求,摒弃多个请求需要建立多个连接的方式。
5.2.3 键的过期机制
etcdv2中的键的实效是使用TTL机制来实现的,每个有存活时间的键,客户端必须定期的进行刷新重新设置保证它不被自动删除,每次刷新同时还会重新建立连接去更新键。也就是说,即使整个集群都处于空闲状态,也会有很多客户端与服务器进行定期通信,以保证某个key不被自动删除。
etcdv3版本中采用了租约机制进行实现,每个租约会有一个TTL,然后将一些key附加到租约上,当租约到期后,附加到它上边的key都会被删除。利用键的过期机制可以实现服务注册功能,我们可以将一个服务的域名、IP等信息注册到etcd中,并给相应的键设置租约,并在TTL时间内定期维持一个心跳进行刷新。当服务故障后,心跳消失从而相应的键就会自动删除,从而实现了服务的注册功能和服务的健康检查功能。
总结一下,就是v2版本比较傻瓜,需要时刻维护每个key的通信,v3就比较智能,整个统一的过期key的代号,我们把代号称之为“租约”,我们只需要维护这个代号即可,避免客户端去维护所有的key。
5.2.4 watch机制
etcdv2中的键被废除以后,为了能够跟踪key的变化,使用了事件机制进行跟踪,维护键的状态,来防止被删除掉的后键还能恢复和watch到,但是有一个滑动窗口的大小限制,那么如果要获取1000个时间之前的键就获取不到了。因此etcdv2中通过watch来同步数据不是那么可靠,断开连接一段时间后就会导致有可能中间的键的改动获取不到了。在etcdv3中支持get和watch键的任意的历史版本记录。
另外,v2中的watch本质上还是建立很多HTTP连接,每一个watch建立一个tcp套接字连接,当watch的客户端过多的时候会大大消耗服务器的资源,如果有数千个客户端watch数千个key,那么etcd v2的服务端的socket和内存资源会很快被耗尽。v3版本中的watch可以进行连接复用,多个客户端可以共用相同的TCP连接,大大减轻了服务器的压力。
总结一下,其实这里主要进行2点优化:
- 实时监听key的更新:解决v2中途key的数据更新,客服端不会感知的问题;
- 多路复用:这个可以想到select和epool模型,就是一个客户之前需要建立多个TCP连接,现在只需要建立一个即可。
5.2.5 数据存储模型
etcd是一个key-value数据库,ectd v2只保存了key的最新的value,之前的value会被直接覆盖,如果需要知道一个key的历史记录,需要对该key维护一个历史变更的窗口,默认保存最新的1000个变更,但是当数据更新较快时,这1000个变更其实“不够用”,因为数据会被快速覆盖,之前的记录还是找不到。为了解决这个问题,etcd v3摒弃了v2不稳定的“滑动窗口”式设计,引入MVCC机制,采用从历史记录为主索引的存储结构,保存了key的所有历史记录变更,并支持数据在无锁状态下的的快速查询。 etcd是一个key-value数据库,etcdv2的key是一个递归的文件目录结构,在v3版本中的键改成了扁平化的数据结构,更加简洁,并通过线段树的优化方式,支持key的快速查询。
由于etcd v3实现了MVCC,保存了每个key-value pair的历史版本,数据大了很多,不能将整个数据库都存放到内存中。因此etcd v3摒弃了内存数据库,转为磁盘数据库,即整个数据都存储在磁盘上,底层的存储引擎使用的是BoltDB。
总结一下,其实这里主要进行3点优化:
- 保存历史数据:摈弃v2的“滑动窗口”式设计,通过MVCC机制,保存了所有的历史数据;
- 数据落磁盘:因为要保存历史数据,数据量态度,不适合全内存存储,使用BoltDB存储;
- 查询优化:摒弃v2的目录式层级化设计,使用线段树优化查询。
5.2.6 MVCC介绍
5.2.6.1 为什么会选择MVCC
高并发情况下,会存在大量读写操。对于etcd v2,它是一个纯内存的数据库,整个数据库有一个Stop-the-World的大锁,可以通过锁的机制来解决并发带来的数据竞争,但是通过锁的方式存在一些弊端,具体如下:
- 锁的粒度不好控制,每次操作Stop-the-World时都会锁住整个数据库。
- 读锁和写锁会相互阻塞
如果使用基于锁的隔离机制,并且有一段很长的读事务,那么在这段时间内这个对象就会无法被改写,后面的事务也会被阻塞,直到这个事务完成为止,这种机制对于并发性能来说影响很大。
MVCC其实就是多版本并发控制,etcd在v3才引入,它可以很好的解决锁带来的问题,每当需要更改或者删除某个数据对象时,DBMS不会在原地删除或者修改这个已有的数据对象本身,而是针对该数据对象创建一个新的版本,这样一来,并发的读取操作可以在无需加锁的情况下读取老版本的数据,而写操作就可以同时进行,这个模式的好处可以让读取操作不再阻塞。
总而言之,MVCC能最大的实现高效的读写并发,尤其是高效的读,因此非常适合etcd这种“读多写少”的场景。
5.2.6.2 数据模型
将讲解MVCC的实现原理前,还需要了解v2和v3的数据存储模型。
对于v2,前面其实已经讲过,v2是一个存内存的数据库,数据会通过WAL日志和Snapshot来持久化数据,具体持久化数据的方式,后面会整体讲述。
对于v3,因为它支持历史版本数据的查询,所以它是将数据存储在一个多版本的持久化K-V存储里面,当持久化键值数据发生变化时,会先保存之前的旧值,所以数据修改后,key先前版本的所有值仍然可以访问和watch。由于v3需要保存数据的历史版本,这就极大地增加了存储量,内存存储不了那么多的数据,所以v3的数据需要持久化到磁盘中,存储数据为BoltDB。
那什么是BoltDB呢?BoltDB是一个纯粹的Go语言版的K-V存储,它的目标是为项目提供一个简单、高效、可靠的嵌入式的、可序列化的键值数据库,而不要像MySQL那样完整的数据库服务器。BoltDB还是一个支持事务的键值存储,etcd事务就是基于BoltDB的事务实现的。为了大家能充分理解,我再扩展2个问题:
1.v2会定时快照,v3需要进行快照么?
答案是不会。v3实现MVCC之后,数据是实时写入BoltDB数据库中,也就是数据的持久化已经“摊销”到了每次对key的写请求上,因此v3就不需要再做快照了。
2.v3中所有的历史数据都会保存下来么?
答案是不会。虽然v3没有快照,数据全部落在BoltDB,但是为了防止数据存储随着时间推移而无限增长,etcd可能会压缩(其实就是删除)key的旧版本数据,说的通俗一点,就是删除BoltDB中旧版本的数据。
5.2.6.3 初探MVCC实现
那么v3是怎么实现MVCC的呢?我们可以先看如下操作:
etcdctl txn <<< 'put key1 "v1" put key2 "v2"'
etcdctl txn <<< 'put key1 "v12" put key2 "v22"'
BoltDB中会存入4条数据,具体代码如下所示:
rev={3 0}, key=key1, value="v1"
rev={3 1}, key=key2, value="v2"
rev={4 0}, key=key1, value="v12"
rev={4 1}, key=key2, value="v22"
reversion主要由2部分组成,第一部分是main rev,每操作一次事务就加1,第二部分是sub rev,同 一个事务中每进行一次操作就加1。如上示例所示,第一次操作的main rev是3,第二次是4 。
了解v3的磁盘存储之后,可以看到要想从BoltDB中查询数据,必须通过reversion,但是客户端都是通过key来查询value,所以etcd在内存中还维护了一个kvindex ,保存的就是key reversion之前的映射关系,用来加速查询。kvindex是基于Google开源的GoLang的B树实现,也就v3在内存中维护的二级索引,这样当客户端通key查询value的时候, 会先在kvindex中查询这个key的所有revision ,然后再通过 revision从BoltDB中查询数据。
6. 日志和快照管理
6.1 数据持久化
etcd对数据的持久化,采用的是WAL日志加Snapshot快照的方式。
etcd对数据的更新都是先写到WAL日志中,当通过Raft将WAL同步到所有分布式节点之后,再将WAL中的数据写到内存。对于WAL日志,其实还有个作用,就是实现redo和undo功能,也就是当数据出现问题时,以为WAL日志记录了对数据的所有操作,所以可以通过WAL对数据库进行恢复和回滚。
既然有了WAL日志,那为什么还需要定期做快照呢?这里其实和Redis中的RDB和AOF日志很像,我们可以把WAL日志对标为Redis的AOF日志,Snapshot快照对标为Redis的RDB日志。在Redis中进行节点间数据同步时,我们是先全量同步RDB日志(快照文件),然后再增量同步AOF日志(数据增量文件)。etcd也不一样,因为WAL日志太琐碎了,如果需要通过WAL日志去同步数据,太慢了,我们先把之前所有的数据同步过去(Snapshot快照文件),然后再同步后续的增量数据(WAL日志)。当对WAL数据昨晚快照后,就可以将旧的WAL数据删除。
6.2 快照管理
至于快照文件是怎么生成的,如果了解Redis中的RDB文件的生成原理,这个就不难理解了。
其实就是通过写时复制技术Copy-On-Write完成的,当需要进行快照时,如果数据有更新,会生成一个数据副本,如图中的“键值对C”,当进行快照时,数据如果未更新,直接落盘,数据如果有更新,同步副本数据即可。
7.常见管理方法
7.1 端口介绍
2379对外提供服务端口;2380各etcd Server间通讯使用的端口。
7.2 命令介绍
因为每次执行etcdctl命令都要带上一大堆的证书信息, 我们做一个alias,方便后面操作。
export ETCDCTL_API=3
alias etcdctl='etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key'
7.2.1 查看etcd集群信息
# 查看集群成员信息
/ # etcdctl member list
52fbc3b72b27629f, started, k8s-master1, https://192.168.223.135:2380, https://192.168.223.135:2379
97081bb482954e76, started, k8s-master2, https://192.168.223.136:2380, https://192.168.223.136:2379
d415186fcf074869, started, k8s-master3, https://192.168.223.137:2380, https://192.168.223.137:2379
# 查看集群各节点状态
/ # alias etcdctl='etcdctl --endpoints=https://192.168.223.136:2379,https://192.168.223.135:2379,https://192.168.223.137:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key'
/ # etcdctl endpoint status --write-out=table
# 查看集群健康状态
/ # etcdctl endpoint health
https://192.168.223.136:2379 is healthy: successfully committed proposal: took = 9.549705ms
https://192.168.223.137:2379 is healthy: successfully committed proposal: took = 10.7308ms
https://192.168.223.135:2379 is healthy: successfully committed proposal: took = 12.239945ms
# 列出所有key
/ # etcdctl get / --prefix --keys-only
…………
7.2.1 内置快照
使用etcd内置快照对etcd进行数据备份,save 后面的snapshotdb为镜像名字,
# 生成名字为snapshotdb的快照
/ # export ETCDCTL_API=3
/ # etcdctl --endpoints 127.0.0.1:2379 snapshot save snapshotdb
/ # etcdctl snapshot status --write-out=table /var/lib/etcd/member/snap/snapshotdb
+---------+----------+------------+------------+
| HASH | REVISION | TOTAL KEYS | TOTAL SIZE |
+---------+----------+------------+------------+
| e798773 | 295646 | 1301 | 2.6 MB |
+---------+----------+------------+------------+
7.2.1.1 备份还原kubeadm安装的etcd集群
7.2.1.1.1 准备工作
#建立备份存放目录
mkdir -p /backup_$(date +%Y%m%d)
#备份/etc/kubernetes目录
cp -r /etc/kubernetes/ /backup_$(date +%Y%m%d)/
#备份/var/lib/etcd目录
cp -r /var/lib/etcd/ /backup_$(date +%Y%m%d)/
#备份 /var/lib/kubelet目录
cp -r /var/lib/kubelet/ /backup_$(date +%Y%m%d)/
使用kubeadm创建的k8s集群,etcd是使用容器运行的,因此备份和还原数据库需要将容器中的etcdctl命令拷贝到操作节点系统下的/usr/bin/目录下
docker cp $(docker ps | grep -v etcd-mirror | grep -w etcd | awk '{print $1}'):/usr/local/bin/etcdctl /usr/bin/
7.2.1.1.2 备份
备份ETCDCTL_API为3的etcd数据到之前的备份目录下。
可以在多个master节点上执行备份操作
ETCDCTL_API=3 etcdctl --endpoints="https://127.0.0.1:2379" --cert="/etc/kubernetes/pki/etcd/server.crt" --key="/etc/kubernetes/pki/etcd/server.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" snapshot save /backup_$(date +%Y%m%d)/snap-$(date +%Y%m%d%H%M).db
7.2.1.1.3 恢复
恢复步骤:
需要先停掉所有Master节点的kube-apiserver和etcd,确保kube-apiserver已经停止了。
需要分别在master1、master2、master3上进行同样的操作。
[root@k8s-Master1 kubernetes]# cd /etc/kubernetes/
[root@k8s-Master1 kubernetes]# mv manifests/ manifests.bak
等待api-server和etcd服务stop
stop之前的样子
stop之后的样子
7.2.1.1.4 变更/var/lib/etcd
需要分别在master1、master2、master3上进行同样的操作。
mv /var/lib/etcd /var/lib/etcd.bak
7.2.1.1.5 scp bak文件
[root@k8s-Master1 kubernetes]# scp /backup_20221208/snap-202212081539.db root@k8s-master2:/backup_20221208/
snap-202212081539.db 100% 2948KB 20.6MB/s 00:00
[root@k8s-Master1 kubernetes]# scp /backup_20221208/snap-202212081539.db root@k8s-master3:/backup_20221208/
snap-202212081539.db 100% 2948KB 25.5MB/s 00:00
[root@k8s-Master1 kubernetes]#
7.2.1.1.6 还原数据
# k8s-master1 上执行
ETCDCTL_API=3 etcdctl snapshot restore /backup_20221208/snap-202212081539.db \
--endpoints=192.168.223.135:2379 \
--name=k8s-master1 \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--initial-advertise-peer-urls=https://192.168.223.135:2380 \
--initial-cluster-token=etcd-cluster-0 \
--initial-cluster=k8s-master1=https://192.168.223.135:2380,k8s-master2=https://192.168.223.136:2380,k8s-master3=https://192.168.223.137:2380 \
--data-dir=/var/lib/etcd
# k8s-master2 上执行
ETCDCTL_API=3 etcdctl snapshot restore /backup_20221208/snap-202212081539.db \
--endpoints=192.168.223.136:2379 \
--name=k8s-master2 \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--initial-advertise-peer-urls=https://192.168.223.136:2380 \
--initial-cluster-token=etcd-cluster-0 \
--initial-cluster=k8s-master1=https://192.168.223.135:2380,k8s-master2=https://192.168.223.136:2380,k8s-master3=https://192.168.223.137:2380 \
--data-dir=/var/lib/etcd
# k8s-master3 上执行
ETCDCTL_API=3 etcdctl snapshot restore /backup_20221208/snap-202212081539.db \
--endpoints=192.168.223.137:2379 \
--name=k8s-master3 \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--initial-advertise-peer-urls=https://192.168.223.137:2380 \
--initial-cluster-token=etcd-cluster-0 \
--initial-cluster=k8s-master1=https://192.168.223.135:2380,k8s-master2=https://192.168.223.136:2380,k8s-master3=https://192.168.223.137:2380 \
--data-dir=/var/lib/etcd
7.2.1.1.7 验证集群可用性
7.2.1.2 备份还原二进制安装的etcd集群
说明:
小编本地资源原因不存在二进制安装etcd集群环境,所以本章节未做验证
7.2.1.2.1 安装etcdctl
yum install -y etcd
7.2.1.2.1 备份
ETCDCTL_API=3 etcdctl \
snapshot save snap.db \
--endpoints=https://192.168.10.160:2379 \
--cacert=/opt/etcd/ssl/ca.pem \
--cert=/opt/etcd/ssl/server.pem \
--key=/opt/etcd/ssl/server-key.pem
7.2.1.2.2 暂停kube-apiserver和etcd
systemctl stop kube-apiserver
systemctl stop etcd etcd
mv /var/lib/etcd/default.etcd /var/lib/etcd/default.etcd.bak
7.2.1.2.3 在每个节点上恢复数据
# 节点一
ETCDCTL_API=3 etcdctl snapshot restore snap.db \
--name etcd-1 \
--initial-cluster= "etcd-1=https://192.168.10.160:2380,etcd-2=https://192.168.10.161:2380,etcd-3=https:192.168.10.162:2380" \
--initial-advertise-peer-url=https://192.168.10.160:2380 \
--data-dir=/var/lib/etcd/default.etcd
# 节点二
ETCDCTL_API=3 etcdctl snapshot restore snap.db \
--name etcd-2 \
--initial-cluster= "etcd-1=https://192.168.10.160:2380,etcd-2=https://192.168.10.161:2380,etcd-3=https:192.168.10.162:2380" \
--initial-advertise-peer-url=https://192.168.10.162:2380 \
--data-dir=/var/lib/etcd/default.etcd
# 节点三
ETCDCTL_API=3 etcdctl snapshot restore snap.db \
--name etcd-3 \
--initial-cluster= "etcd-1=https://192.168.10.160:2380,etcd-2=https://192.168.10.161:2380,etcd-3=https:192.168.10.162:2380" \
--initial-advertise-peer-url=https://192.168.10.162:2380 \
--data-dir=/var/lib/etcd/default.etcd
7.2.1.2.4 验证集群可用性
mv /var/lib/etcd/default.etcd.bak /var/lib/etcd/default.etcd
systemctl start kube-apiserver
systemctl start etcd.service
7.2.2 重启etcd服务
systemctl restart etcd
7.2.3 删除故障节点
/ # export ETCDCTL_API=3
# 查看当前etcd集群清单
/ # etcdctl --endpoints 127.0.0.1:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt member list
52fbc3b72b27629f, started, k8s-master1, https://192.168.223.135:2380, https://192.168.223.135:2379
97081bb482954e76, started, k8s-master2, https://192.168.223.136:2380, https://192.168.223.136:2379
d415186fcf074869, started, k8s-master3, https://192.168.223.137:2380, https://192.168.223.137:2379
# 删除某个etcd节点
/ # etcdctl member remove 52fbc3b72b27629f
Member 52fbc3b72b27629f removed from cluster 2adefcce7f8dcf42
7.2.4 向集群中添加节点
/ # etcdctl member add etcd --peer-urls="https://192.168.223.135:2380"
Member d5ce641905ab71cb added to cluster 2adefcce7f8dcf42
ETCD_NAME="etcd"
ETCD_INITIAL_CLUSTER="k8s-master2=https://192.168.223.136:2380,k8s-master3=https://192.168.223.137:2380,etcd=https://192.168.223.135:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.223.135:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
注意:
添加节点前新的节点要在配置文件中添加- --initial-cluster-state=existing参数表示etcd不作为新集群初始化
7.2.5 查看当前节点状态
/ # etcdctl endpoint status --write-out=table
+------------------------+------------------+---------+---------+-----------+-----------+------------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+------------------------+------------------+---------+---------+-----------+-----------+------------+
| https://127.0.0.1:2379 | d5ce641905ab71cb | 3.3.10 | 3.0 MB | false | 177 | 419601 |
+------------------------+------------------+---------+---------+-----------+-----------+------------+
8.续订etcd证书
8.1kubeadm续订etcd证书
8.1.1获取kubeadm configmap文件
kubectl get configmap -n kube-system kubeadm-config -o yaml > /root/kubeadm/kubeadm-config.yaml
8.1.2 更新etcd-peer证书
8.1.2.1 证书简介
etcd 节点间用来相互通信的证书
8.1.2.2 证书延期
kubeadm alpha certs renew etcd-peer --config=/root/kubeadm/kubeadm-config.yaml
8.1.3 更新etcd-healthcheck-client证书
8.1.3.1 证书简介
存活态探针的证书,用于对 etcd 执行健康检查
8.1.3.2 证书延期
kubeadm certs renew etcd-healthcheck-client --config=/root/kubeadm/kubeadm-config.yaml
8.1.4 更新etcd-server证书
8.1.4.1 证书简介
用于提供 etcd 服务的证书
8.1.4.2 证书延期
kubeadm certs renew etcd-server --config=/root/kubeadm/kubeadm-config.yaml
8.1.5 重启etcd容器
注意:
每个集群的环境都不一样,建议重启前挨个grep拆开,确定etcd容器后在重启。
[root@k8s-Master1 etcd]# docker ps -a | grep etcd | grep "advertise" | grep -v Exit
bf0661bf4393 2c4adeb21b4f "etcd --advertise-cl…" 40 hours ago Up 3 minutes k8s_etcd_etcd_kube-system_28eac234-7252-11ed-9caa-000c29d4a613_0
[root@k8s-Master1 etcd]# docker ps -a | grep etcd | grep "advertise" | grep -v Exit | awk '{print $1}' | xargs docker restart
bf0661bf4393
查看etcd集群状态
[root@k8s-Master1 etcd]# kubectl exec -it -n kube-system etcd sh
/ # export ETCDCTL_API=3
/ # alias etcdctl='etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/et
cd/server.crt --key=/etc/kubernetes/pki/etcd/server.key'
/ # etcdctl member list
97081bb482954e76, started, k8s-master2, https://192.168.223.136:2380, https://192.168.223.136:2379
d415186fcf074869, started, k8s-master3, https://192.168.223.137:2380, https://192.168.223.137:2379
d5ce641905ab71cb, started, k8s-master1, https://192.168.223.135:2380, https://192.168.223.135:2379
8.2 外挂etcd证书续期
看下etcd证书配置文件,发现是8760h
[root@k8s-Master1 etcd]# cat config.json
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "8760h"
}
}
}
}
8.2.1 备份etcd数据
[root@k8s-Master1 etcd]# cd /var/lib
[root@k8s-Master1 etcd]# tar -zvcf etcd.tar.gz etcd/
8.2.2 修改ca配置文件
[root@k8s-Master1 etcd]# cat ca-config.json
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
# 将默认证书签署过期时间修改为10年
"expiry": "87600h"
}
}
}
}
8.2.3 生成新证书
#删除过期证书
[root@k8s-Master1 etcd]# rm -f /etc/etcd/ssl/*
# 创建新证书
[root@k8s-Master1 etcd]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=config.json -profile=kubernetes etcd-csr.json | cfssljson -bare etcd
[root@k8s-Master1 etcd]# cp etcd.pem etcd-key.pem ca.pem /etc/etcd/ssl/
#拷贝到其他etcd节点
[root@k8s-Master1 etcd]# scp -r /etc/etcd/ssl root@${other_node}:/etc/etcd/
# 重启etcd服务(记住,要3个节点一起重启,不然会hang住)
[root@k8s-Master1 etcd]# systemctl restart etcd
8.2.4 重启k8s中间件
etcd替换成功后,再重启kube-apiserver,kube-controller,kube-scheduler这3个容器
9.etcd的主要监控指标
9.1 行内grafana图的地址
9.2 主要告警规则
10.常见问题及处理方法
10.1 某个节点损坏
0. 假设顺坏的是200.54
先停止服务:systemctl stop etcd.service
1. 找到一台正常的节点服务器,比如https://10.10.200.51:2379
etcdctl --cacert=ca.pem --cert=server.pem --key=server-key.pem \
--endpoints="https://10.10.200.51:2379" member list
2. 从集群中删除损坏的节点(假设是200.54)
etcdctl --cacert=ca.pem --cert=server.pem --key=server-key.pem \
--endpoints="https://10.10.200.51:2379" member remove 78c05f4178265b0e
3. 重新加入这个节点
etcdctl --cacert=ca.pem --cert=server.pem --key=server-key.pem \
--endpoints="https://10.10.200.51:2379" member add etcd54 \
--peer-urls=https://10.10.200.54:2380
4. 修改配置并启动损坏的节点(假设是200.54上操作)
删除所有数据rm -rf data/*
修改配置:
initial-cluster-state: "new" # 改成下面这句
initial-cluster-state: "existing"
systemctl start etcd.service
systemctl status etcd.service
10.2 database space exceeded报错恢复步骤
从报错的字面意思来看,是超出数据库空间导致。执行etcdctl endpoint status,查看集群此时各节点的状态,发现DB SIZE为2.1GB。ETCD官方文档说明(https://etcd.io/docs/v3.3.12/dev-guide/limit/)提到ETCD默认的存储大小是2GB。超出后,集群无法进行写入。
10.2.1 备份数据
使用snapshot save命令备份集群数据(前面有写)
10.2.2 获取reversion
etcdctl --write-out="json" --cacert /var/lib/etcd/cert/ca.pem --key /var/lib/etcd/cert/etcd-client-key.pem --cert /var/lib/etcd/cert/etcd-client.pem --endpoints='*.*.*.*:2379' endpoint status |grep -o '"revision":[0-9]*'
10.2.3 compact
etcdctl --cacert /var/lib/etcd/cert/ca.pem --key /var/lib/etcd/cert/etcd-client-key.pem --cert /var/lib/etcd/cert/etcd-client.pem --endpoints='*.*.*.*:2379' compact $revision
10.2.4 defrag
etcdctl --cacert /var/lib/etcd/cert/ca.pem --key /var/lib/etcd/cert/etcd-client-key.pem --cert /var/lib/etcd/cert/etcd-client.pem --endpoints='*.*.*.*:2379' defrag
10.2.5 删除报警
必需删除,否则集群仍然无法使用
etcdctl --write-out="table" --cacert /var/lib/etcd/cert/ca.pem --key /var/lib/etcd/cert/etcd-client-key.pem --cert /var/lib/etcd/cert/etcd-client.pem --endpoints='*.*.*.*:2379 alarm disarm
11. etcd参考文档
官方文档:
https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/configure-upgrade-etcd/
https://kubernetes.io/zh-cn/docs/reference/setup-tools/kubeadm/kubeadm-certs/#cmd-certs-renew
https://github.com/etcd-io/etcd/issues/8169
https://kubernetes.feisky.xyz/concepts/components/etcd
https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/configure-upgrade-etcd/#准备开始
其它文档:
https://cloud.tencent.com/developer/article/1888165
https://juejin.cn/post/7067910112287031333
https://blog.csdn.net/wo18237095579/article/details/119956018
https://bbotte.github.io/service_config/etcd-cluster-troubleshooting.html
https://cloud.tencent.com/developer/article/1708414
https://cloud.tencent.com/developer/news/279231
https://chende.ren/2021/04/23183930-k8s-etcd.html
https://cloud.tencent.com/developer/article/1683582
https://developer.aliyun.com/article/939873
https://www.jianshu.com/p/0ef46c0fba47 数据备份恢复干货
https://blog.51cto.com/dangzhiqiang/2286890 etcd对服务器资源的推荐
https://juejin.cn/post/6992916118058827813 etcd性能测试