3. etcdctl 基础操作实战

3.etcdctl 基础操作实战

etcdctl 是 etcd 官方提供的命令行客户端工具,它是我们与 etcd 集群进行交互、执行日常管理和数据操作最直接、最常用的手段。就像我们使用 kubectl 操作 Kubernetes 一样,熟练掌握 etcdctl 是每一位 etcd 初学者的必经之路。本章将带你从零开始,掌握 etcdctl 的基础用法,完成数据的写入、读取和版本查询等核心操作。

etcdctl 命令行基础

在开始具体操作之前,我们首先需要了解 etcdctl 的基本结构和工作原理。它是一个基于 gRPC 协议与 etcd 服务器通信的客户端。为了确保我们使用的是最新的 v3 API,通常建议在使用前设置环境变量 ETCDCTL_API

环境变量设置

etcd v3 版本的 API 是目前的主流标准。虽然在较新的 etcd 版本中,etcdctl 默认已经指向 v3 API,但为了代码的健壮性和明确性,我们强烈建议在脚本或日常使用中显式声明:

export ETCDCTL_API=3

这条命令告诉 etcdctl 使用 v3 版本的 API 与 etcd 集群通信。如果不设置,某些旧版本的 etcdctl 可能会默认使用已废弃的 v2 API,导致命令不兼容。

基本命令结构

etcdctl 的命令遵循通用的格式:

etcdctl [全局选项] <命令> [命令选项] [参数]
  • 全局选项:作用于整个命令,例如 --endpoints 用于指定 etcd 服务的地址,--cacert--cert--key 用于 TLS 安全连接。
  • 命令:具体要执行的操作,如 putgetwatch 等。
  • 命令选项:针对特定命令的参数,例如 get 命令的 --prefix 选项。
  • 参数:操作所需的数据,如键名、值等。

一个典型的例子是连接到本地运行的 etcd 实例并执行操作:

etcdctl --endpoints=localhost:2379 put mykey "myvalue"

帮助文档

etcdctl 自带了非常完善的帮助系统。当你不确定某个命令如何使用时,可以随时通过以下方式获取帮助:

# 查看所有可用命令
etcdctl --help

# 查看特定命令的详细用法,例如 get 命令
etcdctl get --help

养成查阅帮助文档的习惯,能极大地提高我们的学习和工作效率。

写入第一个键值对

数据存储是 etcd 最核心的功能。我们使用 put 命令来写入数据。put 操作是原子的,意味着它要么完全成功,要么完全失败,不会出现部分写入的情况。

基本写入

让我们向 etcd 中写入一个简单的键值对。假设 etcd 服务运行在本地的默认端口 2379 上。

export ETCDCTL_API=3
etcdctl --endpoints=localhost:2379 put greeting "Hello, etcd!"

代码讲解:

  • export ETCDCTL_API=3:设置使用 v3 API。
  • --endpoints=localhost:2379:指定 etcd 服务的地址。如果是在集群环境下,这里可以指定多个地址,用逗号分隔。
  • put:写入命令。
  • greeting:这是我们的键(Key)。
  • "Hello, etcd!":这是对应的值(Value)。如果值中包含空格,建议使用双引号括起来。

执行成功后,etcdctl 会返回 OK,表示数据已经成功写入并被集群确认。

带选项的写入

put 命令也支持一些高级选项,例如 --prev-kv,它可以在写入新值之前返回键的旧值(如果存在)。

# 写入一个新值,并获取它的旧值
etcdctl --endpoints=localhost:2379 put greeting "Hello, World" --prev-kv

如果 greeting 键之前存在,这个命令会返回旧的值;如果不存在,则不返回任何值。

读取键值数据

写入数据后,下一步自然是学习如何读取它。etcdctl 提供了多种灵活的方式来读取数据。

读取单个键

使用 get 命令可以读取一个特定的键。

etcdctl --endpoints=localhost:2379 get greeting

输出结果:

greeting
Hello, etcd!

输出分为两行:第一行是键,第二行是值。

读取选项

get 命令有几个非常实用的选项:

  1. 只打印值 (--print-value-only):如果你只关心值,不关心键名。

    etcdctl --endpoints=localhost:2379 get greeting --print-value-only
    # 输出: Hello, etcd!
    
  2. 前缀读取 (--prefix):读取所有以指定前缀开头的键。这在管理具有层级结构的配置时非常有用。

    # 先写入几个相关的键
    etcdctl --endpoints=localhost:2379 put config/database/host "localhost"
    etcdctl --endpoints=localhost:2379 put config/database/port "5432"
    etcdctl --endpoints=localhost:2379 put config/cache/enable "true"
    
    # 使用前缀读取所有数据库配置
    etcdctl --endpoints=localhost:2379 get config/database --prefix
    

    输出结果:

    config/database/host
    localhost
    config/database/port
    5432
    
  3. 范围读取 ([key] [range_end]):读取一个区间内的所有键。这是一个半开区间 [key, range_end),即包含 key,但不包含 range_end

    # 读取从 config/database/host 到 config/database/port 之间的所有键
    # 注意:这会包含 config/database/host,但不包含 config/database/port
    etcdctl --endpoints=localhost:2379 get config/database/host config/database/port
    

    一个常见的技巧是,如果想读取所有以 config/database/ 开头的键,可以将 range_end 设置为 key 的下一个字节值。例如,config/database/ 的下一个字节是 config/database0

    etcdctl --endpoints=localhost:2379 get config/database/ config/database0
    

版本信息查询

在第二章中,我们介绍了 etcd 的 MVCC(多版本并发控制)模型。每个键值对都关联了版本信息,etcdctl 允许我们查询这些元数据,这对于理解 etcd 的内部状态和调试问题非常有帮助。

查询键的详细信息

使用 get 命令并指定输出格式为 json,可以查看一个键的完整元数据,包括创建版本(create_revision)、修改版本(mod_revision)和版本号(version)。

etcdctl --endpoints=localhost:2379 get greeting -w json

输出结果(格式化后):

{
  "header": {
    "cluster_id": "12585971608760269493",
    "member_id": "13847567121247652255",
    "revision": "6",
    "raft_term": "2"
  },
  "kvs": [
    {
      "key": "Z3JlZXRpbmc=",
      "create_revision": "2",
      "mod_revision": "6",
      "version": "3",
      "value": "SGVsbG8sIGV0Y2Qh"
    }
  ],
  "count": "1"
}

信息解读:

  • key: 键的 Base64 编码值。
  • create_revision: 该键被创建时的全局修订号(Revision)。
  • mod_revision: 该键最后一次被修改时的全局修订号。
  • version: 该键的版本号。每次修改都会使版本号加 1。如果键被删除后重新创建,版本号会从 1 重新开始。
  • revision: 当前响应的全局修订号。

查询集群状态

除了查询键的版本,我们还可以查询集群的全局状态,这在维护和监控时非常有用。

etcdctl --endpoints=localhost:2379 endpoint status

输出结果(表格格式):

+-----------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|    ENDPOINT     |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| localhost:2379  | 8211f1d0f64f3269 |  3.5.0  |   20 kB |      true |      false |         2 |          9 |                  9 |        |
+-----------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

这个命令返回了关于该 etcd 节点的大量信息,包括:

  • ID: 节点的唯一标识符。
  • VERSION: etcd 服务器的版本。
  • DB SIZE: 数据库当前占用的物理空间大小。
  • IS LEADER: 该节点是否是当前集群的 Leader。
  • RAFT TERM: 当前 Raft 任期号。
  • RAFT INDEX: 当前已提交的 Raft 日志索引。

简单键值操作实践

现在,让我们通过一个完整的实践流程,将前面学到的知识串联起来。我们将模拟一个简单的应用配置管理场景。

场景:管理应用的数据库配置

步骤 1: 写入配置

我们首先写入几个数据库连接相关的配置项。

# 设置环境变量
export ETCDCTL_API=3
ENDPOINTS="localhost:2379"

# 写入数据库主机和端口
etcdctl --endpoints=$ENDPOINTS put app/db/host "db.prod.example.com"
etcdctl --endpoints=$ENDPOINTS put app/db/port "3306"
etcdctl --endpoints=$ENDPOINTS put app/db/username "admin"
etcdctl --endpoints=$ENDPOINTS put app/db/password "super_secret_password"

步骤 2: 读取并验证配置

使用前缀读取来一次性获取所有数据库配置。

etcdctl --endpoints=$ENDPOINTS get app/db --prefix

步骤 3: 更新配置

假设数据库迁移到了新地址,我们需要更新 host 配置。同时,我们想看看更新前的值是什么。

etcdctl --endpoints=$ENDPOINTS put app/db/host "db.new.prod.example.com" --prev-kv

步骤 4: 模拟配置回滚

假设新数据库有问题,需要回滚到旧的配置。我们可以利用 --rev 选项来读取历史版本。

首先,我们需要知道要回滚到哪个修订号。我们可以查看 host 键的修改历史。虽然 etcdctl 本身没有直接的 "查看历史" 命令,但我们可以通过 watch 来记录,或者通过多次修改来模拟。

让我们先查看当前 host 键的详细信息,获取 mod_revision

etcdctl --endpoints=$ENDPOINTS get app/db/host -w json

假设输出显示 mod_revision6。那么在它之前的版本就是 5。我们可以读取修订号为 5 时的 host 值。

# 读取修订号为 5 时的 app/db/host 值
etcdctl --endpoints=$ENDPOINTS get app/db/host --rev=5

如果确认要回滚,我们可以再次执行 put 命令,将值写回。

步骤 5: 清理配置

当应用下线后,我们需要删除这些配置。

# 删除单个配置项
etcdctl --endpoints=$ENDPOINTS del app/db/password

# 一次性删除所有 db 配置
etcdctl --endpoints=$ENDPOINTS del --prefix app/db

通过这个实践,我们完整地体验了 etcd 键值操作的生命周期:写入、读取、更新、历史查询和删除。这些是使用 etcdctl 进行日常工作的基础。

本章我们从 etcdctl 的基础讲起,逐步深入到写入、读取和版本查询等核心操作。我们学习了如何设置环境变量、使用不同的选项来精确控制数据的读写,并通过一个实践案例将这些操作串联起来。掌握了这些基础操作,你已经能够处理 etcd 中大部分的日常数据交互任务。

下一章,我们将深入探讨更复杂的键值操作,包括范围查询、批量操作以及事务处理,这些技能将使你能够更高效、更安全地管理 etcd 中的数据。