文档版本 v3.7-DRAFT 处于 草稿 状态。如需获取最新的稳定版文档,请参阅 v3.6。
与 etcd 交互
用户通常通过放入或获取键的值来与 etcd 交互。本节介绍如何使用 etcdctl(一个用于与 etcd 服务器交互的命令行工具)来完成这些操作。此处描述的概念同样适用于 gRPC API 或客户端库 API。
可通过 ETCDCTL_API 环境变量设置 etcdctl 与 etcd 通信时使用的 API 版本为 2 或 3。默认情况下,主分支上的 etcdctl(3.4 版本)使用 v3 API,而早期版本(3.3 及更早版本)默认使用 v2 API。
请注意,使用 v2 API 创建的任何键都无法通过 v3 API 查询。使用 v3 API 执行 etcdctl get 查询 v2 键时将正常退出(返回码为 0),但不会输出键数据,这是预期行为。
export ETCDCTL_API=3
查找版本
etcdctl 版本和服务器 API 版本有助于确定在 etcd 上执行各种操作时应使用的正确命令。
以下是查找版本的命令:
$ etcdctl version
etcdctl version: 3.1.0-alpha.0+git
API version: 3.1
写入键
应用程序通过向键写入数据将键存储到 etcd 集群中。每个存储的键都会通过 Raft 协议复制到所有 etcd 集群成员,以实现一致性和可靠性。
以下是将键 foo 的值设置为 bar 的命令:
$ etcdctl put foo bar
OK
还可以通过为键附加租约,使其在指定时间段内有效。
以下是将键 foo1 的值设置为 bar1 并持续 10 秒的命令:
$ etcdctl put foo1 bar1 --lease=1234abcd
OK
注意:上述命令中的租约 ID 1234abcd 指的是创建 10 秒租约时返回的 ID,该 ID 可随后附加到键上。
读取键
应用程序可以从 etcd 集群读取键的值。查询可以读取单个键,也可以读取一段范围内的键。
假设 etcd 集群中已存储了以下键:
foo = bar
foo1 = bar1
foo2 = bar2
foo3 = bar3
以下是读取键 foo 值的命令:
$ etcdctl get foo
foo
bar
以下是用十六进制格式读取键 foo 值的命令:
$ etcdctl get foo --hex
\x66\x6f\x6f # Key
\x62\x61\x72 # Value
以下是仅读取键 foo 值的命令:
$ etcdctl get foo --print-value-only
bar
以下是遍历从 foo 到 foo3 范围内键的命令:
$ etcdctl get foo foo3
foo
bar
foo1
bar1
foo2
bar2
注意,由于范围是半开区间 [foo, foo3),因此不包含 foo3。
以下命令用于遍历所有以 foo 为前缀的键:
$ etcdctl get --prefix foo
foo
bar
foo1
bar1
foo2
bar2
foo3
bar3
以下命令用于遍历所有以 foo 为前缀的键,并将结果数量限制为 2 个:
$ etcdctl get --prefix --limit=2 foo
foo
bar
foo1
bar1
读取键的过去版本
应用程序可能希望读取某个键的已过期版本。例如,应用程序可能希望通过访问键的早期版本来回滚到旧的配置。或者,应用程序可能希望通过访问键的历史记录,在多次请求中获得多个键的一致视图。由于对 etcd 集群键值存储的每次修改都会递增 etcd 集群的全局修订版本号,因此应用程序可以通过提供一个较早的 etcd 修订版本来读取已过期的键。
假设一个 etcd 集群已经包含以下键:
foo = bar # revision = 2
foo1 = bar1 # revision = 3
foo = bar_new # revision = 4
foo1 = bar1_new # revision = 5
以下是访问键历史版本的示例:
$ etcdctl get --prefix foo # access the most recent versions of keys
foo
bar_new
foo1
bar1_new
$ etcdctl get --prefix --rev=4 foo # access the versions of keys at revision 4
foo
bar_new
foo1
bar1
$ etcdctl get --prefix --rev=3 foo # access the versions of keys at revision 3
foo
bar
foo1
bar1
$ etcdctl get --prefix --rev=2 foo # access the versions of keys at revision 2
foo
bar
$ etcdctl get --prefix --rev=1 foo # access the versions of keys at revision 1
读取大于或等于指定键字节值的键
应用程序可能希望读取字节值大于或等于指定键的键。
假设一个 etcd 集群已经包含以下键:
a = 123
b = 456
z = 789
以下命令用于读取字节值大于或等于键 b 的所有键:
$ etcdctl get --from-key b
b
456
z
789
删除键
应用程序可以从 etcd 集群中删除单个键或一段范围的键。
假设一个 etcd 集群已经包含以下键:
foo = bar
foo1 = bar1
foo3 = bar3
zoo = val
zoo1 = val1
zoo2 = val2
a = 123
b = 456
z = 789
以下命令用于删除键 foo:
$ etcdctl del foo
1 # one key is deleted
以下命令用于删除从 foo 到 foo9 范围内的键:
$ etcdctl del foo foo9
2 # two keys are deleted
以下命令用于删除键 zoo 并返回被删除的键值对:
$ etcdctl del --prev-kv zoo
1 # one key is deleted
zoo # deleted key
val # the value of the deleted key
以下命令用于删除前缀为 zoo 的所有键:
$ etcdctl del --prefix zoo
2 # two keys are deleted
以下命令用于删除字节值大于或等于键 b 的所有键:
$ etcdctl del --from-key b
2 # two keys are deleted
监听键的变更
应用程序可以监视单个键或一段范围的键,以监控其任何更新。
以下命令用于监视键 foo:
$ etcdctl watch foo
# in another terminal: etcdctl put foo bar
PUT
foo
bar
以下命令用于以十六进制格式监视键 foo:
$ etcdctl watch foo --hex
# in another terminal: etcdctl put foo bar
PUT
\x66\x6f\x6f # Key
\x62\x61\x72 # Value
以下命令用于监视从 foo 到 foo9 范围内的键:
$ etcdctl watch foo foo9
# in another terminal: etcdctl put foo bar
PUT
foo
bar
# in another terminal: etcdctl put foo1 bar1
PUT
foo1
bar1
以下命令用于监视前缀为 foo 的所有键:
$ etcdctl watch --prefix foo
# in another terminal: etcdctl put foo bar
PUT
foo
bar
# in another terminal: etcdctl put fooz1 barz1
PUT
fooz1
barz1
以下命令用于监视多个键 foo 和 zoo:
$ etcdctl watch -i
$ watch foo
$ watch zoo
# in another terminal: etcdctl put foo bar
PUT
foo
bar
# in another terminal: etcdctl put zoo val
PUT
zoo
val
监听键的历史变更
应用程序可能希望监视 etcd 中键的历史变更。例如,应用程序可能希望接收某个键的所有修改记录;如果应用程序一直与 etcd 保持连接,则使用 watch 就足够了。然而,如果应用程序或 etcd 发生故障,则可能在故障期间发生变更,导致应用程序无法实时接收到更新。为了确保更新能够被传递,应用程序必须能够监视键的历史变更。为此,应用程序可以在监视时指定一个历史修订版本,就像读取键的历史版本一样。
假设我们已完成以下操作序列:
$ etcdctl put foo bar # revision = 2
OK
$ etcdctl put foo1 bar1 # revision = 3
OK
$ etcdctl put foo bar_new # revision = 4
OK
$ etcdctl put foo1 bar1_new # revision = 5
OK
以下是监视历史变更的示例:
# watch for changes on key `foo` since revision 2
$ etcdctl watch --rev=2 foo
PUT
foo
bar
PUT
foo
bar_new
# watch for changes on key `foo` since revision 3
$ etcdctl watch --rev=3 foo
PUT
foo
bar_new
以下是仅从最后一次历史变更开始监视的示例:
# watch for changes on key `foo` and return last revision value along with modified value
$ etcdctl watch --prev-kv foo
# in another terminal: etcdctl put foo bar_latest
PUT
foo # key
bar_new # last value of foo key before modification
foo # key
bar_latest # value of foo key after modification
监听进度
应用程序可能希望检查监视的进度,以确定监视流的新鲜程度。例如,如果监视用于更新缓存,则了解缓存是否相对于一次法定人数读取(quorum read)的修订版本过时可能是有用的。
可以在交互式监视会话中使用“progress”命令发送进度请求,要求 etcd 服务器在监视流中发送一条进度通知更新:
$ etcdctl watch -i
$ watch a
$ progress
progress notify: 1
# in another terminal: etcdctl put x 0
# in another terminal: etcdctl put y 1
$ progress
progress notify: 3
注意:进度通知响应中的修订号是来自与 watch 流连接的本地 etcd 服务器节点的修订号。如果该节点发生分区且不处于法定多数(quorum)中,则此进度通知的修订号可能低于对非分区 etcd 服务器节点执行法定多数读取所返回的修订号。
压缩修订版本
正如我们提到的,etcd 会保留修订版本,以便应用程序可以读取键的过去版本。但为了避免积累无限的历史数据,压缩旧的修订版本非常重要。压缩后,etcd 会移除历史修订,释放资源供将来使用。所有在压缩修订号之前被取代的数据都将不可用。
以下是用于压缩修订的命令:
$ etcdctl compact 5
compacted revision 5
# any revisions before the compacted one are not accessible
$ etcdctl get --rev=4 foo
Error: rpc error: code = 11 desc = etcdserver: mvcc: required revision has been compacted
注意:可以通过以 JSON 格式对任意键(无论是否存在)执行 get 命令来查看 etcd 服务器的当前修订号。以下示例展示了对 etcd 服务器中不存在的 mykey 执行该操作:
$ etcdctl get mykey -w=json
{"header":{"cluster_id":14841639068965178418,"member_id":10276657743932975437,"revision":15,"raft_term":4}}
授予租约
应用程序可以从 etcd 集群中申请租约(lease),并将键与租约绑定。当一个键关联到某个租约时,其生命周期将依赖于该租约的生命周期,而租约则由一个生存时间(TTL)控制。每个租约都有一个应用程序在申请时指定的最小 TTL 值。租约的实际 TTL 值至少等于最小 TTL,并由 etcd 集群决定。一旦租约的 TTL 超时,租约即告过期,所有与其关联的键都会被删除。
以下是申请租约的命令:
# grant a lease with 60 second TTL
$ etcdctl lease grant 60
lease 32695410dcc0ca06 granted with TTL(60s)
# attach key foo to lease 32695410dcc0ca06
$ etcdctl put --lease=32695410dcc0ca06 foo bar
OK
撤销租约
应用程序通过租约 ID 来撤销租约。撤销租约会删除其所有关联的键。
假设我们已完成以下操作序列:
$ etcdctl lease grant 60
lease 32695410dcc0ca06 granted with TTL(60s)
$ etcdctl put --lease=32695410dcc0ca06 foo bar
OK
以下是撤销同一租约的命令:
$ etcdctl lease revoke 32695410dcc0ca06
lease 32695410dcc0ca06 revoked
$ etcdctl get foo
# empty response since foo is deleted due to lease revocation
维持租约存活
应用程序可以通过刷新租约的 TTL 来保持租约有效,防止其过期。
假设我们已完成以下操作序列:
$ etcdctl lease grant 60
lease 32695410dcc0ca06 granted with TTL(60s)
以下是保持同一租约有效的命令:
$ etcdctl lease keep-alive 32695410dcc0ca06
lease 32695410dcc0ca06 keepalived with TTL(60)
lease 32695410dcc0ca06 keepalived with TTL(60)
lease 32695410dcc0ca06 keepalived with TTL(60)
...
获取租约信息
应用程序可能需要获取有关租约的信息,以便进行续订,或检查租约是否仍然存在或已经过期。应用程序也可能希望了解某个特定租约所绑定的键。
假设我们已完成以下操作序列:
# grant a lease with 500 second TTL
$ etcdctl lease grant 500
lease 694d5765fc71500b granted with TTL(500s)
# attach key zoo1 to lease 694d5765fc71500b
$ etcdctl put zoo1 val1 --lease=694d5765fc71500b
OK
# attach key zoo2 to lease 694d5765fc71500b
$ etcdctl put zoo2 val2 --lease=694d5765fc71500b
OK
以下是获取租约信息的命令:
$ etcdctl lease timetolive 694d5765fc71500b
lease 694d5765fc71500b granted with TTL(500s), remaining(258s)
以下是获取租约信息及其所绑定键的命令:
$ etcdctl lease timetolive --keys 694d5765fc71500b
lease 694d5765fc71500b granted with TTL(500s), remaining(132s), attached keys([zoo2 zoo1])
# if the lease has expired or does not exist it will give the below response:
Error: etcdserver: requested lease not found