etcd使用简介
转载地址:一路博客(http://www.16boke.com) https://blog.csdn.net/wyc_cs/article/details/75003868
一、下载安装
etcd 基于 Go 语言实现,因此,用户可以从 项目主页下载源代码自行编译,也可以下载编译好的二进制文件,甚至直接使用制作好的 Docker 镜像文件来体验。
GitHub地址
linux:
ETCD_VER=v3.2.20
# choose either URL
GOOGLE_URL=https://storage.googleapis.com/etcd
GITHUB_URL=https://github.com/coreos/etcd/releases/download
DOWNLOAD_URL=${GOOGLE_URL}
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test
curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
# start a local etcd server
/tmp/etcd-download-test/etcd
# write,read to etcd
ETCDCTL_API=3 /tmp/etcd-download-test/etcdctl --endpoints=localhost:2379 put foo bar
ETCDCTL_API=3 /tmp/etcd-download-test/etcdctl --endpoints=localhost:2379 get foo
或者直接使用 wget
或curl
工具下载压缩包,解压使用。
curl -L https://github.com/coreos/etcd/releases/download/v3.2.20/etcd-v3.2.20-linux-amd64.tar.gz -o etcd-v3.2.20-linux-amd64.tar.gz
tar xf etcd-v3.2.20-linux-amd64.tar.gz
cd etcd-v3.2.20-linux-amd64
解压后,可以看到文件包括:
$ ls
Documentation README-etcdctl.md README.md READMEv2-etcdctl.md etcd etcdctl
其中 etcd
是服务主文件,etcdctl
是提供给用户的命令客户端,其他文件是支持文档。
下面将 etcd etcdctl 文件放到系统可执行目录(例如 /usr/local/bin/)。
$ cp etcd* /usr/local/bin/
默认 2379 端口处理客户端的请求,2380 端口用于集群各成员间的通信。
Docker:
etcd uses gcr.io/etcd-development/etcd
as a primary container registry, and quay.io/coreos/etcd
as secondary.
rm -rf /tmp/etcd-data.tmp && mkdir -p /tmp/etcd-data.tmp \
docker rmi gcr.io/etcd-development/etcd:v3.2.20 || true && \
docker run \
-p 2379:2379 \
-p 2380:2380 \
--mount type=bind,source=/tmp/etcd-data.tmp,destination=/etcd-data \
--name etcd-gcr-v3.2.20 \
gcr.io/etcd-development/etcd:v3.2.20 \
/usr/local/bin/etcd \
--name s1 \
--data-dir /etcd-data \
--listen-client-urls http://0.0.0.0:2379 \
--advertise-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380 \
--initial-advertise-peer-urls http://0.0.0.0:2380 \
--initial-cluster s1=http://0.0.0.0:2380 \
--initial-cluster-token tkn \
--initial-cluster-state new
docker exec etcd-gcr-v3.2.20 /bin/sh -c "/usr/local/bin/etcd --version"
docker exec etcd-gcr-v3.2.20 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl version"
docker exec etcd-gcr-v3.2.20 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl endpoint health"
docker exec etcd-gcr-v3.2.20 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put foo bar"
docker exec etcd-gcr-v3.2.20 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl get foo"
注意:etcd 官方标注 quay.io/coreos/etcd 即将废弃,启用新的 gcr.io/etcd-development/etcd 镜像,但后者由于网络原因,国内不能下载到该镜像
二、配置介绍
-
--name
:方便理解的节点名称,默认为 default ,在集群中应该保持唯一,可以使用 hostname -
--data-dir
:服务运行数据保存的路径,默认为 ${name}.etcd -
--snapshot-count
:指定有多少事务(transaction)被提交时,触发截取快照保存到磁盘 -
--heartbeat-interval
:leader 多久发送一次心跳到 followers。默认值是 100ms -
--eletion-timeout
:重新投票的超时时间,如果 follow 在该时间间隔没有收到心跳包,会触发重新投票,默认为 1000 ms -
--listen-peer-urls
:和同伴通信的地址,比如 http://ip:2380 ,如果有多个,使用逗号分隔。需要所有节点都能够访问, 所以不要使用 localhost! -
--listen-client-urls
:对外提供服务的地址:比如 http://ip:2379,http://127.0.0.1:2379 ,客户端会连接到这里和 etcd 交互 -
--advertise-client-urls
:对外公告的该节点客户端监听地址,这个值会告诉集群中其他节点 -
--initial-advertise-peer-urls
:该节点同伴监听地址,这个值会告诉集群中其他节点 -
--initial-cluster
:集群中所有节点的信息,格式为 :node1=http://ip1:2380,node2=http://ip2:2380,…
。注意:这里的 node1 是节点的--name
指定的名字;后面的 ip1:2380 是--initial-advertise-peer-urls
指定的值 -
--initial-cluster-state
:新建集群的时候,这个值为 new ;假如已经存在的集群,这个值为 existing -
--initial-cluster-token
:创建集群的 token,这个值每个集群保持唯一。这样的话,如果你要重新创建集群,即使配置和之前一样,也会再次生成新的集群和节点 uuid;否则会导致多个集群之间的冲突,造成未知的错误
所有以
--init
开头的配置都是在 bootstrap 集群的时候才会用到,后续节点的重启会被忽略。
三、etcd 基础知识
每个 etcd cluster 都是有若干个 member 组成的,每个 member 是一个独立运行的 etcd 实例,单台机器上可以运行多个 member。
在正常运行的状态下,集群中会有一个 leader
,其余的 member 都是 followers
。leader 向 followers 同步日志,保证数据在各个 member 都有副本。leader 还会定时向所有的 member 发送心跳报文,如果在规定的时间里 follower 没有收到心跳,就会重新进行选举。
客户端所有的请求都会先发送给 leader,leader 向所有的 followers 同步日志,等收到超过半数的确认后就把该日志存储到磁盘,并返回响应客户端。
每个 etcd 服务有三大主要部分组成:raft 实现
、WAL 日志存储
、数据的存储和索引
。WAL 会在本地磁盘(就是之前提到的 --data-dir
)上存储日志内容(wal file)和快照(snapshot)。
四、API 文档
etcd 对外通过 HTTP API 对外提供服务,这种方式方便测试(通过 curl 或者其他工具就能和 etcd 交互),也很容易集成到各种语言中(每个语言封装 HTTP API 实现自己的 client 就行)。
这个部分,我们就介绍 etcd 通过 HTTP API 提供了哪些功能,并使用 httpie (yum install httpie
)来交互(当然你也可以使用 curl 或者其他工具)。
4.1、获取 etcd 服务的版本信息
# http http://127.0.0.1:2379/version
HTTP/1.1 200 OK
Content-Length: 45
Content-Type: application/json
Date: Wed, 13 Jun 2018 12:35:08 GMT
{
"etcdcluster": "3.2.0",
"etcdserver": "3.2.20"
}
4.2、key 的增删查改
etcd 的数据按照树形的结构组织,类似于 linux 的文件系统,也有目录和文件的区别,不过一般被称为 nodes
。数据的 endpoint 都是以 /v2/keys
开头(v2 表示当前 API 的版本),比如 /v2/keys/names/cizixs
。
新增、查看、修改、删除、TTL、监听变化、设置目录的 TTL、比较更新的原子操作、比较删除的原子操作、操作目录、隐藏的节点、查看集群数据信息、成员管理等
新增:要创建一个值,只要使用 PUT
方法在对应的 url endpoint 设置就行。如果对应的 key 已经存在, PUT 也会对 key 进行更新。
# http PUT http://127.0.0.1:2379/v2/keys/message value=="hello, etcd"
HTTP/1.1 201 Created
Content-Length: 102
Content-Type: application/json
Date: Wed, 13 Jun 2018 12:40:50 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 11
X-Raft-Index: 16
X-Raft-Term: 5
{
"action": "set",
"node": {
"createdIndex": 11,
"key": "/message",
"modifiedIndex": 11,
"value": "hello, etcd"
}
}
上面这个命令通过 PUT 方法把 /message 设置为 hello, etcd 。返回的格式中,各个字段的意义是:
- action :请求出发的动作,这里因为是新建一个 key 并设置它的值,所以是 set
- node.key :key 的 HTTP 路径
- node.value :请求处理之后,key 的值
- node.createdIndex : createdIndex 是一个递增的值,每次有 key 被创建的时候会增加
- node.modifiedIndex :同上,只不过每次有 key 被修改的时候增加
除返回的 json 体外,上面的情况还包含了一些特殊的 HTTP 头部信息,这些信息说明了 etcd cluster 的一些情况。它们的具体含义如下:
- X-Etcd-Index :当前 etcd 集群的 index
- X-Raft-Index :raft 集群的 index
- X-Raft-Term :raft 集群的任期,每次有 leader 选举的时候,这个值就会增加
查看:查看信息比较简单,使用 GET
方法,url 指向要查看的值就行:
# http GET http://127.0.0.1:2379/v2/keys/message
HTTP/1.1 200 OK
Content-Length: 102
Content-Type: application/json
Date: Wed, 13 Jun 2018 12:44:31 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 11
X-Raft-Index: 16
X-Raft-Term: 5
{
"action": "get",
"node": {
"createdIndex": 11,
"key": "/message",
"modifiedIndex": 11,
"value": "hello, etcd"
}
}
修改:前面已经提过, PUT
也可用来更新 key 的值我们就来看看例子。
# http PUT http://127.0.0.1:2379/v2/keys/message value=="I'm changed"
HTTP/1.1 200 OK
Content-Length: 191
Content-Type: application/json
Date: Wed, 13 Jun 2018 12:46:25 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 12
X-Raft-Index: 17
X-Raft-Term: 5
{
"action": "set",
"node": {
"createdIndex": 12,
"key": "/message",
"modifiedIndex": 12,
"value": "I'm changed"
},
"prevNode": {
"createdIndex": 11,
"key": "/message",
"modifiedIndex": 11,
"value": "hello, etcd"
}
}
和第一次执行 PUT 命令不同的是,返回中多了一个字段 prevNode ,它保存着更新之前该 key 的信息。它的格式和 node 是一样的,如果之前没有这个信息,这个字段会被省略。
删除:删除 key 可以通过 DELETE
方法,,比如我们要删除上面创建的字段:
# http DELETE http://127.0.0.1:2379/v2/keys/message
HTTP/1.1 200 OK
Content-Length: 172
Content-Type: application/json
Date: Wed, 13 Jun 2018 12:47:47 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 13
X-Raft-Index: 18
X-Raft-Term: 5
{
"action": "delete",
"node": {
"createdIndex": 12,
"key": "/message",
"modifiedIndex": 13
},
"prevNode": {
"createdIndex": 12,
"key": "/message",
"modifiedIndex": 12,
"value": "I'm changed"
}
}
注意,这里的 action 是 delete ,并且 modifiedIndex 增加了,但是 createdIndex 没有变化,因为这是一个修改操作,不是新建操作。
TTL: etcd 中,key 可以有 TTL
属性,超过这个时间会被自动删除。我们来设置一个看看:
# http PUT http://127.0.0.1:2379/v2/keys/tempkey value=="Gone with wind" ttl==5
HTTP/1.1 201 Created
Content-Length: 156
Content-Type: application/json
Date: Wed, 13 Jun 2018 12:50:18 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 14
X-Raft-Index: 19
X-Raft-Term: 5
{
"action": "set",
"node": {
"createdIndex": 14,
"expiration": "2018-06-13T12:50:23.689455Z",
"key": "/tempkey",
"modifiedIndex": 14,
"ttl": 5,
"value": "Gone with wind"
}
}
除了一般 key 返回的信息之外,上面多了两个字段:
- expiration :代表 key 过期被删除的时间
- ttl :表示 key 还要多少秒可以存活(这个值是动态的,会根据你请求的时候和过期时间进行计算)
如果我们在 5s 之后再去请求查看该 key,会发现报错信息:
# http http://127.0.0.1:2379/v2/keys/tempkey
HTTP/1.1 404 Not Found
Content-Length: 74
Content-Type: application/json
Date: Wed, 13 Jun 2018 12:51:34 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 15
{
"cause": "/tempkey",
"errorCode": 100,
"index": 15,
"message": "Key not found"
}
http 返回为 404 ,并且返回体中给出了 errorCode 和错误信息。
TTL 也可通过 PUT 方法进行取消,只要设置空值 ttl= 就行,这样 key 就不会过期被删除。比如:
http PUT http://127.0.0.1:2379/v2/keys/foo value==bar ttl== prevExist==true
注意:需要设置 value==bar
,不然 key 会变成空值。
如果只是想更新 TTL,可以添加上 refresh==true
参数
监听变化: etcd 提供了监听的机制,可以让客户端使用 long pulling
监听某个 key,当发生变化的时候接接收通知因为 etcd 经常被用作服务发现,集群中的信息有更新的时候需要及时被检测,做出对应的处理。因此需要有监听机制,来告诉客户端特定 key 的变化情况。
监听动作只需要 GET
方法,添加上 wait=true
参数就行.使用 recursive=true
参数,也能监听某个目录。
# http POST http://127.0.0.1:2379/v2/keys/queue value==job1
HTTP/1.1 201 Created
Content-Length: 117
Content-Type: application/json
Date: Wed, 13 Jun 2018 12:58:01 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 16
X-Raft-Index: 31
X-Raft-Term: 5
{
"action": "create",
"node": {
"createdIndex": 16,
"key": "/queue/00000000000000000016",
"modifiedIndex": 16,
"value": "job1"
}
}
创建的 key 会使用 etcd index,只能保证递增,无法保证是连续的(因为两次创建之间可能会有其他发生)。然后用相同的命令创建多个值,在获取值的时候使用 sorted=true
参数就会返回已经排序的值:
# http http://127.0.0.1:2379/v2/keys/queue sorted==true
HTTP/1.1 200 OK
Content-Length: 189
Content-Type: application/json
Date: Wed, 13 Jun 2018 12:58:54 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 16
X-Raft-Index: 31
X-Raft-Term: 5
{
"action": "get",
"node": {
"createdIndex": 16,
"dir": true,
"key": "/queue",
"modifiedIndex": 16,
"nodes": [
{
"createdIndex": 16,
"key": "/queue/00000000000000000016",
"modifiedIndex": 16,
"value": "job1"
}
]
}
}
设置目录的 TTL: 和 key 类似,目录(dir)也可以有过期时间。设置的方法也一样,只不过多了 dir=true
参数来说明这是一个目录。
# http PUT http://127.0.0.1:2379/v2/keys/dir/n value==a
HTTP/1.1 201 Created
Content-Length: 90
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:01:39 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 17
X-Raft-Index: 32
X-Raft-Term: 5
{
"action": "set",
"node": {
"createdIndex": 17,
"key": "/dir/n",
"modifiedIndex": 17,
"value": "a"
}
}
# http PUT http://127.0.0.1:2379/v2/keys/dir dir==true ttl==5 prevExist==true
HTTP/1.1 200 OK
Content-Length: 218
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:02:46 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 18
X-Raft-Index: 33
X-Raft-Term: 5
{
"action": "update",
"node": {
"createdIndex": 17,
"dir": true,
"expiration": "2018-06-13T13:02:51.299127469Z",
"key": "/dir",
"modifiedIndex": 18,
"ttl": 5
},
"prevNode": {
"createdIndex": 17,
"dir": true,
"key": "/dir",
"modifiedIndex": 17
}
}
目录过期的时候会被自动删除,包括它里面所有的子目录和 key,所有监听这个目录中内容的客户端都会收到对应的事件。
# http get http://127.0.0.1:2379/v2/keys/dir
HTTP/1.1 404 Not Found
Content-Length: 70
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:03:37 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 19
{
"cause": "/dir",
"errorCode": 100,
"index": 19,
"message": "Key not found"
}
比较更新的原子操作: 在分布式环境中,我们需要解决多个客户端的竞争问题,etcd 提供了原子操作 CompareAndSwap(CAS)
,通过这个操作可以很容易实现分布式锁。
简单来说,这个命令只有在客户端提供的条件成立的情况下才会更新对应的值。目前支持的条件包括:
- preValue :检查 key 之前的值是否和客户端提供的一致
- prevIndex :检查 key 之前的 modifiedIndex 是否和客户端提供的一致
- prevExist :检查 key 是否已经存在。如果存在就执行更新操作,如果不存在,执行 create 操作
举个栗子,比如目前 /foo 的值为 bar ,要把它更新成 changed ,可以使用:
# http PUT http://127.0.0.1:2379/v2/keys/foo prevValue==bar value==changed
HTTP/1.1 200 OK
Content-Length: 182
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:07:01 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 21
X-Raft-Index: 47
X-Raft-Term: 5
{
"action": "compareAndSwap",
"node": {
"createdIndex": 20,
"key": "/foo",
"modifiedIndex": 21,
"value": "changed"
},
"prevNode": {
"createdIndex": 20,
"key": "/foo",
"modifiedIndex": 20,
"value": "bar"
}
}
如果提供的条件不对,会报 412 错误:
# http PUT http://127.0.0.1:2379/v2/keys/foo prevValue==bar value==changed
HTTP/1.1 412 Precondition Failed
Content-Length: 83
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:08:12 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 21
{
"cause": "[bar != changed]",
"errorCode": 101,
"index": 21,
"message": "Compare failed"
}
注意:匹配条件是 prevIndex=0 的话,也会通过检查。
这些条件也可以组合起来使用,只有当都满足的时候,才会执行对应的操作。
比较删除的原子操作: 和条件更新类似,etcd 也支持条件删除操作:只有在客户端提供的条件成立的情况下,才会执行删除操作。支持 prevValue
和 prevIndex
两种条件检查,没有 prevExist
,因为删除不存在的值本身就会报错。
我们来删除上面例子中更新的 /foo ,先看一下提供的条件不对的情况:
# http DELETE http://127.0.0.1:2379/v2/keys/foo prevValue==bar
HTTP/1.1 412 Precondition Failed
Content-Length: 83
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:11:05 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 21
{
"cause": "[bar != changed]",
"errorCode": 101,
"index": 21,
"message": "Compare failed"
}
如果提供的条件成立,对应的 key 就会被删除:
# http DELETE http://127.0.0.1:2379/v2/keys/foo prevValue==changed
HTTP/1.1 200 OK
Content-Length: 170
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:11:41 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 22
X-Raft-Index: 52
X-Raft-Term: 5
{
"action": "compareAndDelete",
"node": {
"createdIndex": 20,
"key": "/foo",
"modifiedIndex": 22
},
"prevNode": {
"createdIndex": 20,
"key": "/foo",
"modifiedIndex": 21,
"value": "changed"
}
}
操作目录: 在创建 key 的时候,如果它所在路径的目录不存在,会自动被创建,所以在多数情况下我们不需要关心目录的创建。目录的操作和 key 的操作基本一致,唯一的区别是需要加上 dir=true
参数指明操作的对象是目录。
比如,如果想要显示地创建目录,可以使用 PUT 方法,并设置 dir=true:
# http PUT http://127.0.0.1:2379/v2/keys/anotherdir dir==true
HTTP/1.1 201 Created
Content-Length: 94
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:13:28 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 23
X-Raft-Index: 53
X-Raft-Term: 5
{
"action": "set",
"node": {
"createdIndex": 23,
"dir": true,
"key": "/anotherdir",
"modifiedIndex": 23
}
}
创建目录的操作不能重复执行,再次执行上面的命令会报 HTTP 403 错误。
如果 GET 方法对应的 url 是目录的话,etcd 会列出该目录所有节点的信息(不需要指定 dir=true )。比如要列出根目录下所有的节点:
# http http://127.0.0.1:2379/v2/keys/
HTTP/1.1 200 OK
Content-Length: 324
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:14:19 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 23
X-Raft-Index: 53
X-Raft-Term: 5
{
"action": "get",
"node": {
"dir": true,
"nodes": [
{
"createdIndex": 7,
"key": "/testkey",
"modifiedIndex": 7,
"value": "hello world"
},
{
"createdIndex": 6,
"key": "/test",
"modifiedIndex": 6,
"value": "hehe"
},
{
"createdIndex": 16,
"dir": true,
"key": "/queue",
"modifiedIndex": 16
},
{
"createdIndex": 23,
"dir": true,
"key": "/anotherdir",
"modifiedIndex": 23
}
]
}
}
如果添加上 recursive=true
参数,就会递归地列出所有的值:
# http http://127.0.0.1:2379/v2/keys/\?recursive\=true
HTTP/1.1 200 OK
Content-Length: 424
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:15:08 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 23
X-Raft-Index: 53
X-Raft-Term: 5
{
"action": "get",
"node": {
"dir": true,
"nodes": [
{
"createdIndex": 7,
"key": "/testkey",
"modifiedIndex": 7,
"value": "hello world"
},
{
"createdIndex": 6,
"key": "/test",
"modifiedIndex": 6,
"value": "hehe"
},
{
"createdIndex": 16,
"dir": true,
"key": "/queue",
"modifiedIndex": 16,
"nodes": [
{
"createdIndex": 16,
"key": "/queue/00000000000000000016",
"modifiedIndex": 16,
"value": "job1"
}
]
},
{
"createdIndex": 23,
"dir": true,
"key": "/anotherdir",
"modifiedIndex": 23
}
]
}
}
和 linux 删除目录的设计一样,要区别空目录和非空目录。删除空目录很简单,使用 DELETE 方法,并添加上 dir=true 参数,类似于 rmdir ;而对于非空目录,需要添加上 recursive=true,类似于 rm -rf 。
# http DELETE http://127.0.0.1:2379/v2/keys/queue dir==true
HTTP/1.1 403 Forbidden
Content-Length: 78
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:16:33 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 23
{
"cause": "/queue",
"errorCode": 108,
"index": 23,
"message": "Directory not empty"
}
# http DELETE http://127.0.0.1:2379/v2/keys/queue dir==true recursive==true
HTTP/1.1 200 OK
Content-Length: 168
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:17:18 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
X-Etcd-Index: 24
X-Raft-Index: 55
X-Raft-Term: 5
{
"action": "delete",
"node": {
"createdIndex": 16,
"dir": true,
"key": "/queue",
"modifiedIndex": 24
},
"prevNode": {
"createdIndex": 16,
"dir": true,
"key": "/queue",
"modifiedIndex": 16
}
}
隐藏的节点: etcd 中节点也可以是默认隐藏的,类似于 linux 中以 . 开头的文件或者文件夹,以 _ 开头的节点也是默认隐藏的,不会在列出目录的时候显示。只有知道隐藏节点的完整路径,才能够访问它的信息。
查看集群数据信息: etcd 还保存了集群的数据信息,包括节点之间的网络信息,操作的统计信息。
- /v2/stats/leader 会返回集群中 leader 的信息,以及 followers 的基本信息
- /v2/stats/self 会返回当前节点的信息
- /v2/state/store :会返回各种命令的统计信息
成员管理: etcd 在 /v2/members 下保存着集群中各个成员的信息,
# http http://127.0.0.1:2379/v2/members
HTTP/1.1 200 OK
Content-Length: 133
Content-Type: application/json
Date: Wed, 13 Jun 2018 13:19:29 GMT
X-Etcd-Cluster-Id: cdf818194e3a8c32
{
"members": [
{
"clientURLs": [
"http://localhost:2379"
],
"id": "8e9e05c52164694d",
"name": "default",
"peerURLs": [
"http://localhost:2380"
]
}
]
}
可以通过 POST
方法添加成员:
curl http://10.0.0.10:2379/v2/members -XPOST \ -H "Content-Type: application/json" -d '{"peerURLs":["http://10.0.0.10:2380"]}'
也可以通过 DELETE
方法删除成员:
curl http://10.0.0.10:2379/v2/members/272e204152 -XDELETE
或者通过 PUT
更新成员的 peer url:
curl http://10.0.0.10:2379/v2/members/272e204152 -XPUT \ -H "Content-Type: application/json" -d '{"peerURLs":["http://10.0.0.10:2380"]}'
五、etcdctl 命令行工具
除了 HTTP API 外,etcd 还提供了 etcdctl
命令行工具和 etcd 服务交互。这个命令行用 go 语言编写,也是对 HTTP API 的封装,日常使用起来也更容易。
etcdctl 的安装就不说了,从官网下载二进制文件放到系统的 PATH 路径下就行了。
# 设置一个 key 的值
#./etcdctl set /message "hello, etcd"
hello, etcd
# 获取 key 的值
#./etcdctl get /message
hello, etcd
# 获取 key 的值,包含更详细的元数据
#./etcdctl -o extended get /message
Key: /message
Created-Index: 1073
Modified-Index: 1073
TTL: 0
Index: 1073
hello, etcd
# 获取不存在 key 的值,会报错
#./etcdctl get /notexist
Error: 100: Key not found (/notexist) [1048]
# 设置 key 的 ttl,过期后会被自动删除
#./etcdctl set /tempkey "gone with wind" --ttl 5
gone with wind
#./etcdctl get /tempkey
gone with wind
#./etcdctl get /tempkey
Error: 100: Key not found (/tempkey) [1050]
# 如果 key 的值是 "hello, etcd",就把它替换为 "goodbye, etcd"
#./etcdctl set --swap-with-value "hello, world" /message "goodbye, etcd"
Error: 101: Compare failed ([hello, world != hello, etcd]) [1050]
#./etcdctl set --swap-with-value "hello, etcd" /message "goodbye, etcd"
goodbye, etcd
# 仅当 key 不存在的时候创建
#./etcdctl mk /foo bar
bar
#./etcdctl mk /foo bar
Error: 105: Key already exists (/foo) [1052]
# 自动创建排序的 key
#./etcdctl mk --in-order /queue job1
job1
#./etcdctl mk --in-order /queue job2
job2
#./etcdctl ls --sort /queue
/queue/00000000000000001053
/queue/00000000000000001054
# 更新 key 的值或者 ttl,只有当 key 已经存在的时候才会生效,否则报错
#./etcdctl update /message "I'am changed"
I'am changed
#./etcdctl get /message
I'am changed
#./etcdctl update /notexist "I'am changed"
Error: 100: Key not found (/notexist) [1055]
#./etcdctl update --ttl 3 /message "I'am changed"
I'am changed
#./etcdctl get /message
Error: 100: Key not found (/message) [1057]
# 删除某个 key
#./etcdctl mk /foo bar
bar
#./etcdctl rm /foo
PrevNode.Value: bar
#./etcdctl get /foo
Error: 100: Key not found (/foo) [1062]
# 只有当 key 的值匹配的时候,才进行删除
#./etcdctl mk /foo bar
bar
#./etcdctl rm --with-value wrong /foo
Error: 101: Compare failed ([wrong != bar]) [1063]
#./etcdctl rm --with-value bar /foo
# 创建一个目录
#./etcdctl mkdir /dir
# 删除空目录
#./etcdctl mkdir /dir/subdir/
#./etcdctl rmdir /dir/subdir/
# 删除非空目录
#./etcdctl rmdir /dir
Error: 108: Directory not empty (/dir) [1071]
#./etcdctl rm --recursive /dir
# 列出目录的内容
#./etcdctl ls /
/queue
/anotherdir
/message
# 递归列出目录的内容
#./etcdctl ls --recursive /
/anotherdir
/message
/queue
/queue/00000000000000001053
/queue/00000000000000001054
# 监听某个 key,当 key 改变的时候会打印出变化
#./etcdctl watch /message
changed
# 监听某个目录,当目录中任何 node 改变的时候,都会打印出来
#./etcdctl watch --recursive /
[set] /message
changed
# 一直监听,除非 `CTL + C` 导致退出监听
#./etcdctl watch --forever /message
new value
chaned again
Wola
# 监听目录,并在发生变化的时候执行一个命令
#./etcdctl exec-watch --recursive / -- sh -c "echo change detected."
change detected.
change detected.