Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Etcd学习笔记

25
Sep
2018

Etcd学习笔记

By Alex
/ in Go
0 Comments
关于一致性
CAP原理

分布式系统中,讨论原子系统读写场景时,反复提到的CAP原理:

  1. Consistency:(强)一致性,任意时刻所有节点上的数据保持同步,所有读写操作就是原子的、有序的
  2. Availability:可用性,任何非故障节点必须在有限时间内对请求作出响应
  3. tolerance to the Partition of network:出现网络分区、丢失任意多消息的情况下,系统仍然能工作

任何分布式系统无法同时满足CAP:

  1. 满足AP:如果在出现网络分区的情况下,还要保证可用性,则一致性C必须放弃。因为网络分区导致节点间存在无法通信的情况,为了可用性节点必须使用本地数据提供服务,则就导致了不一致
  2. 满足CP:如果在出现网络分区的情况下,还要保证一致性,那就意味着节点之间的数据同步一直阻塞,丧失可用性
  3. 满足CA:不出现网络分区的情况下,一致性、可用性可以满足

某些NoSQL数据库,为了换取基本的可用性,往往放弃强一致性,仅仅要求最终一致性,此所谓BASE理论。

何为一致性

为了防止单点故障导致数据不可用、丢失,分布式系统通常引入副本机制。保证副本的一致性,成为分布式系统的核心问题。

一致性这个术语,在计算机的不同领域对了三个英文单词:

  1. Coherence:在多核共享内存CPU中,各核心上的缓存的数据如何保持一致
  2. Consensus:强调多个提议者如何就某件事情达成共识,Paxos、Raft等协议解决的就是共识问题
  3. Consistency:强调并发状态下系统暴露给客户端的行为。CAP、ACID中的C都有这个意思

强一致性为何很难满足:

  1. 消息传递异步无序(Asynchronous):现实网络不是一个可靠的信道,存在消息延时、丢失,节点间消息传递做不到同步有序

  2. 节点宕机(fail-stop):节点持续宕机,数据损坏

  3. 节点宕机恢复(fail-recover):节点宕机后恢复,数据冲突

  4. 网络分区(Network partition):网络链路出现问题,将N个节点隔离成多个不能相互通信的部分

  5. 拜占庭将军问题(Byzantine Generals Problem):节点宕机或逻辑错误,甚至不按套路出牌抛出干扰决议的信息。大多数原则

一致性模型
严格一致性

即强一致性,也叫原子一致性,可线性化(Linearizability)。严格一致性要求

  1. 任何一次读操作,都能读取到数据的最近一次写的结果
  2. 所有进程看到的操作,其顺序均一致
顺序一致性

也叫可序列化。所有进程都以相同的顺序看到所有的修改。允许进程没读到其它进程对数据的写更新,但是每个进程读到数据的不同值的顺序必须一致。

因果一致性

要求对一个数据的写操作,必然发生在,读取到此写操作结果的读操作之前。

最终一致性

这种模型从用户,而非数据的角度阐述一致性。

最终一致性属于弱一致性,允许用户读取到数据的更新存在延迟,此延迟叫做不一致性窗口。

复制状态机

包括etcd、ZooKeeper、HDFS在内的分布式系统,均遵循复制状态机模型。一个分布式的复制状态机,由多个复制单元组成。每个复制单元均是一个状态机,其状态保存在一组状态变量中。这些变量(也就是状态机的状态)仅仅能通过外部命令改变。

实现中,“一组状态变量“通常基于操作日志来实现。每个复制单元均存储操作日志,并且严格按顺序逐条执行日志中的操作指令。需要注意的是,执行顺序不一定需要和指令发起顺序完全一致,只需要保证所有复制单元是同样的顺序即可。

如果复制单元的初态相同,只需要保证操作日志(在它们之间)的一致性,就可以复制状态机的整体一致性。

拜占庭将军问题

拜占庭将军们依靠信差达成进攻or撤退的共识,在已知存在不可靠成员的情况下,忠诚将军们如何排除叛徒/间谍的影响?此即拜占庭将军问题。叛徒/间谍传递的虚假消息即拜占庭错误。

在分布式系统中,拜占庭错误可以是硬件错误、网络堵塞、连接断开、遭受攻击,等等。理论上说,要容忍N个拜占庭错误,则需要至少2N+1个复制节点。因此典型的一致性协议都要求半数以上的投票才能作出决定。

简介
Etcd是什么

Etcd是一种分布式的键值存储,用于保存分布式系统中的关键信息。Etcd:

  1. 提供基于gRPC的面向用户的简单API
  2. 支持TLS和可选的客户端身份验证
  3. 每秒可以支持高达10000写操作
  4. 基于分布式共识算法Raft
  5. 可以用于配置管理、服务发现、机器探活
  6. 可以很轻松的实现分布式锁、选举、写屏障、事件监控、租约等分布式协同

默认情况下,Etcd使用TCP端口2379来监听客户端请求,使用TCP端口2380进行Peer之间的通信。

/etc是Unix下存放配置文件的地方,d表示分布式的意思。可以看出Etcd适用于存储非频繁更新的配置信息,而非海量数据,但是目前版本在处理100GB级别的大量数据下性能也不错。此外,整个Etcd集群只有单个“复制组”(没有分片机制),这限制了它的水平扩容能力。

Etcd用在那些需要强一致性、不能容忍脑裂的场景下,即使强一致性会影响可用性。

同类项目比较
ZooKeeper

Etcd和ZooKeeper解决相同的问题,Etcd也从ZooKeeper得到很多启发。Etcd比ZooKeeper更先进的地方有:

  1. 支持动态的集群成员重新配置
  2. 在高负载下支持稳定的读写性能
  3. 多版本并发控制(MVCC)数据模型 —— 支持查询先前版本的键值对
  4. 可靠的键监控,绝不会丢弃事件
  5. 租约原语将连接、会话解耦
  6. 支持安全的分布式锁的API
  7. 基于gRPC协议的客户端
数据模型

Etcd的设计目标是用来存放非频繁更新的数据,提供可靠的Watch插件,它暴露了键值对的历史版本,以支持低成本的快照、监控历史事件。这些设计目标要求它使用一个持久化的、多版本的、支持并发的数据数据模型。

当Etcd键值对的新版本保存后,先前的版本依旧存在。从效果上说,键值对是不可变的,Etcd不会对其进行in-place的更新操作,而总是生成一个新的结构。为了防止历史版本无限增加,Etcd的存储支持压缩(Compact)以删除老旧版本。

逻辑视图

从逻辑角度看,Etcd的存储是一个扁平的二进制键空间,键空间有一个针对键(字节字符串)的词典序索引,因此范围查询的成本较低。

键空间维护了多个修订版(Revisions),每一个原子变动操作(一个事务可以由多个子操作组成)都会产生一个新的修订版。在集群的整个生命周期中,修订版都是单调递增的。

修订版同样支持索引,因此基于修订版的范围扫描也是高效的。

压缩操作需要指定一个修订版号,小于它的修订版会被移除。

一个键的一次生命周期(从创建到删除)叫做代(Generation),每个键可以有多个代。创建(修改)一个键时会增加键的版本(version),如果在当前修订版中键不存在则版本设置为1。删除一个键会创建一个墓碑(Tombstone),将版本设置为0,结束当前代。每次对键的值进行修改都会增加其版本号 —— 在同一代中版本号是单调递增的。

当压缩时,任何在压缩修订版之前结束的代,都会被移除。值在修订版之前的修改记录(仅仅保留最后一个)都会被移除。

物理视图

Etcd将数据存放在一个持久化的B+树中,处于效率的考虑,每个修订版仅仅存储相对于前一个修订版的数据状态变化(Delta)。单个修订版中可能包含了B+树中的多个键。

键值对的键,是三元组 ( major, sub, type):

  1. major:存储键值的修订版
  2. sub:用于区分相同修订版中的不同键
  3. type:用于特殊值的可选后缀,例如t表示值包含墓碑

键值对的值,包含从上一个修订版的的Delta。

B+树按键的词法字节序排列,基于修订版的范围扫描速度快,可以方便的查找从一个修订版到另外一个的值变更情况。

Etcd同时在内存中维护一个B树索引,用于加速针对键的范围扫描。索引的键是物理存储的键面向用户的映射,索引的值则是指向B+树修改点的指针。

术语
术语 说明
Term 选举任期,每次选举之后递增
Vote 选举时的一张投票
Entry Raft算法的日志的一个条目
Candidate 候选人,参与竞选以期称为下一任期的Leader的节点
leader 领导者,负责主动处理写入请求的节点
Follower 跟随者,不负责主动写入,仅仅从Leader同步数据
Commit 提交,持久化数据写入到日志中
Apply 将改变应用到状态机
Propose 提议,请求大部分节点同意数据写入
Raft算法

多个网站提供了Raft算法原理的动画展示,例如http://thesecretlivesofdata.com/raft/。

分布式复制状态机

所有Etcd节点服务器节点都是复制单元,它们共同组成复制状态机:

  1. 由多个复制单元组成。每个单元都是一个状态机
  2. Raft算法负责管理来自客户端的状态机操控命令。这些命令保存在受控复制的日志中
  3. 所有复制单元以严格一致的顺序,处理日志中的命令,因此产生相同的输出

raft-1

 

从外部看来,所有节点都读写复制状态机,从而实现协同,实际上,由单个Leader节点负责真正改变状态机,其它节点仅被动同步:

raft-2

 

Leader选举

任何时候,Raft节点处于以下三种状态之一:

  1. Leader:领导者,一个集群里只能存在一个Leader
  2. Follower:跟随者,不主动负责数据更新,重定向写请求
  3. Candidate:候选人,切换到此状态后,发起一场选举

节点状态变化状态机:

raft-3

 

说明如下:

  1. start up:起始状态,节点刚启动的时候自动进入的是follower状态
  2. times out, starts election:follower在启动之后,将开启一个选举超时的定时器(Leader的心跳会重置定时器),当这个定时器到期时,将切换到candidate状态发起选举
  3. times out, new election:进入candidate 状态之后就开始进行选举,但是如果在下一次选举超时到来之前,都还没有选出一个新的leader,那么还会保持在candidate状态重新开始一次新的选举
  4. receives votes from majority of servers:当candidate状态的节点,收到了超过半数的节点选票,那么将切换状态成为新的leader
  5. discovers current leader or new term:candidate状态的节点,如果收到了来自leader的消息,或者更高任期号的消息,都表示已经有leader了,将切换回到follower状态
  6. discovers server with higher term:leader状态下如果收到来自更高任期号的消息,将切换到follower状态。这种情况大多数发生在有网络分区的状态下

Raft算法选举具有以下特点:

  1. 毛遂自荐:自荐为Candidate后,就会增加Term的值,拉票。从不会推荐别的节点为Candidate
  2. 先来后到:每个节点只有一票,优先投给先提议的Candidate
  3. 随机超时:150-300ms之间,避免同时出现多个Candidate
  4. 直到冒白烟:并非每个任期都必然有Leader,如果选举超时到达后仍然没有Leader,则增加任期并再次选举。直到选举成功否则不罢休,就像罗马教皇选举一样

下图显示了一个没有Leader的任期:

raft-4

 

日志复制

每个日志条目具有三要素:

  1. 任期:日志在哪个任期内产生
  2. 索引号:严格递增的序列号
  3. 命令:执行什么数据操作

日志复制算法的细节:

  1. 客户端写请求都会被重定向给Leader
  2. 在收到这些请求之后, Leader会首先在自己的日志中添加一条新条目
  3. 在本地添加完日志之后,Leader将向集群中其他节点发送AppendEntries gRPC请求以同步日志条目
  4. 日志条目被成功同步后Leader将日志输入到Raft状态机中执行
  5. 大部分节点应答AppendEntries gRPC请求后(这意味着大部分节点写了WAL日志,数据安全了),Leader应答客户端

下面是一个日志复制进度图:

raft-5

可以看到:

  1. x<-4 这个操作只有一个节点跟上了Leader
  2. x<-3 这个操作大部分节点都已经确认
  3. 第3个Follower落后严重,和Leader差了两个任期
读写处理
读操作

Etcd支持以下两种读:

  1. Linearizable Read(默认):需要由Leader发起,Leader发送一个请求给所有节点,在收到多数节点的响应之后,才把结果返回给客户端。如果一个Follower收到linearizable read的请求,需要转发给leader来处理
  2. Serializable read(使用Get API的话要加WithSerializable选项): Leader和follower都可以在本地处理,时延低

etcdctl默认使用Linearizable读,可以指定读取方式:

Shell
1
etcdctl get --consistency=s / 
写操作

只有Etcd的Leader才能处理write。Follower在接到写请求后,要转发给leader来处理。

安装
系统要求

Etcd的性能基准测试在8 vCPU、16GB RAM、50GB SSD的GCE实例上进行,但是任何较为新的硬件架构+低延迟存储+若干GB内存的机器均可满足大部分场景。

由于数据存放在匿名内存而非内存映射文件,V2版本的数据比V3消耗更多内存。

构建

需要Go 1.9以上版本:

Shell
1
2
3
4
5
cd $GOPATH/src/github.com
mkdir etcd-io && cd etcd-io
git clone https://github.com/etcd-io/etcd.git
cd etcd
./build 
静态集群

如果Etcd集群成员是已知的,具有固定的IP地址,则可以静态的初始化一个集群。

每个节点都可以使用如下环境变量:

Shell
1
2
ETCD_INITIAL_CLUSTER="radon=http://10.0.2.1:2380,neon=http://10.0.3.1:2380"
ETCD_INITIAL_CLUSTER_STATE=new

或者如下命令行参数:

Shell
1
2
--initial-cluster radon=http://10.0.2.1:2380,neon=http://10.0.3.1:2380
--initial-cluster-state new

来指定集群成员。

初始化集群

完整的命令行示例:

Shell
1
2
3
4
5
6
7
8
etcd --name radon --initial-advertise-peer-urls http://10.0.2.1:2380
  --listen-peer-urls http://10.0.2.1:2380
  --listen-client-urls http://10.0.2.1:2379,http://127.0.0.1:2379
  --advertise-client-urls http://10.0.2.1:2380
  # 所有以-initial-cluster开头的选项,在第一次运行(Bootstrap)后都被忽略
  --initial-cluster-token etcd.gmem.cc
  --initial-cluster radon=http://10.0.2.1:2380,neon=http://10.0.3.1:2380
  --initial-cluster-state new 
使用TLS

Etcd支持基于TLS加密的集群内部、客户端-集群通信。每个集群节点都应该拥有被共享CA签名的证书:

Shell
1
2
3
4
5
6
7
8
9
10
# 密钥对、证书签名请求
openssl genrsa -out radon.key 2048
export SAN_CFG=$(printf "\n[SAN]\nsubjectAltName=IP:127.0.0.1,IP:10.0.2.1,DNS:radon.gmem.cc")
openssl req -new -sha256 -key radon.key -out radon.csr \
    -subj "/C=CN/ST=BeiJing/O=Gmem Studio/CN=Server Radon" \
    -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(echo $SAN_CFG))
# 执行签名
openssl x509 -req -sha256 -in radon.csr  -out radon.crt -CA ../ca.crt -CAkey ../ca.key -CAcreateserial -days 3650 \
     -extensions SAN -extfile <(echo "${SAN_CFG}")

初始化集群命令需要修改为: 

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
etcd --name radon --initial-advertise-peer-urls https://10.0.2.1:2380
  --listen-peer-urls https://10.0.2.1:2380
  --listen-client-urls https://10.0.2.1:2379,https://127.0.0.1:2379
  --advertise-client-urls https://10.0.2.1:2380
  --initial-cluster-token etcd.gmem.cc
                    # 指定集群成员列表
  --initial-cluster radon=https://10.0.2.1:2380,neon=https://10.0.3.1:2380
  --initial-cluster-state new         # 初始化新集群时使用
  --initial-cluster-state existing    # 加入已有集群时使用
 
  # 客户端TLS相关参数
  --client-cert-auth
  --trusted-ca-file=/usr/share/ca-certificates/GmemCA.crt
  --cert-file=/opt/etcd/cert/radon.crt
  --key-file=/opt/etcd/cert/radon.key
 
  # 集群内部TLS相关参数
  --peer-client-cert-auth
  --peer-trusted-ca-file=/usr/share/ca-certificates/GmemCA.crt
  --peer-cert-file=/opt/etcd/cert/radon.crt
  --peer-key-file=/opt/etcd/cert/radon.key
成员发现

某些情况下,集群成员的IP地址可能是未知、不固定的。典型场景包括云环境、使用DHCP网络。

这种情况下,启动Etcd集群需要依赖于成员发现,成员发现的方法有两种:

  1. Etcd发现服务
  2. DNS SRV记录
配置

所有配置项参考:https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/configuration.md

常用配置项
配置 说明
heartbeat-interval

心跳间隔,Leader通知所有的followers,他还是Leader的频率

根据成员间网络RTT来确定适当的值,默认100ms

election-timeout

选举超时,一个Foller节点等待大多数节点同意它成为Leader的心跳的最大时间,默认1000ms。超时后放弃

般可设置为RTT的0.5-1.5倍

snapshot-count 每发生多少次变更,就保存一次快照。V2后端快照是高成本操作
max-snapshots 最大保存的快照数量,默认5,0表示不限制
quota-backend-bytes

数据大小,默认是2G,当数据达到2G的时候就不允许写入,必须对历史数据进行压缩才能继续写入

默认2G,推荐8G

max-request-bytes Raft消息最大字节数,默认1.5M,推荐10M
auto-compaction-retention

Etcd存储多版本数据,随着写入的主键增加,历史版本需要定时清理。默认的历史数据是不会清理的,数据达到2G就不能写入,必须要清理压缩历史数据才能继续写入

该参数决定清理压缩间隔,单位小时

3.2.0默认每小时压缩一次

initial-election-tick-advance 如果设置为true,在Boot时会快进初始选举(fast-forward initial election)。目标是更快选举
grpc-keepalive-min-time

服务器ping客户端之前,等待的最小(没有活动的)时间,默认5s

Etcd使用gRPC协议,gRPC协议基于HTTP/2,gRPC支持使用HTTP/2的Ping帧来保活 —— 度量RTT和探测一个gRPC连接是否坏掉了

grpc-keepalive-interval 服务器ping客户端的频率(当连接是alive)的,默认2h
grpc-keepalive-timeout 在关闭不响应连接之前需要等待的时间,默认20s
log-level 日志级别,默认info
data-dir 数据存放目录
wal-dir 写前日志目录
max-wals WAL文件的最大数量,默认5,0表示不限制
name 成员的名字 
quota-backend-bytes 后端存储超过指定的大小发起alarm 
cors  CORS请求白名单
initial-advertise-peer-urls 向其它成员通知的,当前成员的Peer URL。默认 http://localhost:2380
advertise-client-urls 当前成员的接受客户端连接的地址
initial-cluster 初始的集群配置,逗号分隔的成员名车:Peer URL列表 
initial-cluster-state 

初始的集群状态:

  1. new  表示新建集群
  2. existing 表示加入既有集群
enable-v2 是否允许V2版本的客户端请求
指标
访问指标
Shell
1
2
3
4
5
6
# 无身份验证的集群
curl http://localhost:2379/metrics
 
# 基于客户端证书进行身份验证的集群
curl --cacert=/etc/kubernetes/pki/ca.crt --key=client.key --cert=client.cert \
  https://localhost:2379/metrics 
常用指标
指标 说明
服务器基础指标
leader_changes_seen_total Leader变换的次数。Leader频繁的变化严重的影响性能
proposals_committed_total 提交的Consensus Proposal总数。不同成员的该指标值应该差距较小,如果一个成员和Leader之间总是有很大差距,说明成员缓慢或不健康
proposals_applied_total 应用的Consensus Proposal总数。Etcd会异步的应用已经提交的Proposal。通常情况下Commit/Apply差距应该较小,高负载下也最多数K,如果差距持续变大,提示Etcf过载。过载可能发生在昂贵查询(大范围Range查询)、大的Txn操作时
proposals_pending 正在排队等候Commit的Proposal数量。数值过大提示客户端导致的高负载、或者成员无法Commit
proposals_failed_total 失败的原因可能包括:由于Leader选举导致的临时失败,由于失去Quorum导致的集群宕机
磁盘相关指标
wal_fsync_duration_seconds 写前日志的fsync延迟直方图(分布),Etcd会在Apply之前,将日志条目写入到磁盘
backend_commit_duration_seconds backend_commit的延迟直方图,当Etcd将最近变更的增量快照写入到磁盘时,调用backend_commit
网络相关指标
peer_sent_bytes_total 发送给特定成员的字节数
peer_received_bytes_total 从特定成员接收的字节数
peer_sent_failures_total 发送失败字节数
peer_received_failures_total 接收失败字节数
peer_round_trip_time_seconds RTT分布直方图
client_grpc_sent_bytes_total 发送给gRPC客户端的字节数
client_grpc_received_bytes_total 从gRPC客户端接收到的字节数
运维

为了保持可靠性,Etcd集群需要周期性的运维。运维操作通常能够自动完成,并且不会宕机,仅仅稍微影响性能。

运维操作主要是管理Etcd键空间的存储资源,如果用尽磁盘配额,则集群进入维护模式,仅仅支持读/删键值的操作。为了避免这种情况,必须压缩键空间的修订版历史(就是丢弃旧版本)。此外,通过碎片整理,也能够回收存储资源。

通过周期性的对集群状态进行快照备份,也可以防止误操作导致的数据丢失。

Raft日志暂存

配置项snapshot-count决定了执行Compaction(和键空间压缩是两回事)操作之前,暂存于内存中的Raft日志条目的数量。当数量到达时,服务器会:

  1. 将快照数据持久化到硬盘
  2. truncate旧的日志条目

如果一个缓慢的Follower请求已经被truncated的日志条目,则Leader强制Follower使用快照来覆盖其自身状态

如果snapshot-count取值很高,则内存中存储更多的日志条目,内存占用变高。于此同时,由于Leader维持更多的日志,因此慢Follower发生快照覆盖的几率更小。

从3.2开始,默认值从10000改为100000。

如果snapshot-count取值过大,可能会影响写吞吐量。内存中对象数据过多,会导致Go GC的Mark阶段变慢。

键空间压缩

Etcd保留键空间的精确历史,旧的修订版需要被定期删除,避免影响性能甚至耗尽键空间存储资源。

手工压缩

键空间会自动进行周期性清理,你也可以调用命令手工触发清理:

Shell
1
2
3
4
5
# 清理掉修订版3以前的历史
etcdctl compact 3
 
# etcdctl get --rev=2 somekey
# Error: rpc error: code = 11 desc = etcdserver: mvcc: required revision has been compacted
自动压缩

使用下面的选项,可以每小时压缩一次:

Shell
1
etcd --auto-compaction-retention=1

在3.2.0,压缩操作默认就是每小时运行一次。

在3.3.0、3.3.1、3.3.2,支持额外的选项: 

Shell
1
2
3
4
5
# 保留最新的1000个修订版,每5分钟压缩一次
etcd --auto-compaction-mode=revision --auto-compaction-retention=1000
 
# 每72/10小时压缩一次,保留72小时的 retention window
-auto-compaction-mode=periodic --auto-compaction-retention=72h
碎片整理 

压缩键空间后,后端数据库中可能存在碎片,碎片是已经释放却无法使用的空间。 

要整理某个成员的碎片,执行:

Shell
1
etcdctl defrag

要整理和默认Endpoint关联的所有Endpoint的碎片,执行:

Shell
1
etcdctl defrag --cluster
存储空间配额

存储空间配额确保了Etcd集群可靠的运作。没有配额,Etcd可能会性能低下(键空间无限增长),或者直接耗尽磁盘空间(没有自动化整理的情况下)。 

使用下面的选项来设置配额:

Shell
1
etcd --quota-backend-bytes=$((16*1024*1024))
快照备份 

应当周期性的将Etcd后端数据库进行备份: etcdctl snapshot save backup.db

CLI
etcdctl
全局选项
选项 说明
--cacert 用于校验服务器证书的根证书
--cert 表明本客户端身份的证书
--key= 表明本客户端身份的私钥
--debug 启用客户端调试日志
--dial-timeout=2s 连接超时
--command-timeout=5s 短时运行的命令的超时
--endpoints gRPC端点列表
--insecure-skip-tls-verify 跳过服务器证书验证
--insecure-transport 禁用传输层安全
--user=username[:password] 基于口令进行身份验证
-w  --write-out="simple" 输出格式:fields, json, protobuf, simple, table

etcdctl命令的全局选项,前缀以ETCDCTL,改为大写下划线形式,即为对应环境变量:

环境变量 说明
ETCDCTL_API 对于3.4以前的版本,需要指定该环境变量为3,才能使用v3 API
ETCDCTL_ENDPOINTS 访问的Etcd gRPC端点列表
ETCDCTL_DIAL_TIMEOUT 连接超时
ETCDCTL_CACERT 指定根证书
ETCDCTL_CERT 指定证书文件
ETCDCTL_KEY 指定密钥文件
子命令
子命令 说明
put

格式:PUT [options] <key> <value>

选项:

lease 附到键上的租约ID,租约和键的到期时间有关
prev-kv 返回修改前的键值
ignore-value 更新键,值不变
ignore-lease 更新键,租约不变

示例:

Shell
1
2
etcdctl put foo bar --lease=1234abcd
etcdctl put foo --ignore-value
get 

格式:GET [options] <key> [range_end]

选项:

hex  打印为HEX格式
limit 限制最大结果数量
prefix 仅仅获取匹配前缀的键
order 结果升降序 ASCEND, DESCEND
sort-by 结果排序依据 CREATE, KEY, MODIFY, VALUE, VERSION
rev 指定修订版
print-value-only 仅仅打印值
consistency  一致性保证,串行化操作
from-key  获取比指定键更大(字节比较)的键
keys-only 仅仅打印键

示例:

Shell
1
2
3
4
5
6
7
8
# 得到所有键的列表
etcdctl get --prefix=true "" --keys-only&nbsp;
 
# 得到所有键值
etcdctl get --from-key ''
 
# 查询K8S
etcdctl get /registry/storageclasses/ceph-rbd-hdd  --prefix --write-out="json" | jq .
lock

在指定的名字上请求一个分布式锁,一旦获取,则知道etcdctl命令退出,会一直占用锁

格式:LOCK [options] <lockname> [command arg1 arg2 ...]

选项:

ttl 锁会话超时秒数

del 

格式:DEL [options] <key> [range_end]

选项:

prefix 删除匹配前缀的键
from-key 删除比指定键更大(字节比较)的键
prev-kv 返回删除的键值

示例:

Shell
1
2
# 清空
etcdctl del --prefix=true "" 
txn 

从标准输入读取多个etcd请求,将它们作为单个事务执行。事务包括一个条件列表,以及当条件满足时需要执行的请求列表、一个条件不满足时需要执行的请求列表

格式:TXN [options]

事务语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Txn> ::= <CMP>* "\n" <THEN> "\n" <ELSE> "\n"
<CMP> ::= (<CMPCREATE>|<CMPMOD>|<CMPVAL>|<CMPVER>|<CMPLEASE>) "\n"
<CMPOP> ::= "<" | "=" | ">"
<CMPCREATE> := ("c"|"create")"("<KEY>")" <CMPOP> <REVISION>
<CMPMOD> ::= ("m"|"mod")"("<KEY>")" <CMPOP> <REVISION>
<CMPVAL> ::= ("val"|"value")"("<KEY>")" <CMPOP> <VALUE>
<CMPVER> ::= ("ver"|"version")"("<KEY>")" <CMPOP> <VERSION>
<CMPLEASE> ::= "lease("<KEY>")" <CMPOP> <LEASE>
<THEN> ::= <OP>*
<ELSE> ::= <OP>*
<OP> ::= ((see put, get, del etcdctl command syntax)) "\n"
<KEY> ::= (%q formatted string)
<VALUE> ::= (%q formatted string)
<REVISION> ::= "\""[0-9]+"\""
<VERSION> ::= "\""[0-9]+"\""
<LEASE> ::= "\""[0-9]+\""

示例:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 交互模式
 
etcdctl txn -i
# compares:
mod("key1") > "0"
 
# success requests (get, put, delete):
put key1 "overwrote-key1"
 
# failure requests (get, put, delete):
put key1 "created-key1"
put key2 "some extra key"
 
 
# 非交互模式
 
etcdctl txn <<<'mod("key1") > "0"
 
put key1 "overwrote-key1"
 
put key1 "created-key1"
put key2 "some extra key"
 
'
compaction

丢弃指定修订版之前的所有etcd事件历史。某些etcd使用MVCC模型,它会保留所有键更新为事件历史。当足够老旧的修订版不再需要时,可以执行该操作回收磁盘空间

格式:COMPACTION [options] <revision>

选项:

physical 设置为true等到操作完成

示例:

Shell
1
etcdctl compaction 1234 
watch

监听针对指定键/键前缀/键范围的事件流,并在事件发生时执行命令。该命令会一直执行,知道用户请求停止或出错

格式:WATCH [options] [key or prefix] [range_end] [--] [exec-command arg1 arg2 ...]

选项:

hex 打印为HEX格式
interactive 交互式的监控会话
prefix 在前缀上监控
prev-kv  获取事件发生之前的键值
rev  从指定修订版开始监控,用于监控过去的事件

示例:

Shell
1
etcdctl watch foo -- echo watch event received
lease

进行键的租约管理

示例:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建一个新的租约,保证过期时间在60s之后
etcdctl lease grant 60
 
# 吊销一个租约,附着的键全部被删除
etcdctl lease revoke 32695410dcc0ca06
 
# 打印所有活动的租约列表
etcdctl lease list
 
# 获取租约剩余时间
etcdctl lease timetolive 2d8257079fa1bc0c
# 以及附着的键
etcdctl lease timetolive 2d8257079fa1bc0c --keys
 
# 周期性的刷新租约,防止过期
etcdctl lease keep-alive 32695410dcc0ca0 
member

集群成员管理

示例:

Shell
1
2
3
4
5
6
7
8
9
10
11
# 添加一个新成员    名字       它的端点列表,逗号分隔
etcdctl member add newMember --peer-urls=https://127.0.0.1:12345
 
# 更新成员的端点列表
etcdctl member update 2be1eb8f84b7f63e --peer-urls=https://127.0.0.1:11112
 
# 移除成员
etcdctl member remove 2be1eb8f84b7f63e
 
# 列出成员
etcdctl member list
endpoint

查询端点信息

示例:

Shell
1
2
3
4
5
6
7
8
# 查询端点健康状况
etcdctl endpoint health
 
# 列出端点状态,可以显示是否Leader
etcdctl -w table endpoint status
 
# 显示端点的键值存储的哈希值
etcdctl endpoint hashkv
check perf

在60s内测试etcd集群的性能。该命令会创建大量的键空间历史,这些历史可以被--auto-compact --auto-defrag选项自动清除

选项:

load 性能检查的工作负载模型: s(small), m(medium), l(large), xl(xLarge)
prefix 为了执行性能测试,需要写入键值,该选项指定这些键值的前缀
auto-compact 测试完毕后自动进行清理
auto-defrag 测试完毕后自动进行碎片整理

示例:

Shell
1
etcdctl check perf --load="l"
check datascale 测试存储数据需要的内存数量,选项同上
defrag

对后端数据文件进行碎片整理。当etcd成员回收磁盘空间(因为删除/compact键)时,会留下很多空隙,这些空隙存储在一个free list中,数据文件的大小不会变化。 碎片整理操作会释放空闲空间到文件系统

注意:

  1. 对运行中的成员进行碎片化整理,会阻塞数据读写操作
  2. 碎片化整理请求不会传播到集群其它成员。在--endpoints中指定所有成员,或者指定--cluster标记,对所有成员执行碎片整理

选项:

data-dir 指定数据目录,用于整理没有被etcd使用的数据目录
endpoints 指定需要整理的节点
cluster 全集群整理

示例:

Shell
1
2
etcdctl defrag --cluster
etcdctl defrag --data-dir default.etcd 
snapshot save

将某个时间点的etcd后端数据库的快照保存为文件

示例:

Shell
1
etcdctl snapshot save snapshot.db
snapshot restore

从快照+新的集群配置中恢复出一个数据目录,供etcd集群成员使用。将快照恢复到新集群配置的所有成员,会初始化一个新的集群

选项:

data-dir 数据目录的路径
wal-dir 写前日志的路径
initial-cluster 被恢复的集群的初始集群配置
initial-cluster-token  被恢复的集群的初始Token
initial-advertise-peer-urls 被恢复集群的Peer URLS列表
name 被恢复的集群成员名字
skip-hash-check 忽略快照完整性检查(如果从数据目录进行了拷贝,则需要此选项)

示例:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
etcdctl snapshot save snapshot.db
 
# 恢复成员
etcdctl snapshot restore snapshot.db --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://127.0.0.1:12380 --name sshot1 \
  --initial-cluster 'sshot1=http://127.0.0.1:12380,sshot2=http://127.0.0.1:22380,sshot3=http://127.0.0.1:32380'
etcdctl snapshot restore snapshot.db --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://127.0.0.1:22380  --name sshot2 \
  --initial-cluster 'sshot1=http://127.0.0.1:12380,sshot2=http://127.0.0.1:22380,sshot3=http://127.0.0.1:32380'
etcdctl snapshot restore snapshot.db --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://127.0.0.1:32380  --name sshot3 \
  --initial-cluster 'sshot1=http://127.0.0.1:12380,sshot2=http://127.0.0.1:22380,sshot3=http://127.0.0.1:32380'
 
# 启动集群成员
etcd --name sshot1 --listen-client-urls http://127.0.0.1:2379 \
--advertise-client-urls http://127.0.0.1:2379 --listen-peer-urls http://127.0.0.1:12380 &
etcd --name sshot2 --listen-client-urls http://127.0.0.1:22379 \
--advertise-client-urls http://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 &
etcd --name sshot3 --listen-client-urls http://127.0.0.1:32379 \
--advertise-client-urls http://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 &
snapshot status 查看镜像的数据库哈希、修订版、键总数、尺寸信息:
Shell
1
etcdctl snapshot status file.db
move-leader 修改集群Leader:
Shell
1
2
3
4
5
6
transferee_id=$(./etcdctl \
  --endpoints localhost:2379,localhost:22379,localhost:32379 \
  endpoint status | grep -m 1 "false" | awk -F', ' '{print $2}')
 
# 转移Leader
etcdctl --endpoints ${leader_ep} move-leader ${transferee_id}
elect

参与一个命名的选举。节点通过提供一个提议值(proposal value)来宣告自己的参选资格。该命令在Leader出现后退出,打印Leader的Proposal

选项:

listen 观察选举,不作为候选人

auth enable 启用etcd集群的身份验证功能
auth disable 禁用etcd集群的身份验证功能
role 管理角色,为角色授予特权:
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 添加角色
etcdctl --user=root:123 role add myrole
# 获取角色
etcdctl --user=root:123 role get myrole
# 删除角色
etcdctl --user=root:123 role delete myrole
# 列出角色
etcdctl --user=root:123 role list
 
# 授予特权
# 为myrole授予对foo键的读写权限
etcdctl --user=root:123 role grant-permission myrole readwrite foo
# 为myrole授予对foo/*的读写权限
etcdctl --user=root:123 role grant-permission --prefix myrole readwrite foo/
 
# 吊销特权
etcdctl --user=root:123 role revoke-permission myrole foo
user 管理用户,为用户授予角色或特权:
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 添加用户
etcdctl --user=root:123 user add myuser
# 获取用户
etcdctl --user=root:123 user get myuser
# 删除用户
etcdctl --user=root:123 user delete myuser
# 列出用户
etcdctl --user=root:123 user list
 
# 设置密码
etcdctl --user=root:123 user passwd myuser
 
# 授予角色
etcdctl --user=root:123 user grant-role userA roleA
# 吊销角色
etcdctl --user=root:123 user revoke-role userA roleA
make-mirror

在另外一个镜像创建指定键前缀的镜像

migrate 将V2格式的键值迁移为V3的MVCC存储
auger

目前K8S存放在Etcd中的数据都是以Protobuf编码的,因此无法阅读,使用该工具可以解码、编码:

Shell
1
2
3
4
5
# 解码为YAML
etcdctl get /registry/services/specs/kube-system/kube-dns | auger decode
 
# 编码为Protobuf并写入到etcd
cat kube-dns.yaml | auger encode | etcdctl put /registry/services/specs/kube-system/kube-dns
API
REST
递归读取
JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# curl -s -k --cacert /etc/etcd/ca.crt --cert /etc/etcd/client.crt --key /etc/etcd/client.key \
#   'https://etcd.gmem.cc:2379/v2/keys/path/to?recursive=true&sorted=true'
 
{
  "action": "get",
  "node": {
    "key": "/galera/mariadb_galera",
    "dir": true,
    "nodes": [
      {
        "key": "/galera/mariadb_galera/172.28.0.27",
        "dir": true,
        "nodes": [
          {
            "key": "/galera/mariadb_galera/172.28.0.27/ipaddress",
            "value": "172.28.0.27",
            "modifiedIndex": 8,
            "createdIndex": 8
          }
        ],
        "modifiedIndex": 8,
        "createdIndex": 8
      }
    ]
  }
} 
常见问题
零散问题
etcdmain: member *** has already been bootstrapped

节点损坏,修复后重新加入集群报此错误,只需要将启动命令中的new换为existing即可:

Shell
1
--initial-cluster-state existing
grpc: addrConn.createTransport failed to connect etcd

在我们的一个arm64的1000+节点K8S集群上,单节点的Etcd出现此报错,日志中还可以看到连接127.0.0.1:2380超时。观察发现APIServer的CPU占用很高,禁用HTTPS可以改善。

connection error: desc = "transport: Error while dialing dial tcp 0.0.0.0:2379: i/o timeout"; Reconnecting to {0.0.0.0:2379 0 }

可能是防火墙问题

mvcc: database space exceeded or applying raft message exceeded backend quota

当Etcd的任何成员的磁盘空间配额到达,在集群范围内出现此报错。集群将进入维护模式,仅仅允许读、删操作,无法插入新数据。

is starting a new election at term

集群失去法定人数(Quorum),正在建立新的Leader时出现此信息。

apply entries took too long

应用日志条目到状态机,消耗超过100ms的情况下报此错误。可能原因:

  1. 磁盘太慢:可以监控 commitduration_seconds指标,正常情况下P99应该小于25ms
  2. CPU饥饿:给Etcd分配给多的CPU资源,或者将Etcd部署到独立节点。Leader会周期性的发送心跳给Follower,这样才能维持自己的Leader身份,否则选举可能被触发
  3. 网络太慢:检查网络延迟和节点之间的丢包率
snapshotting is taking more than seconds to finish

发送快照速度太慢,超过了千兆网络下预期的小于30s。

cluster ID mismatch

节点尝试加入到已经构建好的集群。

rafthttp: failed to find member

集群状态非法,成员无法加入。

← Istio学习笔记
Ubuntu的时钟同步 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • gRPC学习笔记
  • SOFAStack学习笔记
  • Go语言单元测试和仿冒
  • Go语言学习笔记
  • Go语言IO编程

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
  • Three.js学习笔记 24 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2