Etcd 使用总结

1. 简介

Etcd API 特性:

  • 原子性:一个操作要么全部执行,要么全部不执行
  • 一致性:不论客户端请求的是哪个etcd服务器,它都能读取到相同的事件,而且这些事件的顺序也是保持一致的。
  • 隔离性:etcd保证可串行化隔离(serializable isolation),读操作永远不会看到任何中间数据
  • 持久性:任何完成的操作都是持久性的。读操作永远不会返回未持久化存储的数据

2. 集群状态

$ curl http://192.168.3.191:2379/version | python3 -m json.tool
{
    "etcdserver": "3.5.1",
    "etcdcluster": "3.5.0"
}

$ curl http://192.168.3.191:2379/health | python3 -m json.tool
{
    "health": "true",
    "reason": ""
}

$ etcdctl version
etcdctl version: 3.5.1
API version: 3.5

$ etcdctl endpoint status
$ etcdctl endpoint health

$ ETCDCTL_API=2 etcdctl  cluster-health
member 7fe4b7b1994a2a9f is healthy: got healthy result from http://192.168.3.191:2379
member 96c03e6d8f6451f3 is healthy: got healthy result from http://192.168.3.192:2379
member cac6c28b4a6bf042 is healthy: got healthy result from http://192.168.3.193:2379

3. Etcd API V2

3.1 KV

curl http://127.0.0.1:2379/v2/keys/age -X PUT -d value=12
curl http://127.0.0.1:2379/v2/keys/age
curl http://127.0.0.1:2379/v2/keys/age -X DELETE

# TTL
curl http://127.0.0.1:2379/v2/keys/age -X PUT -d value=10 -d ttl=5

# 集群全局信息
curl http://127.0.0.1:2379/v2/keys/age -I   
HTTP/1.1 200 OK
Access-Control-Allow-Headers: accept, content-type, authorization
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE
Access-Control-Allow-Origin: *
Content-Type: application/json
X-Etcd-Cluster-Id: be7c242d574aadc5
X-Etcd-Index: 11
X-Raft-Index: 16
X-Raft-Term: 2
Date: Wed, 12 Jan 2022 05:39:02 GMT
Content-Length: 89

# 动态刷新已存在的key
curl http://127.0.0.1:2379/v2/keys/age -X PUT -d ttl=5 -d refresh=true -d preExist=true

TTL 原理:

  • Etcd 在接收客户端PUT请求时,如果请求参数中包含TTL,会将其值加入当前系统时间,作为key的过期时间。
  • 当前节点将这个请求通过Raft协议RPC消息同步到其他节点。如果接收客户端是Leader,则直接同步;如果是Follower,需要先将其转发给Leader后再同步到其他节点,单该key的 “expiration” 以follower为准,可能存在与leader不一致的情况,所以在follower上,将设置了TTL的key加到一个有序的map中(ttlKeyHeap)
  • 在Leader中允许一个tick,每 500ms 触发一次带leader系统时间的同步消息(syncTime),follower收到该消息后,查询ttlKeyHeap,删除已过期的key

refresh=true: 动态刷新TTL,可解决Etcd节点之间时钟不同步,导致定义的TTL被早删或晚删问题。另外,指定该参数后,无法更新其value

3.2 Watch

watch 机制:Etcd 在客户端等待key的变化并接收通知。它是通过一个 long polling 来实现的。

watch目录时,指定参数:recursive=true

# 一次性watch
$ curl http://127.0.0.1:2379/v2/keys/age?wait=true
{"action":"set","node":{"key":"/age","value":"12","modifiedIndex":26,"createdIndex":26}}

# 带索引watch
$ curl 'http://127.0.0.1:2379/v2/keys/age?wait=true&waitIndex=27'

# 持久化watch
$ curl 'http://127.0.0.1:2379/v2/keys/age?wait=true&recursive=true&stream=true'

# watch被清除
curl 'http://127.0.0.1:2379/v2/keys/age?wait=true&waitIndex=0'

3.3 统计

# leader数据,注意该请求只能发往leader节点
$ curl http://127.0.0.1:2379/v2/stats/leader | python3 -m json.tool
{
    "leader": "7fe4b7b1994a2a9f",
    "followers": {
        "12971f2c752331ba": {
            "latency": {
                "current": 0.00286,
                "average": 0.004560272727272729,
                "standardDeviation": 0.0031605226500513463,
                "minimum": 0.00042,
                "maximum": 0.0314
            },
            "counts": {
                "fail": 1,
                "success": 242
            }
        },
        "96c03e6d8f6451f3": {
            "latency": {
                "current": 0.0036,
                "average": 0.004349815261044178,
                "standardDeviation": 0.0045908728796599,
                "minimum": 0.00038,
                "maximum": 0.04868
            },
            "counts": {
                "fail": 0,
                "success": 249
            }
        }
    }
}

# 查询leader
$ ETCDCTL_API=2 etcdctl --endpoints http://192.168.3.192:2379 member list
12971f2c752331ba: name=etcd-3 peerURLs=http://192.168.3.193:2380 clientURLs=http://192.168.3.193:2379,http://localhost:2379 isLeader=false
7fe4b7b1994a2a9f: name=etcd-1 peerURLs=http://192.168.3.191:2380 clientURLs=http://192.168.3.191:2379,http://localhost:2379 isLeader=true
96c03e6d8f6451f3: name=etcd-2 peerURLs=http://192.168.3.192:2380 clientURLs=http://192.168.3.192:2379,http://localhost:2379 isLeader=false
# 节点自身数据
$ curl http://192.168.3.192:2379/v2/stats/self | python3 -m json.tool
{
    "name": "etcd-2",
    "id": "96c03e6d8f6451f3",
    "state": "StateFollower",
    "startTime": "2022-01-12T13:12:54.345682074+08:00",
    "leaderInfo": {
        "leader": "7fe4b7b1994a2a9f",
        "uptime": "1h38m34.91545621s",
        "startTime": "2022-01-12T13:12:55.583282779+08:00"
    },
    "recvAppendRequestCnt": 249,
    "sendAppendRequestCnt": 0
}
# 其他统计
$ curl http://192.168.3.192:2379/v2/stats/store | python3 -m json.tool
{
    "getsSuccess": 3,
    "getsFail": 14,
    "setsSuccess": 24,
    "setsFail": 4,
    "deleteSuccess": 1,
    "deleteFail": 0,
    "updateSuccess": 0,
    "updateFail": 0,
    "createSuccess": 4,
    "createFail": 0,
    "compareAndSwapSuccess": 0,
    "compareAndSwapFail": 0,
    "compareAndDeleteSuccess": 0,
    "compareAndDeleteFail": 0,
    "expireCount": 6,
    "watchers": 0
}

3.4 成员维护

注意:已存在的节点重新加入集群,需要修改配置initial-cluster-state: 'existing'

# 获取所有成员
$ curl http://192.168.3.191:2379/v2/members | python3 -m json.tool
{
    "members": [
        {
            "id": "12971f2c752331ba",
            "name": "etcd-3",
            "peerURLs": [
                "http://192.168.3.193:2380"
            ],
            "clientURLs": [
                "http://192.168.3.193:2379",
                "http://localhost:2379"
            ]
        },
        {
            "id": "7fe4b7b1994a2a9f",
            "name": "etcd-1",
            "peerURLs": [
                "http://192.168.3.191:2380"
            ],
            "clientURLs": [
                "http://192.168.3.191:2379",
                "http://localhost:2379"
            ]
        },
        {
            "id": "96c03e6d8f6451f3",
            "name": "etcd-2",
            "peerURLs": [
                "http://192.168.3.192:2380"
            ],
            "clientURLs": [
                "http://192.168.3.192:2379",
                "http://localhost:2379"
            ]
        }
    ]
}
# 删除成员
$ curl http://192.168.3.191:2379/v2/members/12971f2c752331ba -X DELETE

# 新增成员
$ curl http://192.168.3.191:2379/v2/members -X POST -H "Content-Type:application/json" -d '{"peerURLs": ["http://192.168.3.193:2380"]}'
{"id":"87c40f906dbafc3f","name":"","peerURLs":["http://192.168.3.193:2380"],"clientURLs":[]}

# 修改成员
curl http://192.168.3.191:2379/v2/members/87c40f906dbafc3f -X PUT -H "Content-Type:application/json" -d '{"peerURLs": ["http://192.168.3.193:2381"]}'

4. Etcd API V3

接口文档:https://github.com/etcd-io/etcd/blob/main/Documentation/dev-guide/apispec/swagger/rpc.swagger.json

/v3/auth/authenticate
/v3/auth/disable
/v3/auth/enable
/v3/auth/role/add
/v3/auth/role/delete
/v3/auth/role/get
/v3/auth/role/grant
/v3/auth/role/list
/v3/auth/role/revoke
/v3/auth/status
/v3/auth/user/add
/v3/auth/user/changepw
/v3/auth/user/delete
/v3/auth/user/get
/v3/auth/user/grant
/v3/auth/user/list
/v3/auth/user/revoke
/v3/cluster/member/add
/v3/cluster/member/list
/v3/cluster/member/promote
/v3/cluster/member/remove
/v3/cluster/member/update
/v3/kv/compaction
/v3/kv/deleterange
/v3/kv/lease/leases
/v3/kv/lease/revoke
/v3/kv/lease/timetolive
/v3/kv/put
/v3/kv/range
/v3/kv/txn
/v3/lease/grant
/v3/lease/keepalive
/v3/lease/leases
/v3/lease/revoke
/v3/lease/timetolive
/v3/maintenance/alarm
/v3/maintenance/defragment
/v3/maintenance/downgrade
/v3/maintenance/hash
/v3/maintenance/snapshot
/v3/maintenance/status
/v3/maintenance/transfer-leadership
/v3/watch

4.1 KV

参数需要先base64转换:foo:Zm9v, bar:YmFy

curl -L http://localhost:2379/v3/kv/put \
  -X POST -d '{"key": "Zm9v", "value": "YmFy"}'
  
curl -L http://localhost:2379/v3/kv/range \
  -X POST -d '{"key": "Zm9v"}'
  
curl -L http://localhost:2379/v3/kv/range \
  -X POST -d '{"key": "Zm9v", "range_end": "Zm9w"}'

4.2 Watch

curl -N http://localhost:2379/v3/watch \
  -X POST -d '{"create_request": {"key":"Zm9v"} }'

4.3 Transactions

# target CREATE
curl -L http://localhost:2379/v3/kv/txn \
  -X POST \
  -d '{"compare":[{"target":"CREATE","key":"Zm9v","createRevision":"2"}],"success":[{"requestPut":{"key":"Zm9v","value":"YmFy"}}]}'
  
# target VERSION
curl -L http://localhost:2379/v3/kv/txn \
  -X POST \
  -d '{"compare":[{"version":"4","result":"EQUAL","target":"VERSION","key":"Zm9v"}],"success":[{"requestRange":{"key":"Zm9v"}}]}'

4.4 Auth

# create root user
curl -L http://localhost:2379/v3/auth/user/add \
  -X POST -d '{"name": "root", "password": "pass"}'

# create root role
curl -L http://localhost:2379/v3/auth/role/add \
  -X POST -d '{"name": "root"}'

# grant root role
curl -L http://localhost:2379/v3/auth/user/grant \
  -X POST -d '{"user": "root", "role": "root"}'

# enable auth
curl -L http://localhost:2379/v3/auth/enable -X POST -d '{}'

# get the auth token for the root user
curl -L http://localhost:2379/v3/auth/authenticate \
  -X POST -d '{"name": "root", "password": "pass"}'
# {"header":{"cluster_id":"6617056074685781600","member_id":"15300616325842721311","revision":"46","raft_term":"3"},"token":"PpNSrukaCCPpbzGT.30"}

# fetch a key using authentication credentials
curl -L http://localhost:2379/v3/kv/put \
  -H 'Authorization: PpNSrukaCCPpbzGT.30' \
  -X POST -d '{"key": "Zm9v", "value": "YmFy"}'

# disable auth
curl -L http://localhost:2379/v3/auth/disable \
  -H 'Authorization: BLAWaOJuPEiGzRft.36' \
  -X POST -d '{}'

5. etcdctl

API 版本选择:ETCDCTL_API=2/3, 默认版本为3

5.1 KV

# 写
$ etcdctl put /test/name jack
$ etcdctl put /test/age 12

# 读
$ etcdctl get /test/name
$ etcdctl get /test/name --print-value-only
$ etcdctl get /test --prefix --keys-only
$ etcdctl get / --prefix --limit=2

# 读取历史版本:revision, 需要支持才行,详见配置
$ etcdctl put /test/age 13
$ etcdctl put /test/age 14
$ etcdctl put /test/age 15
$ etcdctl put /test/age 16
$ etcdctl get /test/age -w json | python3 -m json.tool
{
    "header": {
        "cluster_id": 13725885541475069381,
        "member_id": 9215692710915746463,
        "revision": 12,
        "raft_term": 9
    },
    "kvs": [
        {
            "key": "L3Rlc3QvYWdl",
            "create_revision": 4,
            "mod_revision": 12,
            "version": 9,
            "value": "MTY="
        }
    ],
    "count": 1
}

$ etcdctl get /test/age --rev=8
/test/age
12

$ etcdctl get /test/age --rev=11
/test/age
15

$ etcdctl get /test/age --rev=12
/test/age
16

# compact up to revision 10
$ etcdctl compact 10
$ compacted revision 10

$ etcdctl get /test/age --rev=8
{"level":"warn","ts":"2022-01-13T09:48:59.105+0800","logger":"etcd-client","caller":"v3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"etcd-endpoints://0x4000408700/127.0.0.1:2379","attempt":0,"error":"rpc error: code = OutOfRange desc = etcdserver: mvcc: required revision has been compacted"}
Error: etcdserver: mvcc: required revision has been compacted

$ etcdctl get /test/age --rev=11
/test/age
15

$ etcdctl get /test/age --rev=12
/test/age
16

# 按key字段序读取
$ etcdctl put /abc/a 1
$ etcdctl put /abc/b 2
$ etcdctl put /abc/z 9

$ etcdctl get --from-key /abc/b
/abc/b
2
/abc/z
9
/test/age
16
/test/name
jack
age
12

$ etcdctl get --from-key /abc --keys-only
/abc/a
/abc/b
/abc/z
/test/age
/test/name
age

# 删
$ etcdctl del age name
$ etcdctl del --prev-kv /abc/z   # 删除并同时返回值
$ etcdctl del --prefix /abc

$ etcdctl put a 1
$ etcdctl put b 2
$ etcdctl put c 3
$ etcdctl del --from-key b       # 按字段序删除
$ etcdctl get --prefix ''  --keys-only
/test/age
/test/name
a

5.2 Watch

watch机制:一旦某个 Key 返生变化,客户端就能够感知到变化。除非watch命令捕获到退出信号量,否则会一直等待而不会退出

$ etcdctl watch /test/name
PUT
/test/name
joe
PUT
/test/name
tom
DELETE
/test/name

$ etcdctl watch --prefix /
PUT
/test/name
tom
PUT
/test/age
20

$ etcdctl watch -i
watch /test/age
PUT
/test/age
21

$ etcdctl watch --rev=10 /test/age
PUT
/test/age
14
PUT
/test/age
15
PUT
/test/age
16
PUT
/test/age
20
PUT
/test/age
21
PUT
/test/age
21

$ etcdctl watch --prev-kv /test/age
PUT
/test/age
21
/test/age
22

5.3 Lease

租约是 v3 API 的特性。客户端可以为 key 授予租约(lease)。当一个 key 绑定一个租约时,它的生命周期便会与该租约的 TTL(time-to-live)保持一致。每个租约都有一个由用户授予的 TTL 值。如果某个租约的 TTL 超时了,那么该租约就会过期而且上面绑定的所有 Key 也会被自动删除。

# 创建租约
$ etcdctl lease grant 600
lease 2a9f7e511bbf1043 granted with TTL(600s)

# 租约有效期
$ etcdctl lease timetolive 2a9f7e511bbf1043 
lease 2a9f7e511bbf1043 granted with TTL(600s), remaining(575s)

# 绑定key
$ etcdctl put --lease=2a9f7e511bbf1043 /test/name tom

# 租约绑定的keys
$ etcdctl lease timetolive --keys 2a9f7e511bbf1043
lease 2a9f7e511bbf1043 granted with TTL(600s), remaining(477s), attached keys([/test/name])

# 自动续租,每次续租都发生在TTL快要过期时
$ etcdctl lease keep-alive 2a9f7e511bbf1043

# 租约撤销后,它绑定的key也会被清除
$ etcdctl lease revoke 2a9f7e511bbf1043   

5.4 Cluster

$ etcdctl member list
7fe4b7b1994a2a9f, started, etcd-1, http://192.168.3.191:2380, http://192.168.3.191:2379,http://localhost:2379, false
96c03e6d8f6451f3, started, etcd-2, http://192.168.3.192:2380, http://192.168.3.192:2379,http://localhost:2379, false
e26dd4722a25bb82, unstarted, , http://192.168.3.193:2380, , false

$ etcdctl member remove e26dd4722a25bb82

# etcd-3上执行
$ systemctl stop etcd
$ rm -rf /var/lib/etcd
$ systemctl start etcd

$ etcdctl member add etcd-3 --peer-urls=http://192.168.3.193:2380

5.5 灾难恢复

5.5.1 备份

etcdctl snapshot save snapshot.db

5.5.2 恢复

# 检查数据文件
etcdutl snapshot status snapshot.db -w table
+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| 667a1338 |       35 |         46 |      25 kB |
+----------+----------+------------+------------+

# 恢复数据文件:注意--data-dir指定的目录必须为空
etcdutl snapshot restore snapshot.db --data-dir /var/lib/etcd/default.etcd --name etcd-1 --initial-advertise-peer-urls http://192.168.80.200:2380 --initial-cluster etcd-1=http://192.168.80.200:2380 --initial-cluster-token etcd-cluster

# 启动etcd
systemctl start etcd

# 检查数据
etcdctl get / --prefix
/test/age
22
/test/name
tom

5.6 历史版本

# 保留一小时历史版本
etcd --auto-compaction-mode=periodic --auto-compaction-retention=1

# 保留1000个版本,每5分钟减少1000个历史版本,例如当前版本为30000,那么最老的版本为29000
etcd --auto-compaction-mode=revision --auto-compaction-retention=1000

# 版本压缩,即删除小于等于它的历史版本
etcdctl compact 3
etcdctl get --rev=2 somekey  # 无法获取历史版本

5.7 碎片化

压缩历史版本之后,后台数据库将会存在内部的碎片。这些碎片无法被后台存储使用,却仍占据节点的存储空间。因此消除碎片化的过程就是释放这些存储空间。压缩旧的历史版本会对后台数据库打个 ”洞”,从而导致碎片的产生。这些碎片空间对 Etcd 是可用的,但对宿主机文件系统是不可用的。

etcdctl defrag
etcdctl defrag --cluster

5.8 存储配额

Etcd 的存储配额可保证集群操作的可靠性。如果没有存储配额,那么 Etcd 的性能就会因为存储空间的持续增长而严重下降,甚至有耗完集群磁盘空间导致不可预测集群行为的风险。一旦其中一个节点的后台数据库的存储空间超出了存储配额,Etcd 就会触发集群范围的告警,并将集群置于接受读 key 和删除 key 的维护模式。只有在释放足够的空间和消除后端数据库的碎片之后,清除存储配额告警,集群才能恢复正常操作。

$ etcd --quota-backend-bytes=$((16*1024*1024))   # 16M

# 压测
$ while true; do dd if=/dev/urandom bs=1024 count=1024 | etcdctl put key || break; done

# 状态
$ etcdctl endpoint status --write-out=table 
+----------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------------------------------+
|    ENDPOINT    |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX |             ERRORS             |
+----------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------------------------------+
| 127.0.0.1:2379 | d456b57b7265da1f |   3.5.1 |   17 MB |      true |      false |         3 |         19 |                 19 |  memberID:15300616325842721311 |
|                |                  |         |         |           |            |           |            |                    |                 alarm:NOSPACE  |
+----------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------------------------------+

# 告警
$ etcdctl alarm list
memberID:15300616325842721311 alarm:NOSPACE

# 最新的版本
$ etcdctl endpoint status -w json | python3 -m json.tool
[
    {
        "Endpoint": "127.0.0.1:2379",
        "Status": {
            "header": {
                "cluster_id": 6617056074685781600,
                "member_id": 15300616325842721311,
                "revision": 45,
                "raft_term": 3
            },
            "version": "3.5.1",
            "dbSize": 16830464,
            "leader": 15300616325842721311,
            "raftIndex": 21,
            "raftTerm": 3,
            "raftAppliedIndex": 21,
            "errors": [
                "memberID:15300616325842721311 alarm:NOSPACE "
            ],
            "dbSizeInUse": 11587584
        }
    }
]

# 清除碎片
$ etcdctl defrag

# 消除告警
$ etcdctl alarm disarm

# 测试写入是否正常
$ etcdctl put key newvalue

6. TLS

# restful api
curl --cacert ./ca.pem --cert ./etcd.pem --key ./etcd-key.pem -sL https://127.0.0.1:2379/version

$ echo -n '/registry' | base64
L3JlZ2lzdHJ5

$ echo -n '\0' | base64
XDA=

$ curl --cacert ./ca.pem --cert ./etcd.pem --key ./etcd-key.pem -sL https://127.0.0.1:2379/v3/kv/range \
-X POST -d '{"key": "L3JlZ2lzdHJ5", "range_end": "XDA=", "keys_only": true}'

# etcdctl
$ etcdctl --cacert=/etc/kubernetes/pki/ca.pem --cert=/etc/kubernetes/pki/etcd.pem --key=/etc/kubernetes/pki/etcd-key.pem get /registry --prefix --keys-only=true
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Etcd和Consul都是常用的注册中心之一。 Etcd是一个分布式的键值存储系统,它使用Raft一致性算法来确保数据的一致性和可靠性。它支持高可用性,可用于服务发现和配置共享。Etcd是由CoreOS开发和维护的,它使用Go语言编写,并且被广泛应用于容器化环境中。 Consul也是一个服务发现和配置共享的工具,它使用Gossip协议来实现节点之间的通信,并使用Raft算法来保证数据的一致性。Consul使用Go语言编写,具有天然的可移植性,并且支持多种操作系统和容器平台。它提供了可视化的Web界面和命令行工具,使得部署和管理变得更加便捷。 总结来说,Etcd和Consul都是用于服务发现和配置共享的工具,它们提供了可靠的分布式存储和一致性算法来确保数据的可靠性和一致性。它们的特点和使用方式有所不同,可以根据具体需求选择合适的注册中心。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [全方位对比 Zookeeper、Eureka、Nacos、Consul 和 Etcd 实现原理和选型](https://blog.csdn.net/qwer123451234123/article/details/124257451)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值