与 etcd 交互

etcdctl:用于与 etcd 服务器交互的命令行工具

用户通常通过放入或获取键的值来与 etcd 交互。本节介绍如何使用 etcdctl(一个用于与 etcd 服务器交互的命令行工具)来完成这些操作。此处描述的概念同样适用于 gRPC API 或客户端库 API。

可通过 ETCDCTL_API 环境变量设置 etcdctl 与 etcd 通信时使用的 API 版本为 23。默认情况下,主分支上的 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

以下是遍历从 foofoo3 范围内键的命令:

$ 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

以下命令用于删除从 foofoo9 范围内的键:

$ 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

以下命令用于监视从 foofoo9 范围内的键:

$ 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

以下命令用于监视多个键 foozoo

$ 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

最后更新于 2025 年 6 月 3 日:递归地将 v3.6 的内容复制到 v3.7(a90b2a6)