Etcd学习

Sunday, December 12, 2021

1.概述

Etcd 是一个开源的、高度一致的分布式键值存储,它提供了一种可靠的方式来存储需要由分布式系统或机器集群访问的数据。它可以优雅地处理网络分区期间的领导者选举,即使在领导者节点中也可以容忍机器故障。

从简单应用程序到Kubernetes到任何复杂性的应用程序都可以从etcd中读写数据。应用程序可以读取和写入etcd中的数据。一个简单的用例是将数据库连接详细信息或功能标志存储在etcd中作为键值对。可以观察这些值,使您的应用在更改时可以重新配置自己。高级用途利用etcd的一致性保证来实施数据库领导者选举或跨一组工作人员执行分布式锁定。

etcd内部采用raft协议作为一致性算法,etcd基于Go语言实现,etcd与zookeeper相比算是轻量级系统,两者的一致性协议也一样,etcd的raft比zookeeper的paxos简单。安装配置简单,有丰富的http api,支持SSL证书验证,ETCD 被广泛用在分布式系统中,如K8S中,还有各种微服务中。

1.1 为什么需要Etcd?

所有的分布式系统,都面临的一个问题是多个节点之间的数据共享问题,这个和团队协作的道理是一样的,成员可以分头干活,但总是需要共享一些必须的信息,比如谁是 leader, 都有哪些成员,依赖任务之间的顺序协调等。所以分布式系统要么自己实现一个可靠的共享存储来同步信息(比如 Elasticsearch ),要么依赖一个可靠的共享存储服务,而 Etcd 就是这样一个服务,同zookeeper。而kuberntes 系统使用 etcd 存储所有数据,此外calico网络也使用该etcd集群。

1.2 Etcd的特点

  • 简单:安装配置使用简单,提供 HTTP API

  • 安全:支持 SSL 证书

  • 可靠:采用 raft 算法,实现分布式系统数据的可用性和一致性

1.3 Etcd 提供什么能力?

提供存储以及获取数据的接口,它通过协议保证 Etcd 集群中的多个节点数据的强一致性。用于存储元信息以及共享配置。

提供监听机制,客户端可以监听某个key或者某些key的变更(v2和v3的机制不同)。用于监听和推送变更。

提供key的过期以及续约机制,客户端通过定时刷新来实现续约(v2和v3的实现机制也不一样)。用于集群监控以及服务注册发现。

提供原子的CAS(Compare-and-Swap)和 CAD(Compare-and-Delete)支持(v2通过接口参数实现,v3通过批量事务实现)。用于分布式锁以及leader选举。

1.4 Etcd,Zookeeper,Consul 比较

这三个产品是经常被人拿来做选型比较的。 Etcd 和 Zookeeper 提供的能力非常相似,都是通用的一致性元信息存储,都提供watch机制用于变更通知和分发,也都被分布式系统用来作为共享信息存储,在软件生态中所处的位置也几乎是一样的,可以互相替代的。

二者除了实现细节,语言,一致性协议上的区别,最大的区别在周边生态圈。Zookeeper 是apache下的,用java写的,提供rpc接口,最早从hadoop项目中孵化出来,在分布式系统中得到广泛使用(hadoop, solr, kafka, mesos 等)。

Etcd 是coreos公司旗下的开源产品,比较新,以其简单好用的rest接口以及活跃的社区俘获了一批用户,在新的一些集群中得到使用(比如kubernetes)。虽然v3为了性能也改成二进制rpc接口了,但其易用性上比 Zookeeper 还是好一些。

而 Consul 的目标则更为具体一些,Etcd 和 Zookeeper 提供的是分布式一致性存储能力,具体的业务场景需要用户自己实现,比如服务发现,比如配置变更。而Consul 则以服务发现和配置变更为主要目标,同时附带了kv存储。 在软件生态中,越抽象的组件适用范围越广,但同时对具体业务场景需求的满足上肯定有不足之处。

1.5 使用场景

  • 键值对存储
  • 服务注册与发现
  • 消息发布与订阅
  • 分布式通知与协调
  • 分布式锁

1.6 概念解释

这些概念介绍来自炎焱

Raft etcd 所采用的保证分布式系统强一致性的算法。
Node 一个 Raft 状态机实例
Member 一个 etcd 实例。它管理着一个 Node,并且可以为客户端请求提供服务
Cluster 由多个 Member 构成可以协同工作的 etcd 集群
Peer 对同一个 etcd 集群中另外一个 Member 的称呼
Client 向 etcd 集群发送 HTTP 请求的客户端
WAL 预写式日志,etcd 用于持久化存储的日志格式
snapshot etcd 防止 WAL 文件过多而设置的快照,存储 etcd 数据状态。
Proxy etcd 的一种模式,为 etcd 集群提供反向代理服务
Leader Raft 算法中通过竞选而产生的处理所有数据提交的节点。
Follower 竞选失败的节点作为 Raft 中的从属节点,为算法提供强一致性保证。
Candidate 当 Follower 超过一定时间接收不到 Leader 的心跳时转变为 Candidate 开始竞选
Term 某个节点成为 Leader 到下一次竞选时间,称为一个 Term
Index 数据项编号。Raft 中通过 Term 和 Index 来定位数据

2. 搭建本地集群

这里简单在本地同一台机器上搭建一个 Etcd 集群,每个节点监听不同的端口,数据都存储在 /Users/Jormin/etcd/data 目录下,如下:

节点 IP 客户端端口 内部端口 数据路径
etcd-01 127.0.0.1 12379 12380 /Users/Jormin/etcd/data/etcd1
etcd-02 127.0.0.1 22379 22380 /Users/Jormin/etcd/data/etcd2
etcd-03 127.0.0.1 32379 32380 /Users/Jormin/etcd/data/etcd3

data-dir 需要配置成绝对路径,否则会在当前路径下创建配置的路径

安装脚本:

#!/bin/bash

TOKEN=etcd-cluster
CLUSTER_STATE=new
NAME_1=etcd-01
NAME_2=etcd-02
NAME_3=etcd-03
HOST_1=127.0.0.1
HOST_2=127.0.0.1
HOST_3=127.0.0.1
DATA_DIR_1=/Users/Jormin/etcd/data/etcd1
DATA_DIR_2=/Users/Jormin/etcd/data/etcd2
DATA_DIR_3=/Users/Jormin/etcd/data/etcd3
CLIENT_PORT_1=12379
CLIENT_PORT_2=22379
CLIENT_PORT_3=32379
INNER_PORT_1=12380
INNER_PORT_2=22380
INNER_PORT_3=32380
CLUSTER=${NAME_1}=http://${HOST_1}:${INNER_PORT_1},${NAME_2}=http://${HOST_2}:${INNER_PORT_2},${NAME_3}=http://${HOST_3}:${INNER_PORT_3}

# node1
nohup etcd --data-dir=${DATA_DIR_1} --name ${NAME_1} \
	--initial-advertise-peer-urls http://${HOST_1}:${INNER_PORT_1} --listen-peer-urls http://${HOST_1}:${INNER_PORT_1} \
	--advertise-client-urls http://${HOST_1}:${CLIENT_PORT_1} --listen-client-urls http://${HOST_1}:${CLIENT_PORT_1} \
	--initial-cluster ${CLUSTER} \
	--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} \
> /tmp/etcd1.log 2>&1 &
	
# node2
nohup etcd --data-dir=${DATA_DIR_2} --name ${NAME_2} \
	--initial-advertise-peer-urls http://${HOST_2}:${INNER_PORT_2} --listen-peer-urls http://${HOST_2}:${INNER_PORT_2} \
	--advertise-client-urls http://${HOST_2}:${CLIENT_PORT_2} --listen-client-urls http://${HOST_2}:${CLIENT_PORT_2} \
	--initial-cluster ${CLUSTER} \
	--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} \
> /tmp/etcd2.log 2>&1 &
	
# node3
nohup etcd --data-dir=${DATA_DIR_3} --name ${NAME_3} \
	--initial-advertise-peer-urls http://${HOST_3}:${INNER_PORT_3} --listen-peer-urls http://${HOST_3}:${INNER_PORT_3} \
	--advertise-client-urls http://${HOST_3}:${CLIENT_PORT_3} --listen-client-urls http://${HOST_3}:${CLIENT_PORT_3} \
	--initial-cluster ${CLUSTER} \
	--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} \
> /tmp/etcd3.log 2>&1 &

3. etcdctl 基本使用

以下是 etcdctl 的说明:

➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --help
NAME:
	etcdctl - A simple command line client for etcd3.

USAGE:
	etcdctl [flags]

VERSION:
	3.6.0-pre

API VERSION:
	3.6


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
	completion		Generate completion script
	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)

其中 OPTIONS 部分是所有命令通用的全局选项。

3.1 版本信息: version

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 version
etcdctl version: 3.6.0-pre
API version: 3.6

3.2 集群节点: endpoint

以下是 endpoint 命令的使用说明:

➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 endpoint --help
NAME:
	endpoint - Endpoint related commands

USAGE:
	etcdctl endpoint <subcommand> [flags]

API VERSION:
	3.6


COMMANDS:
	hashkv	Prints the KV history hash for each endpoint in --endpoints
	health	Checks the healthiness of endpoints specified in `--endpoints` flag
	status	Prints out the status of endpoints specified in `--endpoints` flag

OPTIONS:
      --cluster[=false]	use all endpoints from the cluster member list
  -h, --help[=false]	help for endpoint
节点状态: endpoint status
➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 endpoint status
+-----------------+------------------+-----------+---------+-----------+------------+-----------+------------+--------------------+--------+
|    ENDPOINT     |        ID        |  VERSION  | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------+------------------+-----------+---------+-----------+------------+-----------+------------+--------------------+--------+
| 127.0.0.1:12379 | 7cdab567eef63dd5 | 3.6.0-pre |   25 kB |      true |      false |         2 |          8 |                  8 |        |
| 127.0.0.1:22379 | ac59546d70530a35 | 3.6.0-pre |   25 kB |     false |      false |         2 |          8 |                  8 |        |
| 127.0.0.1:32379 | bb6bc014a5e1e32b | 3.6.0-pre |   25 kB |     false |      false |         2 |          8 |                  8 |        |
+-----------------+------------------+-----------+---------+-----------+------------+-----------+------------+--------------------+--------+
节点健康信息: endpoint health
➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 endpoint health
+-----------------+--------+------------+-------+
|    ENDPOINT     | HEALTH |    TOOK    | ERROR |
+-----------------+--------+------------+-------+
| 127.0.0.1:12379 |   true | 5.894617ms |       |
| 127.0.0.1:32379 |   true | 5.867142ms |       |
| 127.0.0.1:22379 |   true | 5.913361ms |       |
+-----------------+--------+------------+-------+
节点历史hash: endpoint hashkv
➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 endpoint hashkv
+-----------------+------------+
|    ENDPOINT     |    HASH    |
+-----------------+------------+
| 127.0.0.1:12379 | 1084519789 |
| 127.0.0.1:22379 | 1084519789 |
| 127.0.0.1:32379 | 1084519789 |
+-----------------+------------+

3.3 成员: member / move-leader

以下是 endpoint 命令的使用说明:

➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member --help
NAME:
	member - Membership related commands

USAGE:
	etcdctl member <subcommand> [flags]

API VERSION:
	3.6


COMMANDS:
	add	Adds a member into the cluster
	list	Lists all members in the cluster
	promote	Promotes a non-voting member in the cluster
	remove	Removes a member from the cluster
	update	Updates a member in the cluster

OPTIONS:
  -h, --help[=false]	help for member
成员列表: member list
➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member list
+------------------+---------+---------+------------------------+------------------------+------------+
|        ID        | STATUS  |  NAME   |       PEER ADDRS       |      CLIENT ADDRS      | IS LEARNER |
+------------------+---------+---------+------------------------+------------------------+------------+
| 7cdab567eef63dd5 | started | etcd-01 | http://127.0.0.1:12380 | http://127.0.0.1:12379 |      false |
| ac59546d70530a35 | started | etcd-02 | http://127.0.0.1:22380 | http://127.0.0.1:22379 |      false |
| bb6bc014a5e1e32b | started | etcd-03 | http://127.0.0.1:32380 | http://127.0.0.1:32379 |      false |
+------------------+---------+---------+------------------------+------------------------+------------+
添加成员: member add

这里新增一个节点 etcd-04,监听端口 42379 和 42380,先使用 member add 命令添加新的节点,然后根据打印的信息启动新的节点

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member add etcd-04 --peer-urls=http://127.0.0.1:42380 --learner=false
Member 639de5e60f9c824f added to cluster f9947e0cbc17617c

ETCD_NAME="etcd-04"
ETCD_INITIAL_CLUSTER="etcd-04=http://127.0.0.1:42380,etcd-01=http://127.0.0.1:12380,etcd-02=http://127.0.0.1:22380,etcd-03=http://127.0.0.1:32380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://127.0.0.1:42380"
ETCD_INITIAL_CLUSTER_STATE="existing"

添加结点信息后,启动新的节点:

nohup etcd --data-dir=/Users/Jormin/etcd/data/etcd4 --name etcd-04 \
	--initial-advertise-peer-urls http://127.0.0.1:42380 --listen-peer-urls http://127.0.0.1:42380 \
	--advertise-client-urls http://127.0.0.1:42379 --listen-client-urls http://127.0.0.1:42379 \
	--initial-cluster etcd-01=http://127.0.0.1:12380,etcd-04=http://127.0.0.1:42380,etcd-02=http://127.0.0.1:22380,etcd-03=http://127.0.0.1:32380 \
	--initial-cluster-state existing \
	--initial-cluster-token etcd-cluster \
> /tmp/etcd4.log 2>&1 &

启动后查看最新的成员列表:

➜  etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member list
+------------------+---------+---------+------------------------+------------------------+------------+
|        ID        | STATUS  |  NAME   |       PEER ADDRS       |      CLIENT ADDRS      | IS LEARNER |
+------------------+---------+---------+------------------------+------------------------+------------+
|  628170c800dbcee | started | etcd-03 | http://127.0.0.1:32380 | http://127.0.0.1:32379 |      false |
| 9e737febb6b99eee | started | etcd-02 | http://127.0.0.1:22380 | http://127.0.0.1:22379 |      false |
| b80e75d7d583ec99 | started | etcd-04 | http://127.0.0.1:42380 | http://127.0.0.1:42379 |      false |
| ddd67b312462fd7b | started | etcd-01 | http://127.0.0.1:12380 | http://127.0.0.1:12379 |      false |
+------------------+---------+---------+------------------------+------------------------+------------+
➜  ~ ll /Users/Jormin/etcd/data
total 0
drwx------  3 jormin  staff    96B 12 12 12:48 etcd1
drwx------  3 jormin  staff    96B 12 12 12:48 etcd2
drwx------  3 jormin  staff    96B 12 12 12:48 etcd3
drwx------  3 jormin  staff    96B 12 12 12:50 etcd4
更新成员: member update

这里我们将节点 etcd-04 的 peer-urls 更新为一个不存在的地址:

➜  etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member update b80e75d7d583ec99 --peer-urls http://127.0.0.1:52380
Member b80e75d7d583ec99 updated in cluster 73841b4a9097c907
删除成员: member remove
➜  etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member remove b80e75d7d583ec99
Member b80e75d7d583ec99 removed from cluster 73841b4a9097c907
➜  etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member list
+------------------+---------+---------+------------------------+------------------------+------------+
|        ID        | STATUS  |  NAME   |       PEER ADDRS       |      CLIENT ADDRS      | IS LEARNER |
+------------------+---------+---------+------------------------+------------------------+------------+
|  628170c800dbcee | started | etcd-03 | http://127.0.0.1:32380 | http://127.0.0.1:32379 |      false |
| 5060a277d1ea5267 | started | etcd-04 | http://127.0.0.1:42380 | http://127.0.0.1:42379 |      false |
| 9e737febb6b99eee | started | etcd-02 | http://127.0.0.1:22380 | http://127.0.0.1:22379 |      false |
| ddd67b312462fd7b | started | etcd-01 | http://127.0.0.1:12380 | http://127.0.0.1:12379 |      false |
+------------------+---------+---------+------------------------+------------------------+------------+
# 节点移除后数据并不会删除
➜  ~ ll /Users/Jormin/etcd/data
total 0
drwx------  3 jormin  staff    96B 12 12 12:48 etcd1
drwx------  3 jormin  staff    96B 12 12 12:48 etcd2
drwx------  3 jormin  staff    96B 12 12 12:48 etcd3
drwx------  3 jormin  staff    96B 12 12 12:50 etcd4
将学习者提升为投票成员: member promote

添加成员时,如果使用了 –learner,则新添加的成员为学习者,使用该命令则可以让该成员提升为投票成员。

# 新增加一个学习者
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member add etcd-04 --peer-urls=http://127.0.0.1:42380 --learner
Member 325b6b014ba294df added to cluster 73841b4a9097c907

ETCD_NAME="etcd-04"
ETCD_INITIAL_CLUSTER="etcd-03=http://127.0.0.1:32380,etcd-04=http://127.0.0.1:42380,etcd-02=http://127.0.0.1:22380,etcd-01=http://127.0.0.1:12380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://127.0.0.1:42380"
ETCD_INITIAL_CLUSTER_STATE="existing"

# 启动该节点
➜  ~ nohup etcd --data-dir=/Users/Jormin/etcd/data/etcd4 --name etcd-04 \
        --initial-advertise-peer-urls http://127.0.0.1:42380 --listen-peer-urls http://127.0.0.1:42380 \
        --advertise-client-urls http://127.0.0.1:42379 --listen-client-urls http://127.0.0.1:42379 \
        --initial-cluster etcd-01=http://127.0.0.1:12380,etcd-04=http://127.0.0.1:42380,etcd-02=http://127.0.0.1:22380,etcd-03=http://127.0.0.1:32380 \
        --initial-cluster-state existing \
        --initial-cluster-token etcd-cluster \
> /tmp/etcd4.log 2>&1 &
[1] 15949

# 查看成员列表,可以看到 etcd-04 是 learner
➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member list
+------------------+---------+---------+------------------------+------------------------+------------+
|        ID        | STATUS  |  NAME   |       PEER ADDRS       |      CLIENT ADDRS      | IS LEARNER |
+------------------+---------+---------+------------------------+------------------------+------------+
|  628170c800dbcee | started | etcd-03 | http://127.0.0.1:32380 | http://127.0.0.1:32379 |      false |
| 325b6b014ba294df | started | etcd-04 | http://127.0.0.1:42380 | http://127.0.0.1:42379 |       true |
| 9e737febb6b99eee | started | etcd-02 | http://127.0.0.1:22380 | http://127.0.0.1:22379 |      false |
| ddd67b312462fd7b | started | etcd-01 | http://127.0.0.1:12380 | http://127.0.0.1:12379 |      false |
+------------------+---------+---------+------------------------+------------------------+------------+

# 提升 etcd-04
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member promote 325b6b014ba294df --user=root:111111
Member 325b6b014ba294df promoted in cluster 73841b4a9097c907

# 重新查看成员列表,可以看到 etcd-04 已经不是 learner
➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 member list
+------------------+---------+---------+------------------------+------------------------+------------+
|        ID        | STATUS  |  NAME   |       PEER ADDRS       |      CLIENT ADDRS      | IS LEARNER |
+------------------+---------+---------+------------------------+------------------------+------------+
|  628170c800dbcee | started | etcd-03 | http://127.0.0.1:32380 | http://127.0.0.1:32379 |      false |
| 325b6b014ba294df | started | etcd-04 | http://127.0.0.1:42380 | http://127.0.0.1:42379 |      false |
| 9e737febb6b99eee | started | etcd-02 | http://127.0.0.1:22380 | http://127.0.0.1:22379 |      false |
| ddd67b312462fd7b | started | etcd-01 | http://127.0.0.1:12380 | http://127.0.0.1:12379 |      false |
+------------------+---------+---------+------------------------+------------------------+------------+
将另一成员提升为 leader: move-leader

使用 endpoint status 查看节点是否 leader,可以看出 127.0.0.1:12379 也就是 etcd-01 是leader:

➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 endpoint status
+-----------------+------------------+-----------+---------+-----------+------------+-----------+------------+--------------------+--------+
|    ENDPOINT     |        ID        |  VERSION  | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------+------------------+-----------+---------+-----------+------------+-----------+------------+--------------------+--------+
| 127.0.0.1:12379 | ddd67b312462fd7b | 3.6.0-pre |   25 kB |      true |      false |         2 |        130 |                130 |        |
| 127.0.0.1:22379 | 9e737febb6b99eee | 3.6.0-pre |   25 kB |     false |      false |         2 |        130 |                130 |        |
| 127.0.0.1:32379 |  628170c800dbcee | 3.6.0-pre |   25 kB |     false |      false |         2 |        130 |                130 |        |
+-----------------+------------------+-----------+---------+-----------+------------+-----------+------------+--------------------+--------+

etcd-02 提升为 leader,之后重新查看节点状态,可以看出 127.0.0.1:22379 也就是 etcd-02 成为了 leader:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 move-leader 9e737febb6b99eee
Leadership transferred from ddd67b312462fd7b to 9e737febb6b99eee
➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 endpoint status
+-----------------+------------------+-----------+---------+-----------+------------+-----------+------------+--------------------+--------+
|    ENDPOINT     |        ID        |  VERSION  | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------+------------------+-----------+---------+-----------+------------+-----------+------------+--------------------+--------+
| 127.0.0.1:12379 | ddd67b312462fd7b | 3.6.0-pre |   25 kB |     false |      false |         5 |        133 |                133 |        |
| 127.0.0.1:22379 | 9e737febb6b99eee | 3.6.0-pre |   25 kB |      true |      false |         5 |        133 |                133 |        |
| 127.0.0.1:32379 |  628170c800dbcee | 3.6.0-pre |   25 kB |     false |      false |         5 |        133 |                133 |        |
+-----------------+------------------+-----------+---------+-----------+------------+-----------+------------+--------------------+--------+

3.4 鉴权: auth

查看鉴权状态: auth status
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 auth status
Authentication Status: false
AuthRevision: 1
启用鉴权: auth enable

默认启动的 etcd 没有开启鉴权,并且也没有配置 root 账户及 root 角色,如果此时启动鉴权会报错:root 用户不存在,如下:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 auth enable
{"level":"warn","ts":"2021-12-12T13:59:09.250+0800","logger":"etcd-client","caller":"v3/retry_interceptor.go:63","msg":"retrying of unary invoker failed","target":"etcd-endpoints://0xc00000a1e0/#initially=[127.0.0.1:12379;127.0.0.1:22379;127.0.0.1:32379]","attempt":0,"error":"rpc error: code = FailedPrecondition desc = etcdserver: root user does not exist"}
Error: etcdserver: root user does not exist

需要添加下 root 用户并配置 root 角色再重新开启 auth,如下:

# 创建 root 用户,密码为 111111
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user add root:111111
User root created
# 为 root 用户授予 root 角色
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user grant-role root root
Role root is granted to user root
# 启用鉴权
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 auth enable
Authentication Enabled

开启鉴权后部分操作需要带上 user 信息,格式为 –user :

关闭鉴权: auth disable

关闭鉴权的时候需要提供用户信息,否则会报错,如下:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 auth disable
{"level":"warn","ts":"2021-12-12T14:05:45.966+0800","logger":"etcd-client","caller":"v3/retry_interceptor.go:63","msg":"retrying of unary invoker failed","target":"etcd-endpoints://0xc00000a1e0/#initially=[127.0.0.1:12379;127.0.0.1:22379;127.0.0.1:32379]","attempt":0,"error":"rpc error: code = InvalidArgument desc = etcdserver: user name is empty"}
Error: etcdserver: user name is empty

正确关闭的命令如下:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 auth disable --user root:111111
Authentication Disabled

3.5 角色: role

如果开启了鉴权,角色相关的操作需要带上用户信息

查看角色列表: role list
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 role list --user root:111111
root
添加角色: role add

这里我们添加三个角色

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 role add role1
Role role1 created
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 role add role2
Role role2 created
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 role add role3
Role role3 created
查看角色信息: role get
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 role get root --user root:111111
Role root
KV Read:
	[, <open ended>
KV Write:
	[, <open ended>
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 role get role1 --user root:111111
Role role1
KV Read:
KV Write:
删除角色: role delete
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 role delete role3 --user root:111111
Role role3 deleted
角色赋予权限: role grant-permission

权限分为三种:

  • read
  • write
  • readwrite

赋权时可以指定前缀匹配,需要增加 –prefix 选项,默认为 false

# key a 赋予读的权限
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 role grant-permission role1 read a --user root:111111
Role role1 updated
# key b 赋予写的权限
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 role grant-permission role1 write b --user root:111111
Role role1 updated
# key c 赋予读写的权限
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 role grant-permission role1 readwrite c --user root:111111
Role role1 updated
# 创建用户 user1
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user add user1:111111 --user root:111111
User user1 created
# 为用户 user1 赋予 role1 角色
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user grant-role user1 role1 --user root:111111
Role role1 is granted to user user1

接下来使用用户 user1 分别测试 a、b、c 三个 key 的权限:

# key a,只有读的权限,没有写的权限
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 get a --user user1:111111
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 put a 1 --user user1:111111
{"level":"warn","ts":"2021-12-12T14:58:05.528+0800","logger":"etcd-client","caller":"v3/retry_interceptor.go:63","msg":"retrying of unary invoker failed","target":"etcd-endpoints://0xc00000a1e0/#initially=[127.0.0.1:12379;127.0.0.1:22379;127.0.0.1:32379]","attempt":0,"error":"rpc error: code = PermissionDenied desc = etcdserver: permission denied"}
Error: etcdserver: permission denied

# key b,只有写的权限,没有读的权限
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 get b --user user1:111111
{"level":"warn","ts":"2021-12-12T14:58:37.571+0800","logger":"etcd-client","caller":"v3/retry_interceptor.go:63","msg":"retrying of unary invoker failed","target":"etcd-endpoints://0xc00000a5a0/#initially=[127.0.0.1:12379;127.0.0.1:22379;127.0.0.1:32379]","attempt":0,"error":"rpc error: code = PermissionDenied desc = etcdserver: permission denied"}
Error: etcdserver: permission denied
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 put b 2 --user user1:111111
OK

# key c,既有读的权限,也有写的权限
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 put c 3 --user user1:111111
OK
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 get c --user user1:111111
c
3
撤回角色的权限: role revoke-permission
# 撤回 role1 对 c 的权限
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 role revoke-permission role1 c --user root:111111
Permission of key c is revoked from role role1

# 使用 user1 进行测试
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 get c --user user1:111111
{"level":"warn","ts":"2021-12-12T15:02:14.590+0800","logger":"etcd-client","caller":"v3/retry_interceptor.go:63","msg":"retrying of unary invoker failed","target":"etcd-endpoints://0xc00000a5a0/#initially=[127.0.0.1:12379;127.0.0.1:22379;127.0.0.1:32379]","attempt":0,"error":"rpc error: code = PermissionDenied desc = etcdserver: permission denied"}
Error: etcdserver: permission denied
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 put c 4 --user user1:111111
{"level":"warn","ts":"2021-12-12T15:02:23.007+0800","logger":"etcd-client","caller":"v3/retry_interceptor.go:63","msg":"retrying of unary invoker failed","target":"etcd-endpoints://0xc00000a1e0/#initially=[127.0.0.1:12379;127.0.0.1:22379;127.0.0.1:32379]","attempt":0,"error":"rpc error: code = PermissionDenied desc = etcdserver: permission denied"}
Error: etcdserver: permission denied

3.6 用户: user

如果开启了鉴权,角色相关的操作需要带上用户信息。

添加用户: user add

直接设置账号密码:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user add user2:111111 --user root:111111
User user2 created

从标准输入设置密码:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user add user3 --user root:111111
Password of user3:
Type password of user3 again for confirmation:
User user3 created

其它选项:

  • –new-user-password:从命令行标志提供密码
  • –no-password:创建没有密码的用户(仅限基于 CN 的身份验证)
用户列表: user list
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user list --user=root:111111
root
user1
user2
user3
用户信息: user get
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user get user1 --user=root:111111
User: user1
Roles: role1

打印角色授予的权限信息:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user get user1 --user=root:111111 --detail
User: user1

Role role1
KV Read:
	a
KV Write:
	b
删除用户: user delete
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user delete user3 --user=root:111111
User user3 deleted
修改密码: user passwd
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user passwd user2 --user=root:111111
Password of user2:
Type password of user2 again for confirmation:
Password updated
赋予角色: user grant-role
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user grant-role user2 role2 --user=root:111111
Role role2 is granted to user user2
撤销角色: user revoke-role
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 user revoke-role user2 role2 --user=root:111111
Role role2 is revoked from user user2

3.7 租约: lease

租约,是ETCD的重要特性,用于实现key定时删除功能。与Redis的定时删除功能基本一致。

每个 key 最多可以附加一个租约。当租约到期或被撤销时,该租约所附的所有 key 都将被删除。每个过期的密钥都会在事件历史记录中生成一个删除事件。

创建租约: lease grant

返回的 3cee7dacf8ccc410 即为创建的租约,ttl 为有效期。

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 lease grant 10
lease 3cee7dacf8ccc410 granted with TTL(10s)
租约列表: lease list
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 lease list
found 1 leases
3cee7dacf8ccc410
获取租约详细信息: lease timetolive
# 租约过期
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 lease timetolive 3cee7dacf8ccc410
lease 3cee7dacf8ccc410 already expired

# 新建一个 400s 的租约
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 lease grant 400
lease 7d7b7dacf8cd9410 granted with TTL(400s)

# 查看新建租约的信息,remaining 为剩余租约时间
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 lease timetolive 7d7b7dacf8cd9410
lease 7d7b7dacf8cd9410 granted with TTL(400s), remaining(273s)

# 创建一个使用该租约的 key
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111  put a 1 --lease 7d7b7dacf8cd9410
OK

# 查询租约的信息以及附加的 keys
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 lease timetolive 7d7b7dacf8cd9410 --keys
lease 7d7b7dacf8cd9410 granted with TTL(400s), remaining(177s), attached keys([a])
延长租约时间: lease keep-alive

该操作会不停的延长租约的时间,快到期就延长

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 lease keep-alive 7d7b7dacf8cd9410
lease 7d7b7dacf8cd9410 keepalived with TTL(400)
lease 7d7b7dacf8cd9410 keepalived with TTL(400)
lease 7d7b7dacf8cd9410 keepalived with TTL(400)

如果只想让租约延长一次的话需要加上 –once

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 lease keep-alive 7d7b7dacf8cd9410 --once
lease 7d7b7dacf8cd9410 keepalived with TTL(400)
撤销租约: lease revoke

如果启用了鉴权,该操作需要带上用户信息

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 lease revoke 7d7b7dacf8cd9410
lease 7d7b7dacf8cd9410 revoked

撤销后查看租约信息及对应的key信息,可以看到对应的 key 已经删除了

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 lease timetolive 7d7b7dacf8cd9410
lease 7d7b7dacf8cd9410 already expired
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get a
➜  ~

3.8 基础操作

设置: put
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 put foo bar
OK

–lease:使用一个10秒的租约设置 kv,并且在10秒后查看,可以看到已经获取不到值了

# 创建一个10秒的租约
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 lease grant 10
lease 1eee7dacf8ccd844 granted with TTL(10s)
# 使用租约设置 kv
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 put a 1 --lease 1eee7dacf8ccd844
OK
# 10秒后查看 key
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get a

–prev-kv:设置一个 key,之后修改值,并使用 –prev-kv 选项,可以看到第二次设置时打印了第一次设置的 kv

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 put a 1
OK
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 put a 2 --prev-kv
OK
a
1

除此之外可选的选项有:

--ignore-lease:更新一个key,使用当前的租约
--ignore-value:更新一个key,使用当前的值
读取: get
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get foo
foo
bar

可选的选项有:

--consistency="l":可线性化(l)或可序列化(s)
--count-only[=false]:仅仅获取数量,需要搭配 **--write-out=fields** 使用
--from-key[=false]:使用字节比较获取大于或等于给定 key 的 keys,不能与 **--prefix** 一起使用
--keys-only[=false]:仅仅获取 keys
--limit=0:展示结果的最大数量
--order="":结果排序,可选值有:ASCEND or DESCEND,默认 ASCEND(升序)
--prefix[=false]:使用前缀匹配
--print-value-only[=false]:输出格式为 **simple** 时,仅打印值
--rev=0:指定 kv 版本
--sort-by="":排序,可选的排序方式有:CREATE, KEY, MODIFY, VALUE, or VERSION

部分示例:

# --count-only 结合 --write-out=fields
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get foo --count-only --write-out=fields
"ClusterID" : 8323808018354915591
"MemberID" : 15985099378392235387
"Revision" : 14
"RaftTerm" : 6
"More" : false
"Count" : 1

# --prefix
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get foo --prefix
foo
bar
foo1
a
foo2
b

# --prefix 结合 --order
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get foo --prefix --order DESCEND
foo2
b
foo1
a
foo
bar

# --prefix 结合 --limit
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get foo --prefix --limit 1
foo
bar

# --prefix 结合 --sort-by
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get foo --prefix --sort-by="VALUE"
foo1
a
foo2
b
foo
bar

# --prefix 结合 --print-value-only
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get foo --prefix --print-value-only
bar
a
b

# --prefix 结合 --keys-only
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get foo --prefix --keys-only
foo

foo1

foo2

# --from-key
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get foo1 --from-key
foo1
a
foo2
b
删除: del
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 del a
1

可使用的选项有:

--from-key[=false]:使用字节比较删除大于或等于给定 key 的 keys,不能与 **--prefix** 一起使用
--prefix[=false]:根据前缀删除
--prev-kv[=false]:返回删除的键值对
监听: watch

可使用的选项有:

--interactive[=false]: 交互模式,启动后可以交互输入 watch 命令
--prefix[=false]:监听前缀
--prev-kv[=false]:事件发生前获取旧的键值对
--progress-notify[=false]:从服务器获取定期观看进度通知
--rev=0:从指定版本开始监听

使用 –prefix–prev-kv 开启监听,并启动另一个终端修改值,观察监听结果:

修改键值对:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 put foo new-1
OK
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 put foo1 new-2
OK
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 put foo3 new-3
OK

监听结果:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 watch foo --prefix --prev-kv
PUT
foo
1
foo
new-1
PUT
foo1
1
foo1
new-2
PUT
foo3
new-3

3.9 事务: txn

etcd 支持事务,以交互模式执行,先输入判断条件,之后输入条件判断为 true 时的执行命令以及为 false 时的执行命令,条件、成功执行命令、失败执行命令 都可以输入多条,回车分割,两个回车表示输入结束,输入下一项内容,如下:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 put test 1
OK
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 txn -i
compares:
value("test") = "1"

success requests (get, put, del):
put res true

failure requests (get, put, del):
put res false

SUCCESS

OK
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 get res
res
true

解释:

  1. 首先设置 test 的值为 1

  2. 交互式模式开启事务

  3. 输入条件

    需要注意条件中的 = 两边需要有空格,可以输入多条,我们测试只输入一条,输入两次回车表示开始输入结果为 true 时的执行命令

  4. 输入判断为 true 时的执行命令

    可以输入多条,我们测试只输入一条,输入两次回车表示开始输入结果为 false 时的执行命令

  5. 输入判断为 false 时的执行命令

    可以输入多条,我们测试只输入一条,输入两次回车表示输入结束

  6. 获取 res 的值,由于我们的输入的提交为 true,所以执行 success 请求中的命令,因而 res 的值为 true

3.10 分布式锁: lock

两个终端同时获取锁

终端1获取到锁:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 lock testlock
testlock/7d7b7dacf8cd94bd

终端2获取时会阻塞等待:

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 lock testlock


3.11 快照: snapshot

保存快照: snapshot save

快照必须请求到一个选定的节点,而不是多个,即 –endpoints= 只能设置一个

➜  ~ etcdctl --endpoints=127.0.0.1:12379 --user root:111111 snapshot save snapshot.db
{"level":"info","ts":1639314943.7754638,"caller":"snapshot/v3_snapshot.go:69","msg":"created temporary db file","path":"snapshot.db.part"}
{"level":"info","ts":1639314943.882068,"logger":"client","caller":"v3/maintenance.go:243","msg":"opened snapshot stream; downloading"}
{"level":"info","ts":1639314943.929734,"caller":"snapshot/v3_snapshot.go:77","msg":"fetching snapshot","endpoint":"127.0.0.1:12379"}
{"level":"info","ts":1639314943.929992,"logger":"client","caller":"v3/maintenance.go:306","msg":"completed snapshot read; closing"}
{"level":"info","ts":1639314943.949552,"caller":"snapshot/v3_snapshot.go:92","msg":"fetched snapshot","endpoint":"127.0.0.1:12379","size":"33 kB","took":"now","etcd-version":""}
{"level":"info","ts":1639314943.949843,"caller":"snapshot/v3_snapshot.go:102","msg":"saved","path":"snapshot.db"}
Snapshot saved at snapshot.db
➜  ~ ll
-rw-------   1 jormin  staff    32K 12 12 21:15 snapshot.db
查看快照文件的状态: snapshot status

改命令已废弃,新版使用 etcdutl snapshot status

➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379 --user root:111111 snapshot status snapshot.db
Deprecated: Use `etcdutl snapshot status` instead.

+----------+----------+------------+------------+---------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE | VERSION |
+----------+----------+------------+------------+---------+
| 66de14b5 |       47 |         63 |      33 kB |         |
+----------+----------+------------+------------+---------+
使用快照恢复etcd数据: snapshot restore

可以使用的选项有:

--data-dir="": 恢复到数据目录
--initial-advertise-peer-urls="http://localhost:2380": 使用 initial-advertise-peer-urls 恢复
--initial-cluster="default=http://localhost:2380": 使用 initial-cluster 恢复
--initial-cluster-token="etcd-cluster": 使用 cluster-token 恢复
--name="default": 使用 name 恢复
--skip-hash-check[=false]: 忽略 hash 检测,如果使用 --data-dir 的话需要配置此项
--wal-dir="": 恢复到 wal 目录

示例:

➜  ~ etcdctl --write-out=table --endpoints=127.0.0.1:12379 --user root:111111 snapshot restore snapshot.db --initial-cluster-token="etcd-cluster"
Deprecated: Use `etcdutl snapshot restore` instead.

2021-12-12T21:20:38+08:00	info	snapshot/v3_snapshot.go:259	restoring snapshot	{"path": "snapshot.db", "wal-dir": "default.etcd/member/wal", "data-dir": "default.etcd", "snap-dir": "default.etcd/member/snap", "stack": "go.etcd.io/etcd/etcdutl/v3/snapshot.(*v3Manager).Restore\n\t/Users/Jormin/go/src/github.com/etcd-io/etcd/etcdutl/snapshot/v3_snapshot.go:265\ngo.etcd.io/etcd/etcdutl/v3/etcdutl.SnapshotRestoreCommandFunc\n\t/Users/Jormin/go/src/github.com/etcd-io/etcd/etcdutl/etcdutl/snapshot_command.go:148\ngo.etcd.io/etcd/etcdctl/v3/ctlv3/command.snapshotRestoreCommandFunc\n\t/Users/Jormin/go/src/github.com/etcd-io/etcd/etcdctl/ctlv3/command/snapshot_command.go:134\ngithub.com/spf13/cobra.(*Command).execute\n\t/Users/Jormin/go/pkg/mod/github.com/spf13/[email protected]/command.go:856\ngithub.com/spf13/cobra.(*Command).ExecuteC\n\t/Users/Jormin/go/pkg/mod/github.com/spf13/[email protected]/command.go:960\ngithub.com/spf13/cobra.(*Command).Execute\n\t/Users/Jormin/go/pkg/mod/github.com/spf13/[email protected]/command.go:897\ngo.etcd.io/etcd/etcdctl/v3/ctlv3.Start\n\t/Users/Jormin/go/src/github.com/etcd-io/etcd/etcdctl/ctlv3/ctl.go:111\ngo.etcd.io/etcd/etcdctl/v3/ctlv3.MustStart\n\t/Users/Jormin/go/src/github.com/etcd-io/etcd/etcdctl/ctlv3/ctl.go:115\nmain.main\n\t/Users/Jormin/go/src/github.com/etcd-io/etcd/etcdctl/main.go:59\nruntime.main\n\t/usr/local/go/src/runtime/proc.go:225"}
2021-12-12T21:20:38+08:00	info	schema/membership.go:121	Trimming membership information from the backend...
2021-12-12T21:20:38+08:00	info	membership/cluster.go:390	added member	{"cluster-id": "cdf818194e3a8c32", "local-member-id": "0", "added-peer-id": "8e9e05c52164694d", "added-peer-peer-urls": ["http://localhost:2380"]}
2021-12-12T21:20:38+08:00	info	snapshot/v3_snapshot.go:280	restored snapshot	{"path": "snapshot.db", "wal-dir": "default.etcd/member/wal", "data-dir": "default.etcd", "snap-dir": "default.etcd/member/snap"}

3.12 警报: alarm

列出所有警报
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 alarm list
➜  ~
解除警报
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 alarm disarm
➜  ~

3.13 检查: check

检查内存使用情况: check datascale

检查在给定服务器端点上为不同工作负载保存数据的内存使用情况。

➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 check datascale
Start data scale check for work load [10000 key-value pairs, 1024 bytes per key-value, 50 concurrent clients].
 10000 / 10000 Boooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00% 4s
PASS: Approximate system memory used : 45.71 MB.
检查集群性能: check perf
➜  ~ etcdctl --endpoints=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 --user root:111111 check perf
 60 / 60 Boooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00% 1m0s
PASS: Throughput is 150 writes/s
PASS: Slowest request took 0.204026s
PASS: Stddev is 0.016203s
PASS

3.14 其余命令

compaction: 压缩 etcd 中的事件历史
completion: 生成完成脚本
defrag: 对具有给定端点的 etcd 成员的存储进行碎片整理
elect: 观察并参与leader选举
make-mirror: 在目标 etcd 集群上创建镜像
Etcd Etcd Etcdctl

Go使用etcdHelm搭建etcd集群