集群管理命令

etcdctl是一个**命令行的客户端**,它提供了一些命令,可以方便我们在对服务进行测试或者手动修改数据库内容。etcdctl命令基本用法如下所示:

    etcdctl [global options] command [command options] [args...]

具体的命令选项参数可以通过 etcdctl command --help来获取相关帮助

 环境变量

获得etcd数据库的访问url

    [root@k8s-master ~]# kubectl -n kube-system get pods etcd-k8s-master -o yaml  | grep -A10 "containers:" | grep "https://"
      containers:
      - command:
        - etcd
        - --advertise-client-urls=https://192.168.166.128:2379
        - --cert-file=/etc/kubernetes/pki/etcd/server.crt
        - --client-cert-auth=true
        - --data-dir=/var/lib/etcd
        - --experimental-initial-corrupt-check=true
        - --experimental-watch-progress-notify-interval=5s
        - --initial-advertise-peer-urls=https://192.168.166.128:2380
        - --initial-cluster=k8s-master=https://192.168.166.128:2380
    

如果遇到使用了TLS加密的集群,通常每条指令都需要指定证书路径和etcd节点地址,可以把相关命令行参数添加在环境变量中,在**~/.bashrc**添加以下内容:

    [root@tiaoban etcd]# cat ~/.bashrc
    HOST_1=https://192.168.166.128:2379
    ENDPOINTS=${HOST_1}
    # 如果需要使用原生命令,在命令开头加一个\ 例如:\etcdctl command
    alias etcdctl="etcdctl --cert /etc/kubernetes/pki/etcd/peer.crt --key /etc/kubernetes/pki/etcd/peer.key --endpoints=https://192.168.158.6:2379  --insecure-skip-tls-verify"
    [root@tiaoban etcd]# source ~/.bashrc

 查看etcd版本

    [root@tiaoban etcd]# etcdctl version
    etcdctl version: 3.4.23
    API version: 3.4

 查看etcd集群节点信息

    etcdctl  member list -w table
    +------------------+---------+------------+----------------------------+----------------------------+------------+
    |        ID        | STATUS  |    NAME    |         PEER ADDRS         |        CLIENT ADDRS        | IS LEARNER |
    +------------------+---------+------------+----------------------------+----------------------------+------------+
    | eba84a8571780cea | started | k8s-master | https://192.168.166.128:2380 | https://192.168.166.128:2379 |      false |
    +------------------+---------+------------+----------------------------+----------------------------+------------+
    

 查看集群健康状态

    etcdctl endpoint status -w table
    +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    |          ENDPOINT          |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
    +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    | https://192.168.166.128:2379 | eba84a8571780cea |  3.5.15 |  7.1 MB |      true |      false |         4 |      15658 |              15658 |        |
    +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    
   表格内容解析
    
        ENDPOINT:节点的地址,这里是 https://192.168.158.6:2379。这表示该节点的网络地址和端口。
        ID:节点的唯一标识符,这里是 6dc5c9ae772d8898。
        VERSION:节点的版本号,这里是 3.5.9。
        DB SIZE:数据库大小,这里是 9.1 MB。
        IS LEADER:是否为集群的领导者节点。true 表示该节点是领导者。
        IS LEARNER:是否为学习者节点。false 表示该节点不是学习者节点。
        RAFT TERM:Raft协议中的任期编号,这里是 4。Raft协议用于分布式系统的共识机制,任期编号用于区分不同的选举周期。
        RAFT INDEX:Raft协议中的日志索引,这里是 30622。它表示当前日志的最新位置。
        RAFT APPLIED INDEX:Raft协议中已应用的日志索引,这里是 30622。它表示已提交并应用到状态机的日志位置。
        ERRORS:错误信息,这里为空,表示没有错误

 查看告警事件

如果内部出现问题,会触发告警,可以通过命令查看告警引起原因,命令如下所示:

    etcdctl alarm <subcommand> [flags]

常用的子命令主要有两个:

    # 查看所有告警
    etcdctl alarm list
    # 解除所有告警
    etcdctl alarm disarm

 添加成员(单节点部署的etcd无法直接扩容)(不用做)

当集群部署完成后,后续可能需要进行节点扩缩容,就可以使用member命令管理节点。先查看当前集群信息

    [root@tiaoban etcd]# etcdctl endpoint status --cluster -w table
    +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    |          ENDPOINT          |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
    +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    | http://192.168.10.100:2379 | 2e0eda3ad6bc6e1e |  3.4.23 |   20 kB |      true |      false |         8 |         16 |                 16 |        |
    |  http://192.168.10.12:2379 | 5d2c1bd3b22f796f |  3.4.23 |   20 kB |     false |      false |         8 |         16 |                 16 |        |
    |  http://192.168.10.11:2379 | bc34c6bd673bdf9f |  3.4.23 |   20 kB |     false |      false |         8 |         16 |                 16 |        |
    +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

在启动新的etcd节点前,先向etcd集群声明添加节点的peer-urls和节点名称

    [root@tiaoban etcd]# etcdctl member add etcd4 --peer-urls=http://192.168.158.9:2380
    Member b112a60ec305e42a added to cluster cd30cff36981306b
    
    ETCD_NAME="etcd4"
    ETCD_INITIAL_CLUSTER="etcd1=http://192.168.10.100:2380,etcd3=http://192.168.10.12:2380,etcd4=http://192.168.10.100:12380,etcd2=http://192.168.10.11:2380"
    ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.10.100:12380"
    ETCD_INITIAL_CLUSTER_STATE="existing"

接下来使用docker创建一个版本为3.4.23的etcd节点,运行在192.168.10.100上,使用host网络模式,endpoints地址为[http://192.168.10.100:12379](http://192.168.10.100:12379),节点名称为etcd4。

    [root@tiaoban etcd]# mkdir -p /opt/docker/etcd/{conf,data}
    [root@tiaoban etcd]# chown -R 1001:1001 /opt/docker/etcd/data/
    [root@tiaoban etcd]# cat /opt/docker/etcd/conf/etcd.conf 
    # 节点名称
    name: 'etcd4'
    # 指定节点的数据存储目录
    data-dir: '/data'
    # 监听客户端请求的地址列表
    listen-client-urls: "http://192.168.10.100:12379"
    # 监听URL,用于节点之间通信监听地址
    listen-peer-urls: "http://192.168.10.100:12380"
    # 对外公告的该节点客户端监听地址,这个值会告诉集群中其他节点
    advertise-client-urls: "http://192.168.10.100:12379"
    # 服务端之间通讯使用的地址列表,该节点同伴监听地址,这个值会告诉集群中其他节点
    initial-advertise-peer-urls: "http://192.168.10.100:12380"
    # etcd启动时,etcd集群的节点地址列表
    initial-cluster: "etcd1=http://192.168.10.100:2380,etcd3=http://192.168.10.12:2380,etcd2=http://192.168.10.11:2380,etcd4=http://192.168.10.100:12380"
    # etcd集群初始化的状态,new代表新建集群,existing表示加入现有集群
    initial-cluster-state: 'existing'
    [root@tiaoban etcd]# docker run --name=etcd4 --net=host -d -v /opt/docker/etcd/data:/data -v /opt/docker/etcd/conf:/conf bitnami/etcd:latest etcd --config-file /conf/etcd.conf
    a142f38c785f2b7c217fb15f01ac62addfeb22eeb44da00363b1f7b5ce398439

etcd4启动后,查看集群节点信息:

    [root@tiaoban etcd]# etcdctl endpoint status --cluster -w table
    +-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    |          ENDPOINT           |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
    +-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    |  http://192.168.10.100:2379 | 2e0eda3ad6bc6e1e |  3.4.23 |   20 kB |      true |      false |         6 |         11 |                 11 |        |
    |   http://192.168.10.12:2379 | 5d2c1bd3b22f796f |  3.4.23 |   20 kB |     false |      false |         6 |         11 |                 11 |        |
    | http://192.168.10.100:12379 | b112a60ec305e42a |  3.4.23 |   20 kB |     false |      false |         6 |         11 |                 11 |        |
    |   http://192.168.10.11:2379 | bc34c6bd673bdf9f |  3.4.23 |   20 kB |     false |      false |         6 |         11 |                 11 |        |
    +-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

#### 更新成员

当etcd节点故障,启动etcd时报错`**member count is unequal**`。如果有保留的数据目录下的文件时,可以通过使用 member update 命令,在保留 etcd 数据的情况下初始化集群数据,重新构建一个新的etcd集群节点。 模拟192.168.10.100:12380节点故障,但数据目录文件有备份,启动一个新的节点,地址为:192.168.10.100:22380

    # 停用旧节点
    [root@tiaoban etcd]# docker stop etcd4
    etcd4
    [root@tiaoban etcd]# docker rm etcd4
    etcd4
    
    # 更新节点地址
    [root@tiaoban etcd]# cat conf/etcd.conf 
    # 节点名称
    name: 'etcd4'
    # 指定节点的数据存储目录
    data-dir: '/data'
    # 监听客户端请求的地址列表
    listen-client-urls: "http://192.168.10.100:22379"
    # 监听URL,用于节点之间通信监听地址
    listen-peer-urls: "http://192.168.10.100:22380"
    # 对外公告的该节点客户端监听地址,这个值会告诉集群中其他节点
    advertise-client-urls: "http://192.168.10.100:22379"
    # 服务端之间通讯使用的地址列表,该节点同伴监听地址,这个值会告诉集群中其他节点
    initial-advertise-peer-urls: "http://192.168.10.100:22380"
    # etcd启动时,etcd集群的节点地址列表
    initial-cluster: "etcd1=http://192.168.10.100:2380,etcd2=http://192.168.10.11:2380,etcd3=http://192.168.10.12:2380,etcd4=http://192.168.10.100:22380"
    # etcd集群初始化的状态,new代表新建集群,existing表示加入现有集群
    initial-cluster-state: 'existing'
    
    # 启动新节点
    [root@tiaoban etcd]# docker run --name=etcd4 --net=host -d -v /opt/docker/etcd/data:/data -v /opt/docker/etcd/conf:/conf bitnami/etcd:3.4.23 etcd --config-file /conf/etcd.conf
    03c03ac7e6b50a8600cefe443ecafdb03f8f61f153b1a1138029c1726826d74e
    [root@tiaoban etcd]# docker ps
    CONTAINER ID   IMAGE                 COMMAND                   CREATED         STATUS         PORTS     NAMES
    03c03ac7e6b5   bitnami/etcd:3.4.23   "/opt/bitnami/script…"   3 seconds ago   Up 3 seconds             etcd4

执行更新member操作,指定新的节点地址。

    [root@tiaoban etcd]# etcdctl member update b112a60ec305e42a --peer-urls=http://192.168.10.100:22380
    Member b112a60ec305e42a updated in cluster cd30cff36981306b

查看集群节点信息,节点信息更新完成。

    [root@tiaoban etcd]# etcdctl endpoint status --cluster -w table
    +-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    |          ENDPOINT           |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
    +-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    |  http://192.168.10.100:2379 | 2e0eda3ad6bc6e1e |  3.4.23 |   20 kB |      true |      false |         6 |         14 |                 14 |        |
    |   http://192.168.10.12:2379 | 5d2c1bd3b22f796f |  3.4.23 |   20 kB |     false |      false |         6 |         14 |                 14 |        |
    | http://192.168.10.100:22379 | b112a60ec305e42a |  3.4.23 |   20 kB |     false |      false |         6 |         14 |                 14 |        |
    |   http://192.168.10.11:2379 | bc34c6bd673bdf9f |  3.4.23 |   20 kB |     false |      false |         6 |         14 |                 14 |        |
    +-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

#### 删除成员

主要用法如下所示:

    etcdctl member remove <memberID> [flags]

模拟192.168.10.100:22379节点下线操作

    [root@tiaoban etcd]# docker stop etcd4
    etcd4
    [root@tiaoban etcd]# docker rm etcd4
    etcd4
    [root@tiaoban etcd]# etcdctl member remove b112a60ec305e42a
    Member b112a60ec305e42a removed from cluster cd30cff36981306b
    [root@tiaoban etcd]# etcdctl endpoint status --cluster -w table
    +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    |          ENDPOINT          |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
    +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    | http://192.168.10.100:2379 | 2e0eda3ad6bc6e1e |  3.4.23 |   20 kB |      true |      false |         6 |         16 |                 16 |        |
    |  http://192.168.10.12:2379 | 5d2c1bd3b22f796f |  3.4.23 |   20 kB |     false |      false |         6 |         16 |                 16 |        |
    |  http://192.168.10.11:2379 | bc34c6bd673bdf9f |  3.4.23 |   20 kB |     false |      false |         6 |         16 |                 16 |        |
    +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

    以下是 `etcdctl` 命令和选项的中文翻译:
    
    命令:
    - `alarm disarm` 停止所有告警
    - `alarm list` 列出所有告警
    - `auth disable` 禁用身份验证
    - `auth enable` 启用身份验证
    - `auth status` 返回身份验证状态
    - `check datascale` 检查给定服务器端点上不同工作负载的数据存储内存使用情况
    - `check perf` 检查 etcd 集群的性能
    - `compaction` 压缩 etcd 中的事件历史记录
    - `defrag` 对具有给定端点的 etcd 成员进行存储碎片整理
    - `del` 删除指定的键或键范围 [key, range_end)
    - `elect` 观察并参与领导者选举
    - `endpoint hashkv` 打印 `--endpoints` 中每个端点的 KV 历史记录哈希
    - `endpoint health` 检查 `--endpoints` 标志中指定的端点的健康状态
    - `endpoint status` 打印 `--endpoints` 标志中指定的端点的状态
    - `get` 获取键或键范围
    - `help` 任何命令的帮助
    - `lease grant` 创建租约
    - `lease keep-alive` 保持租约活跃(续租)
    - `lease list` 列出所有活跃的租约
    - `lease revoke` 撤销租约
    - `lease timetolive` 获取租约信息
    - `lock` 获取命名锁
    - `make-mirror` 在目标 etcd 集群创建镜像
    - `member add` 向集群中添加成员
    - `member list` 列出集群中的所有成员
    - `member promote` 将集群中的非投票成员提升为投票成员
    - `member remove` 从集群中移除成员
    - `member update` 更新集群中的成员
    - `move-leader` 将领导权转移到另一个 etcd 集群成员
    - `put` 将给定的键放入存储中
    - `role add` 添加新角色
    - `role delete` 删除角色
    - `role get` 获取角色的详细信息
    - `role grant-permission` 给角色授予键
    - `role list` 列出所有角色
    - `role revoke-permission` 从角色中撤销键
    - `snapshot restore` 将 etcd 成员快照恢复到 etcd 目录
    - `snapshot save` 将 etcd 节点后端快照存储到给定文件
    - `snapshot status` [已弃用] 获取给定文件的后端快照状态
    - `txn` 事务处理所有请求
    - `user add` 添加新用户
    - `user delete` 删除用户
    - `user get` 获取用户的详细信息
    - `user grant-role` 给用户授予角色
    - `user list` 列出所有用户
    - `user passwd` 更改用户密码
    - `user revoke-role` 从用户中撤销角色
    - `version` 打印 etcdctl 的版本
    - `watch` 监视键或前缀上的事件流
    
    选项:
    - `--cacert=""` 使用此 CA 包验证启用了 TLS 的安全服务器的证书
    - `--cert=""` 使用此 TLS 证书文件标识安全客户端
    - `--command-timeout=5s` 短命令的超时时间(不包括拨号超时)
    - `--debug[=false]` 启用客户端调试日志记录
    - `--dial-timeout=2s` 客户端连接的拨号超时时间
    - `-d, --discovery-srv=""` 查询描述集群端点的 SRV 记录的域名
    - `--discovery-srv-name=""` 使用 DNS 发现时查询的服务名称
    - `--endpoints=[127.0.0.1:2379]` gRPC 端点
    - `-h, --help[=false]` etcdctl 的帮助
    - `--hex[=false]` 将字节字符串打印为十六进制编码的字符串
    - `--insecure-discovery[=true]` 接受描述集群端点的不安全 SRV 记录
    - `--insecure-skip-tls-verify[=false]` 跳过服务器证书验证(注意:此选项仅应在测试目的下启用)
    - `--insecure-transport[=true]` 禁用客户端连接的传输安全
    - `--keepalive-time=2s` 客户端连接的保活时间
    - `--keepalive-timeout=6s` 客户端连接的保活超时时间
    - `--key=""` 使用此 TLS 密钥文件标识安全客户端
    - `--password=""` 身份验证的密码(如果使用此选项,--user 选项不应包含密码)
    - `--user=""` 身份验证的用户名[:密码](如果未提供密码,将提示输入)
    - `-w, --write-out="simple"` 设置输出格式(字段,JSON,protobuf,简单,表格)
    

### 数据库操作命令

#### 增加(put)

添加一个键值,基本用法如下所示:

    etcdctl put [options] <key> <value> [flags]

常用参数如下所示:

| **参数** | **功能描述** |
| --- | --- |
| –prev-kv | 输出修改前的键值 |

注意事项:

* 其中value接受从stdin的输入内容
* 如果value是以横线-开始,将会被视为flag,如果不希望出现这种情况,可以使用两个横线代替–
* 若键已经存在,则进行更新并覆盖原有值,若不存在,则进行添加

示例

    [root@tiaoban etcd]# etcdctl put name cuiliang
    OK
    [root@tiaoban etcd]# etcdctl put location -- -beijing
    OK
    [root@tiaoban etcd]# etcdctl put foo1 bar1
    OK
    [root@tiaoban etcd]# etcdctl put foo2 bar2
    OK
    [root@tiaoban etcd]# etcdctl put foo3 bar3
    OK

#### 查询(get)

查询键值,基本用法如下所示:

    etcdctl get [options] <key> [range_end] [flags]

常用参数如下所示:

| **参数** | **功能描述** |
| --- | --- |
| –hex | 以十六进制形式输出 |
| –limit number | 设置输出结果的最大值 |
| –prefix | 根据prefix进行匹配key |
| –order | 对输出结果进行排序,ASCEND 或 DESCEND |
| –sort-by | 按给定字段排序,CREATE, KEY, MODIFY, VALUE, VERSION |
| –print-value-only | 仅输出value值 |
| –from-key | 按byte进行比较,获取大于等于指定key的结果 |
| –keys-only | 仅获取keys |

示例

    #基本命令:获取所有键值对
    etcdctl get / --prefix --keys-only
    # 只显示值,慎用
    etcdctl get / --prefix --print-value-only
    # 获取键值
    [root@tiaoban etcd]# etcdctl get name
    name
    cuiliang
    # 只获取值
    [root@tiaoban etcd]# etcdctl get location --print-value-only
    -beijing
    # 批量取从foo1到foo3的值,不包括foo3
    [root@tiaoban etcd]# etcdctl get foo foo3 --print-value-only
    bar1
    bar2
    # 批量获取前缀为foo的值
    [root@tiaoban etcd]# etcdctl get --prefix foo --print-value-only
    bar1
    bar2
    bar3
    # 批量获取符合前缀的前两个值
    [root@tiaoban etcd]# etcdctl get --prefix --limit=2 foo --print-value-only
    bar1
    bar2
    # 批量获取前缀为foo的值,并排序
    [root@tiaoban etcd]# etcdctl get --prefix foo --print-value-only --order DESCEND
    bar3
    bar2
    bar1

#### 删除(del)

删除键值,基本用法如下所示:

    etcdctl del [options] <key> [range_end] [flags]

常用参数如下所示:

| **参数** | **功能描述** |
| --- | --- |
| –prefix | 根据prefix进行匹配删除 |
| –prev-kv | 输出删除的键值 |
| –from-key | 按byte进行比较,删除大于等于指定key的结果 |

示例

    # 删除name的键值
    [root@tiaoban etcd]# etcdctl del name
    1
    # 删除从foo1到foo3且不包含foo3的键值
    [root@tiaoban etcd]# etcdctl del foo1 foo3
    2
    # 删除前缀为foo的所有键值
    [root@tiaoban etcd]# etcdctl del --prefix foo
    1

#### 更新(put覆盖)

若键已经存在,则进行更新并覆盖原有值,若不存在,则进行添加。

#### 查询键历史记录查询

etcd在每次键值变更时,都会记录变更信息,便于我们查看键变更记录

### 监听命令

watch是监听键或前缀发生改变的事件流, 主要用法如下所示:

    etcdctl watch [options] [key or prefix] [range_end] [--] [exec-command arg1 arg2 ...] [flags]

示例如下所示:

    # 对某个key监听操作,当key1发生改变时,会返回最新值
    etcdctl watch name
    # 监听key前缀
    etcdctl watch name --prefix
    # 监听到改变后执行相关操作
    etcdctl watch name --  etcdctl get age

etcdctl watch name – etcdctl put name Kevin,如果写成,会不会变成死循环,导致无限监视,尽量避免。示例

#### 监听单个键

    # 启动监听命令
    [root@tiaoban etcd]# etcdctl watch foo
    
    #另一个控制台执行新增命令
    [root@tiaoban ~]# etcdctl put foo bar
    OK
    
    # 观察控制台监听输出
    [root@tiaoban etcd]# etcdctl watch foo
    PUT
    foo
    bar
    
    #另一个控制台执行更新命令
    [root@tiaoban ~]# etcdctl put foo bar123
    OK
    
    # 观察控制台监听输出
    [root@tiaoban etcd]# etcdctl watch foo
    PUT
    foo
    bar
    PUT
    foo
    bar123
    
    #另一个控制台执行删除命令
    [root@tiaoban ~]# etcdctl del foo
    1
    
    # 观察控制台监听输出
    [root@tiaoban etcd]# etcdctl watch foo
    PUT
    foo
    bar
    PUT
    foo
    bar123
    DELETE
    foo

#### 同时监听多个键

    # 监听前缀为foo的键
    [root@tiaoban etcd]# etcdctl watch --prefix foo
    # 另一个控制台执行操作
    [root@tiaoban ~]# etcdctl put foo1 bar1
    OK
    [root@tiaoban ~]# etcdctl put foo2 bar2
    OK
    [root@tiaoban ~]# etcdctl del foo1
    1
    # 观察控制台输出
    [root@tiaoban etcd]# etcdctl watch --prefix foo
    PUT
    foo1
    bar1
    PUT
    foo2
    bar2
    DELETE
    foo1
    
    
    # 监听指定的多个键
    [root@tiaoban etcd]# etcdctl watch -i
    watch name
    watch location
    
    # 另一个控制台执行操作
    [root@tiaoban ~]# etcdctl put name cuiliang
    OK
    [root@tiaoban ~]# etcdctl del name
    1
    [root@tiaoban ~]# etcdctl put location beijing
    OK
    # 观察控制台输出
    [root@tiaoban etcd]# etcdctl watch -i
    watch name
    watch location
    PUT
    name
    cuiliang
    DELETE
    name
    
    PUT
    location
    beijing

### 租约命令

租约具有生命周期,需要为租约授予一个TTL(time to live),将租约绑定到一个key上,则key的生命周期与租约一致,可续租,可撤销租约,类似于redis为键设置过期时间。其主要用法如下所示:

    etcdctl lease <subcommand> [flags]

#### 添加租约

主要用法如下所示:

    etcdctl lease grant <ttl> [flags]

示例:

    # 设置60秒后过期时间
    [root@tiaoban etcd]# etcdctl lease grant 60
    lease 6e1e86f4c6512a2b granted with TTL(60s)
    # 把foo和租约绑定,设置成60秒后过期
    [root@tiaoban etcd]# etcdctl put --lease=6e1e86f4c6512a29 foo bar
    OK
    # 租约期内查询键值
    [root@tiaoban etcd]# etcdctl get foo
    foo
    bar
    # 租约期外查询键值
    [root@tiaoban etcd]# etcdctl get foo
    返回为空

#### 查看租约

查看租约信息,以便续租或查看租约是否仍然存在或已过期。查看租约详情主要用法如下所示:

    etcdctl lease timetolive <leaseID> [options] [flags]

示例:

    # 添加一个50秒的租约
    [root@tiaoban etcd]# etcdctl lease grant 50
    lease 6e1e86f4c6512a32 granted with TTL(50s)
    # 将name键绑定到6e1e86f4c6512a32租约上
    [root@tiaoban etcd]# etcdctl put --lease=6e1e86f4c6512a32 name cuiliang
    OK
    # 查看所有租约列表
    [root@tiaoban etcd]# etcdctl lease list
    found 1 leases
    6e1e86f4c6512a32
    # 查看租约详情,remaining(6s) 剩余有效时间6秒;--keys 获取租约绑定的 key
    [root@tiaoban etcd]# etcdctl lease timetolive --keys 6e1e86f4c6512a32
    lease 6e1e86f4c6512a32 granted with TTL(50s), remaining(6s), attached keys([name])

#### 租约续约

通过刷新 TTL 值来保持租约的有效,使其不会过期。 主要用法如下所示:

    etcdctl lease keep-alive [options] <leaseID> [flags]

示例如下所示:

    # 设置60秒后过期租约
    [root@tiaoban etcd]# etcdctl lease grant 60
    lease 6e1e86f4c6512a36 granted with TTL(60s)
    # 把name和租约绑定,设置成 60 秒后过期
    [root@tiaoban etcd]# etcdctl put --lease=6e1e86f4c6512a36 name cuiliang
    OK
    # 自动定时执行续约,续约成功后每次租约为60秒
    [root@tiaoban etcd]# etcdctl lease keep-alive 6e1e86f4c6512a36
    lease 6e1e86f4c6512a36 keepalived with TTL(60)
    lease 6e1e86f4c6512a36 keepalived with TTL(60)
    lease 6e1e86f4c6512a36 keepalived with TTL(60)
    ……

#### 删除租约

通过租约 ID 撤销租约,撤销租约将删除其所有绑定的 key。 主要用法如下所示:

    etcdctl lease revoke <leaseID> [flags]

示例如下所示:

    # 设置600秒后过期租约
    [root@tiaoban etcd]# etcdctl lease grant 600
    lease 6e1e86f4c6512a39 granted with TTL(600s)
    # 把foo和租约绑定,600秒后过期
    [root@tiaoban etcd]# etcdctl put --lease=6e1e86f4c6512a39 foo bar
    OK
    # 查看租约详情
    [root@tiaoban etcd]# etcdctl lease timetolive --keys 6e1e86f4c6512a39
    lease 6e1e86f4c6512a39 granted with TTL(600s), remaining(556s), attached keys([foo])
    # 删除租约
    [root@tiaoban etcd]# etcdctl lease revoke 6e1e86f4c6512a39
    lease 6e1e86f4c6512a39 revoked
    # 查看租约详情
    [root@tiaoban etcd]# etcdctl lease timetolive --keys 6e1e86f4c6512a39
    lease 6e1e86f4c6512a39 already expired
    # 获取键值
    [root@tiaoban etcd]# etcdctl get foo
    返回为空

#### 多key同一租约

一个租约支持绑定多个 key

    # 设置60秒后过期的租约
    [root@tiaoban etcd]# etcdctl lease grant 60
    lease 6e1e86f4c6512a3e granted with TTL(60s)
    # foo1与租约绑定
    [root@tiaoban etcd]# etcdctl put --lease=6e1e86f4c6512a3e foo1 bar1
    OK
    # foo2与租约绑定
    [root@tiaoban etcd]# etcdctl put --lease=6e1e86f4c6512a3e foo2 bar2
    OK
    # 查看租约详情
    [root@tiaoban etcd]# etcdctl lease timetolive --keys 6e1e86f4c6512a3e
    lease 6e1e86f4c6512a3e granted with TTL(60s), remaining(14s), attached keys([foo1 foo2])

租约过期后,所有 key 值都会被删除,因此:

* 当租约只绑定了一个 key 时,想删除这个 key,最好的办法是撤销它的租约,而不是直接删除这个 key。
* 当租约没有绑定key时,应主动把它撤销掉,单纯删除 key 后,续约操作持续进行,会造成内存泄露。

直接删除key演示:

    # 设置租约并绑定 zoo1
    [root@tiaoban etcd]# etcdctl lease grant 60
    lease 6e1e86f4c6512a43 granted with TTL(60s)
    [root@tiaoban etcd]# etcdctl --lease=6e1e86f4c6512a43 put zoo1 val1
    OK
    # 续约
    [root@tiaoban etcd]# etcdctl lease keep-alive 6e1e86f4c6512a43
    lease 6e1e86f4c6512a43 keepalived with TTL(60)
    
    # 此时在另一个控制台执行删除key操作:
    [root@tiaoban ~]# etcdctl del zoo1
    1
    # 单纯删除 key 后,续约操作持续进行,会造成内存泄露
    [root@tiaoban etcd]# etcdctl lease keep-alive 6e1e86f4c6512a43
    lease 6e1e86f4c6512a43 keepalived with TTL(60)
    lease 6e1e86f4c6512a43 keepalived with TTL(60)
    lease 6e1e86f4c6512a43 keepalived with TTL(60)
    ...

撤销key的租约演示:

    # 设置租约并绑定 zoo1
    [root@tiaoban etcd]# etcdctl lease grant 50
    lease 32698142c52a1717 granted with TTL(50s)
    [root@tiaoban etcd]# etcdctl --lease=32698142c52a1717 put zoo1 val1
    OK
    
    # 续约
    [root@tiaoban etcd]# etcdctl lease keep-alive 32698142c52a1717
    lease 32698142c52a1717 keepalived with TTL(50)
    lease 32698142c52a1717 keepalived with TTL(50)
    
    # 另一个控制台执行:etcdctl lease revoke 32698142c52a1717
    
    # 续约撤销并退出
    lease 32698142c52a1717 expired or revoked.
    [root@tiaoban etcd]# etcdctl get zoo1
    # 返回空

### 备份恢复命令

主要用于管理节点的快照,其主要用法如下所示:

    etcdctl snapshot <subcommand> [flags]

#### 生成快照

其主要用法如下所示:

    etcdctl snapshot save <filename> [flags]

示例如下所示:

    etcdctl snapshot save etcd-snapshot.db

#### 查看快照

其主要用法如下所示:

    etcdctl snapshot status <filename> [flags]

示例如下所示:

    etcdctl snapshot status etcd-snapshot.db -w table

#### 恢复快照

**首先把etcd数据库中的所有数据都必须删除;rm -rf /var/lib/etcd/***

其主要用法如下所示:

    etcdctl snapshot restore <filename> [options] [flags]

#### 备份恢复演示

* 新建一个名为name的key

    [root@tiaoban ~]# etcdctl put name cuiliang
    OK
    [root@tiaoban ~]# etcdctl get name
    name
    cuiliang
    [root@tiaoban ~]# etcdctl endpoint status -w table
    +---------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    |      ENDPOINT       |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
    +---------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    | 192.168.10.100:2379 | 2e0eda3ad6bc6e1e |  3.4.23 |   20 kB |      true |      false |         4 |         10 |                 10 |        |
    |  192.168.10.11:2379 | bc34c6bd673bdf9f |  3.4.23 |   20 kB |     false |      false |         4 |         10 |                 10 |        |
    |  192.168.10.12:2379 | 5d2c1bd3b22f796f |  3.4.23 |   20 kB |     false |      false |         4 |         10 |                 10 |        |
    +---------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

* 生成快照,创建名为snap.db的备份文件

    [root@k8s-work1 ~]# etcdctl snapshot save snap.db
    {"level":"info","ts":1679220752.5883558,"caller":"snapshot/v3_snapshot.go:119","msg":"created temporary db file","path":"snap.db.part"}
    {"level":"info","ts":"2023-03-19T18:12:32.592+0800","caller":"clientv3/maintenance.go:200","msg":"opened snapshot stream; downloading"}
    {"level":"info","ts":1679220752.5924425,"caller":"snapshot/v3_snapshot.go:127","msg":"fetching snapshot","endpoint":"127.0.0.1:2379"}
    {"level":"info","ts":"2023-03-19T18:12:32.595+0800","caller":"clientv3/maintenance.go:208","msg":"completed snapshot read; closing"}
    {"level":"info","ts":1679220752.597161,"caller":"snapshot/v3_snapshot.go:142","msg":"fetched snapshot","endpoint":"127.0.0.1:2379","size":"25 kB","took":0.008507131}
    {"level":"info","ts":1679220752.5973082,"caller":"snapshot/v3_snapshot.go:152","msg":"saved","path":"snap.db"}
    Snapshot saved at snap.db

* 查看备份文件详情

    [root@k8s-work1 ~]# ls -lh snap.db 
    -rw------- 1 root root 25K 3月  19 18:12 snap.db
    [root@k8s-work1 ~]# etcdctl snapshot status snap.db -w table
    +----------+----------+------------+------------+
    |   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
    +----------+----------+------------+------------+
    | 8f097221 |       39 |         47 |      25 kB |
    +----------+----------+------------+------------+

* 把快照文件传到其他节点

    [root@k8s-work1 ~]# scp snap.db 192.168.10.100:/root                                                                                                                      100%   24KB   6.9MB/s   00:00    
    [root@k8s-work1 ~]# scp snap.db 192.168.10.12:/root

* 停止所有节点的etcd服务,并删除数据目录

    [root@k8s-work1 ~]# rm -rf /var/lib/etcd/*
    [root@k8s-work1 ~]# etcdctl snapshot restore test.db
    [root@k8s-work1 ~]# cp -r default.etcd/member/ /var/lib/etcd/
    [root@k8s-work1 ~]# docker ps -a | grep etcd
    [root@k8s-work1 ~]# docker restart 550
    [root@k8s-work1 ~]# etcdctl get s
    s
    1
    # 其余两个节点相同操作

## 恢复案例

所有 Kubernetes 对象都存储在 etcd 上。定期备份 etcd 集群数据对于在灾难场景(例如丢失所有控制平面节点)下恢复 Kubernetes 集群非常重要。 快照文件包含所有 Kubernetes 状态和关键信息。

在一个基线上为etcd做快照能够实现etcd数据的备份。通过定期地为etcd节点后端数据库做快照,etcd就能从一个已知的良好状态的时间点进行恢复。运行在虚拟机的k8s集群,如果偶遇突然断电,就可能会部分文件有问题,导致etcd和apiserver起不来,这样整个集群都无法运行,因此在k8s的集群进行etcd备份十分重要,下面主要演示单master集群和多master集群2个方面。

### 单master集群

环境准备:kubeadm安装的一主三从

    root@k8s-01 ~]# kubectl get nodes
    NAME     STATUS   ROLES                  AGE   VERSION
    k8s-01   Ready    control-plane,master   22h   v1.22.3
    k8s-02   Ready    <none>                 22h   v1.22.3
    k8s-03   Ready    <none>                 22h   v1.22.3
    k8s-04   Ready    <none>                 22h   v1.22.3
    [root@k8s-01 ~]#

1. 先备份etcd数据

    [root@k8s-01 kubernetes]#  ETCDCTL_API=3 etcdctl snapshot save /opt/etcd-back/snap.db --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/peer.crt --key=/etc/kubernetes/pki/etcd/peer.key

2. 创建测试pod

    [root@k8s-01 ~]# kubectl get pods
    NAME                                      READY   STATUS    RESTARTS        AGE
    nfs-client-provisioner-69b76b8dc6-6l8xs   1/1     Running   7 (3h55m ago)   4h43m
    nginx-6799fc88d8-5rqg8                    1/1     Running   0               48s
    nginx-6799fc88d8-phvkx                    1/1     Running   0               48s
    nginx-6799fc88d8-rwjc6                    1/1     Running   0               48s
    [root@k8s-01 ~]#

3. 停止etcd和apiserver

> control-plane阶段用于为API Server、Controller Manager和Scheduler生成静态Pod配置清单,而etcd阶段则为本地etcd存储生成静态Pod配置清单,它们都会保存于/etc/kubernetes/manifests目录中。当前主机上的kubelet服务会监视该目录中的配置清单的创建、变动和删除等状态变动,并根据变动完成Pod创建、更新或删除操作。因此,这两个阶段创建生成的各配置清单将会启动Master组件的相关Pod

    [root@k8s-01 kubernetes]#  mv /etc/kubernetes/manifests/ /etc/kubernetes/manifests-backup/
    [root@k8s-01 kubernetes]# kubectl get pods -A
    The connection to the server 192.168.1.128:6443 was refused - did you specify the right host or port?
    [root@k8s-01 kubernetes]#

![image20220718230447435](https://i-blog.csdnimg.cn/blog_migrate/fc2568ebfdcaf56da885cfa77944b135.png)

4. 变更/var/lib/etcd

    [root@k8s-01 kubernetes]#  mv /var/lib/etcd /var/lib/etcd.bak
    [root@k8s-01 kubernetes]#

5. 恢复etcd数据

    [root@k8s-01 lib]# ETCDCTL_API=3 etcdctl --endpoints="https://127.0.0.1:2379"  --cert="/etc/kubernetes/pki/etcd/server.crt"  --key="/etc/kubernetes/pki/etcd/server.key"  --cacert="/etc/kubernetes/pki/etcd/ca.crt"   snapshot restore /opt/etcd-back/snap.db  --data-dir=/var/lib/etcd/

6. 启动etcd和apiserver,查看pods

    [root@k8s-01 lib]#  cd /etc/kubernetes/
    [root@k8s-01 kubernetes]#  mv manifests-backup manifests
    [root@k8s-01 kubernetes]#  kubectl get pods
    NAME                                      READY   STATUS    RESTARTS         AGE
    nfs-client-provisioner-69b76b8dc6-6l8xs   1/1     Running   12 (2m25s ago)   4h48m
    [root@k8s-01 ~]#  kubectl get pods -n kube-system
    NAME                                       READY   STATUS    RESTARTS       AGE
    calico-kube-controllers-65898446b5-t2mqq   1/1     Running   11 (16h ago)   21h
    calico-node-8md6b                          1/1     Running   0              21h
    calico-node-9457b                          1/1     Running   0              21h
    calico-node-nxs2w                          1/1     Running   0              21h
    calico-node-p7d52                          1/1     Running   0              21h
    coredns-7f6cbbb7b8-g84gl                   1/1     Running   0              22h
    coredns-7f6cbbb7b8-j9q4q                   1/1     Running   0              22h
    etcd-k8s-01                                1/1     Running   0              22h
    kube-apiserver-k8s-01                      1/1     Running   0              22h
    kube-controller-manager-k8s-01             1/1     Running   0              22h
    kube-proxy-49b8g                           1/1     Running   0              22h
    kube-proxy-8wh5l                           1/1     Running   0              22h
    kube-proxy-b6lqq                           1/1     Running   0              22h
    kube-proxy-tldpv                           1/1     Running   0              22h
    kube-scheduler-k8s-01                      1/1     Running   0              22h
    [root@k8s-01 ~]#

由于3个nginx是备份之后启动的,所以恢复后都不存在了。

### 多master集群

环境准备:kubeadm安装的二主二从

    [root@k8s-01 ~]# kubectl get nodes
    NAME     STATUS   ROLES                  AGE   VERSION
    k8s-01   Ready    control-plane,master   16h   v1.22.3
    k8s-02   Ready    control-plane,master   16h   v1.22.3
    k8s-03   Ready    <none>                 16h   v1.22.3
    k8s-04   Ready    <none>                 16h   v1.22.3
    [root@k8s-01 etcd-v3.5.4-linux-amd64]# ETCDCTL_API=3 etcdctl --endpoints=https://192.168.1.123:2379,https://192.168.1.124:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/peer.crt --key=/etc/kubernetes/pki/etcd/peer.key member list
    58915ab47aed1957, started, k8s-02, https://192.168.1.124:2380, https://192.168.1.124:2379, false
    c48307bcc0ac155e, started, k8s-01, https://192.168.1.123:2380, https://192.168.1.123:2379, false
    [root@k8s-01 etcd-v3.5.4-linux-amd64]#

1. 2台master都需要备份:

    [root@k8s-01 ~]# ETCDCTL_API=3 etcdctl --endpoints="https://127.0.0.1:2379"  --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/peer.crt --key=/etc/kubernetes/pki/etcd/peer.key  snapshot save /snap-$(date +%Y%m%d%H%M).db
    [root@k8s-02 ~]# ETCDCTL_API=3 etcdctl --endpoints="https://127.0.0.1:2379"  --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/peer.crt --key=/etc/kubernetes/pki/etcd/peer.key  snapshot save /snap-$(date +%Y%m%d%H%M).db

2. 创建3个测试pod

    [root@k8s-01 ~]# kubectl get pods
    NAME                      READY   STATUS    RESTARTS   AGE
    nginx-6799fc88d8-2x6gw    1/1     Running   0          4m22s
    nginx-6799fc88d8-82mjz    1/1     Running   0          4m22s
    nginx-6799fc88d8-sbb6n    1/1     Running   0          4m22s
    tomcat-7d987c7694-552v2   1/1     Running   0          2m8s
    [root@k8s-01 ~]#

3. 停掉Master机器的kube-apiserver和etcd

    [root@k8s-01 kubernetes]#  mv /etc/kubernetes/manifests/ /etc/kubernetes/manifests-backup/
    [root@k8s-02 kubernetes]#  mv /etc/kubernetes/manifests/ /etc/kubernetes/manifests-backup/

4. 变更/var/lib/etcd

    [root@k8s-01 kubernetes]#  mv /var/lib/etcd /var/lib/etcd.bak
    [root@k8s-02 kubernetes]#  mv /var/lib/etcd /var/lib/etcd.bak

5. 恢复etcd数据,etcd集群用同一份snapshot恢复;

    [root@k8s-01 /]# ETCDCTL_API=3 etcdctl snapshot restore /snap-202207182330.db     --endpoints=192.168.1.123:2379     --name=k8s-01    --cacert=/etc/kubernetes/pki/etcd/ca.crt  --cert=/etc/kubernetes/pki/etcd/peer.crt   --key=/etc/kubernetes/pki/etcd/peer.key       --initial-advertise-peer-urls=https://192.168.1.123:2380     --initial-cluster-token=etcd-cluster-0     --initial-cluster=k8s-01=https://192.168.1.123:2380,k8s-02=https://192.168.1.124:2380      --data-dir=/var/lib/etcd
    [root@k8s-01 /]# scp snap-202207182330.db root@192.168.1.124:/
    root@192.168.1.124's password:
    snap-202207182330.db                                                                                                  100% 4780KB  45.8MB/s   00:00
    [root@k8s-02 /]# ETCDCTL_API=3 etcdctl snapshot restore /snap-202207182330.db     --endpoints=192.168.1.124:2379     --name=k8s-02    --cacert=/etc/kubernetes/pki/etcd/ca.crt  --cert=/etc/kubernetes/pki/etcd/peer.crt   --key=/etc/kubernetes/pki/etcd/peer.key       --initial-advertise-peer-urls=https://192.168.1.124:2380     --initial-cluster-token=etcd-cluster-0     --initial-cluster=k8s-01=https://192.168.1.123:2380,k8s-02=https://192.168.1.124:2380      --data-dir=/var/lib/etcd

6.master节点上启动etcd和apiserver,查看pods

    [root@k8s-01 lib]#  cd /etc/kubernetes/
    [root@k8s-01 kubernetes]#  mv manifests-backup manifests
    [root@k8s-02 lib]#  cd /etc/kubernetes/
    [root@k8s-02 kubernetes]#  mv manifests-backup manifests
    [root@k8s-01 lib]# kubectl get pods
    ###发现无法看到后创建的pod信息
    [root@k8s-01 ~]# kubectl get pods -n kube-system
    NAME                                       READY   STATUS    RESTARTS       AGE
    calico-kube-controllers-65898446b5-drjjj   1/1     Running   10 (16h ago)   16h
    calico-node-9s7p2                          1/1     Running   0              16h
    calico-node-fnbj4                          1/1     Running   0              16h
    calico-node-nx6q6                          1/1     Running   0              16h
    calico-node-qcffj                          1/1     Running   0              16h
    coredns-7f6cbbb7b8-mn9hj                   1/1     Running   0              16h
    coredns-7f6cbbb7b8-nrwbf                   1/1     Running   0              16h
    etcd-k8s-01                                1/1     Running   1              16h
    etcd-k8s-02                                1/1     Running   0              16h
    kube-apiserver-k8s-01                      1/1     Running   2 (16h ago)    16h
    kube-apiserver-k8s-02                      1/1     Running   0              16h
    kube-controller-manager-k8s-01             1/1     Running   2              16h
    kube-controller-manager-k8s-02             1/1     Running   0              16h
    kube-proxy-d824j                           1/1     Running   0              16h
    kube-proxy-k5gw4                           1/1     Running   0              16h
    kube-proxy-mxmhp                           1/1     Running   0              16h
    kube-proxy-nvpf4                           1/1     Running   0              16h
    kube-scheduler-k8s-01                      1/1     Running   1              16h
    kube-scheduler-k8s-02                      1/1     Running   0              16h
    [root@k8s-01 ~]#

更多推荐