etcd

环境信息

  • etcd v3.5

etcd 官网安装指南
Github 下载链接

wget https://github.com/etcd-io/etcd/releases/download/v3.5.9/etcd-v3.5.9-linux-amd64.tar.gz

tar -xf etcd-v3.5.9-linux-amd64.tar.gz -C /usr/local/

ln -s /usr/local/etcd-v3.5.9-linux-amd64/etcd /usr/bin/
ln -s /usr/local/etcd-v3.5.9-linux-amd64/etcdctl /usr/bin/
ln -s /usr/local/etcd-v3.5.9-linux-amd64/etcdutl /usr/bin/

启动 etcd

# etcd
{"level":"warn","ts":"2023-10-05T02:16:52.853273Z","caller":"embed/config.go:673","msg":"Running http and grpc server on single port. This is not recommended for production."}
{"level":"info","ts":"2023-10-05T02:16:52.853914Z","caller":"etcdmain/etcd.go:73","msg":"Running: ","args":["etcd"]}
{"level":"warn","ts":"2023-10-05T02:16:52.853947Z","caller":"etcdmain/etcd.go:105","msg":"'data-dir' was empty; using default","data-dir":"default.etcd"}
{"level":"warn","ts":"2023-10-05T02:16:52.853994Z","caller":"embed/config.go:673","msg":"Running http and grpc server on single port. This is not recommended for production."}
{"level":"info","ts":"2023-10-05T02:16:52.854009Z","caller":"embed/etcd.go:127","msg":"configuring peer listeners","listen-peer-urls":["http://localhost:2380"]}
...

常用管理命令

etcd

查看版本信息

# etcd --version
etcd Version: 3.5.3
Git SHA: 0452feec7
Go Version: go1.16.15
Go OS/Arch: linux/amd64

创建集群

etcd 创建集群涉及参数说明

name 说明 命令行参数
name 每个集群成员的唯一名称 --name=etcd0
initial-advertise-peer-urls 群成员广播给集群其他成员(用于连接本节点)的 URL
默认为 http://IP:2380
--initial-advertise-peer-urls=http://10.0.0.10:2380
listen-peer-urls 在这些(一个或多个) URL 上监听其他集群成员的连接请求
通信包括了集群管理任务、数据同步和心跳检测等
http://10.0.0.10:2380,http://127.0.0.1:2380
listen-client-urls 该成员监听客户端连接的 URL。 默认端口 2379 --listen-client-urls=http://10.0.0.10:2379,http://127.0.0.1:2379
advertise-client-urls 该成员广播给客户端的 URL --advertise-client-urls=http://10.0.0.10:2379
initial-cluster 所有 etcd 成员的初始列表 --initial-cluster=etcd0=http://10.0.0.10:2380,etcd1=http://10.0.0.11:2380,etcd2=http://10.0.0.12:2380
data-dir etcd 数据的存储目录。 --data-dir=/var/lib/etcd
initial-cluster-token 初始集群的唯一标识符,用于区分不同的 etcd 集群 --initial-cluster-token=my-etcd-token
initial-cluster-state 初始集群状态,可以是 newexisting
通常在引导新集群时使用 new,而在添加或删除成员时使用 existing
--initial-cluster-state=new
quota-backend-bytes etcd 的后端数据库大小的硬限制,默认是 2GB --quota-backend-bytes=3000000000
cert-file
key-file
用于 HTTPS 的证书和私钥 --cert-file=/etc/kubernetes/pki/etcd/server.crt
--key-file=/etc/kubernetes/pki/etcd/server.key
trusted-ca-file 客户端和对等体的验证所需的 CA 证书 --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
client-cert-auth 启用客户端证书验证,默认为 false --client-cert-auth

listen-peer-urlsinitial-advertise-peer-urls 的区别说明:

  • listen-peer-urls

    这个参数指定了 etcd 成员应该在哪些地址和端口上监听来自其他 etcd 成员的请求(数据同步、领导选举、集群更改等相关)

    主要用途是定义哪个网络接口和端口号应该被 etcd 服务绑定,以便它可以接收来自其他成员的连接

  • initial-advertise-peer-urls

    这个参数告诉 etcd 该如何 通告自己 给集群中的其他成员。这是其他集群成员用来联系此 etcd 成员的地址。

    主要用途是 当新成员加入集群时,它需要通知其他成员自己的存在,以及如何与自己通信,因此它必须是个其他节点可达的 Endpoints,如 http://0.0.0.0:2380 就不行。

    listen-client-urlsadvertise-client-urls 的区别同理

示例环境说明

主机 IP 角色
etcd1 172.17.0.2/16 etcd node
etcd2 172.17.0.3/16 etcd node
etcd3 172.17.0.4/16 etcd node

分别在 3 个节点上执行以下 3 条命令,创建集群

etcd1 执行命令:

etcd --data-dir=data.etcd --name etcd1 \
--initial-advertise-peer-urls http://172.17.0.2:2380 --listen-peer-urls http://172.17.0.2:2380,http://127.0.0.1:2380 \
--advertise-client-urls http://172.17.0.2:2379 --listen-client-urls http://172.17.0.2:2379,http://127.0.0.1:2379 \
--initial-cluster etcd1=http://172.17.0.2:2380,etcd2=http://172.17.0.3:2380,etcd3=http://172.17.0.4:2380 \
--initial-cluster-state new --initial-cluster-token etcd-cluster

etcd2 执行命令:

etcd --data-dir=data.etcd --name etcd2 \
--initial-advertise-peer-urls http://172.17.0.3:2380 --listen-peer-urls http://172.17.0.3:2380,http://127.0.0.1:2380 \
--advertise-client-urls http://172.17.0.3:2379 --listen-client-urls http://172.17.0.3:2379,http://127.0.0.1:2379 \
--initial-cluster etcd1=http://172.17.0.2:2380,etcd2=http://172.17.0.3:2380,etcd3=http://172.17.0.4:2380 \
--initial-cluster-state new --initial-cluster-token etcd-cluster

etcd3 执行命令:

etcd --data-dir=data.etcd --name etcd3 \
--initial-advertise-peer-urls http://172.17.0.4:2380 --listen-peer-urls http://172.17.0.4:2380,http://127.0.0.1:2380 \
--advertise-client-urls http://172.17.0.4:2379 --listen-client-urls http://172.17.0.4:2379,http://127.0.0.1:2379 \
--initial-cluster etcd1=http://172.17.0.2:2380,etcd2=http://172.17.0.3:2380,etcd3=http://172.17.0.4:2380 \
--initial-cluster-state new --initial-cluster-token etcd-cluster

检查节点健康状态

# ENDPOINT=http://172.17.0.3:2380,http://172.17.0.2:2380,http://172.17.0.4:2380

# etcdctl endpoint status --endpoints=$ENDPOINT -w table
+------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| http://172.17.0.3:2380 | 660aa483274d103a | 3.5.9 | 20 kB | false | false | 2 | 9 | 9 | |
| http://172.17.0.2:2380 | 69015be41c714f32 | 3.5.9 | 20 kB | true | false | 2 | 9 | 9 | |
| http://172.17.0.4:2380 | ad0233873e2a0054 | 3.5.9 | 20 kB | false | false | 2 | 9 | 9 | |
+------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

# etcdctl endpoint health --endpoints=$ENDPOINT -w table
+------------------------+--------+------------+-------+
| ENDPOINT | HEALTH | TOOK | ERROR |
+------------------------+--------+------------+-------+
| http://172.17.0.2:2380 | true | 2.44553ms | |
| http://172.17.0.3:2380 | true | 2.804559ms | |
| http://172.17.0.4:2380 | true | 2.580515ms | |
+------------------------+--------+------------+-------+

etcdctl

查看版本信息

# etcdctl version
etcdctl version: 3.5.3
API version: 3.5

etcd 集群管理

查看 etcd 集群成员列表

Kubernetes 中查看 etcd 集群成员列表使用如下命令

# kubectl exec -n kube-system -it etcd-k8s-master1 -- sh -c "ETCDCTL_API=3 etcdctl member list --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key"
3c087bf12db7a0f, started, k8s-master2, https://172.31.30.115:2380, https://172.31.30.115:2379, false
92e04392f8ad0046, started, k8s-master3, https://172.31.29.250:2380, https://172.31.29.250:2379, false
c71592552b3eb9bb, started, k8s-master1, https://172.31.30.123:2380, https://172.31.30.123:2379, false

指定输出格式为 table

# etcdctl member list --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --write-out=table
+------------------+---------+-------------+----------------------------+----------------------------+------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS | IS LEARNER |
+------------------+---------+-------------+----------------------------+----------------------------+------------+
| 3c087bf12db7a0f | started | k8s-master2 | https://172.31.30.115:2380 | https://172.31.30.115:2379 | false |
| 92e04392f8ad0046 | started | k8s-master3 | https://172.31.29.250:2380 | https://172.31.29.250:2379 | false |
| c71592552b3eb9bb | started | k8s-master1 | https://172.31.30.123:2380 | https://172.31.30.123:2379 | false |
+------------------+---------+-------------+----------------------------+----------------------------+------------+

输出内容说明如下:

  • 3c087bf12db7a0f : 集群中每一个成员的唯一 ID。
  • started : 集群成员的当前状态。started 表示活动的。
  • k8s-master2 : etcd 集群成员的名字,通常与其主机名或节点名相对应
  • https://172.31.30.115:2380 : Peer URLs,其他 etcd 成员用于与该成员通信的 URL。默认为 本地 IP 的 2380 端口
  • https://172.31.30.123:2379 : Client URLs ,客户端用于与 etcd 成员通信的 URL。默认为 本地 IP 的 2379 端口
  • Is Learner : 表示该成员是否是一个 learner。Learner 是 etcd 的一个新功能,允许一个成员作为非投票成员加入集群,直到它准备好成为一个完全参与的成员。false 表示它们都不是 learners

检查集群状态

检查单个节点的集群配置状态

# etcdctl --write-out=table endpoint status --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key
+----------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+----------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| 127.0.0.1:2379 | c71592552b3eb9bb | 3.5.3 | 106 MB | false | false | 16 | 228198001 | 228198001 | |
+----------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

查看所有节点的集群配置状态

# ENDPOINTS=https://172.31.30.115:2379,https://172.31.29.250:2379,https://172.31.30.123:2379

# etcdctl --write-out=table --endpoints=$ENDPOINTS endpoint status
+----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://172.31.30.115:2379 | 3c087bf12db7a0f | 3.5.3 | 106 MB | true | false | 16 | 228199920 | 228199920 | |
| https://172.31.29.250:2379 | 92e04392f8ad0046 | 3.5.3 | 106 MB | false | false | 16 | 228199920 | 228199920 | |
| https://172.31.30.123:2379 | c71592552b3eb9bb | 3.5.3 | 106 MB | false | false | 16 | 228199920 | 228199920 | |
+----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

# etcdctl --write-out=table --endpoints=$ENDPOINTS endpoint health
+----------------------------+--------+------------+-------+
| ENDPOINT | HEALTH | TOOK | ERROR |
+----------------------------+--------+------------+-------+
| https://172.31.30.115:2379 | true | 7.927976ms | |
| https://172.31.30.123:2379 | true | 8.011055ms | |
| https://172.31.29.250:2379 | true | 8.349179ms | |
+----------------------------+--------+------------+-------+

增加 key

# etcdctl put foo "hello world"
OK

查询 key

# etcdctl get foo
foo
hello world

按照 key 的前缀查询

# etcdctl get foo --prefix
foo
hello world
foo1
hello
foo2
world

获取所有的 key

# etcdctl get / --prefix

# etcdctl get / --prefix --keys-only
/registry/acme.cert-manager.io/orders/default/rancher1-uat-148962587001-r0wfppse-com-5lztf-3961318969

/registry/acme.cert-manager.io/orders/default/rancher1-uat-148962587001-r0wfppse-com-9wjcq-3961318969

/registry/acme.cert-manager.io/orders/default/rancher1-uat-148962587001-r0wfppse-com-ktsx5-3961318969

/registry/acme.cert-manager.io/orders/default/rancher1-uat-148962587001-r0wfppse-com-lb5h7-3961318969

/registry/acme.cert-manager.io/orders/default/rancher1-uat-148962587001-r0wfppse-com-rztlh-3961318969

/registry/apiextensions.k8s.io/customresourcedefinitions/alertmanagers.monitoring.coreos.com

/registry/apiextensions.k8s.io/customresourcedefinitions/amazonec2configs.rke-machine-config.cattle.io

/registry/apiextensions.k8s.io/customresourcedefinitions/amazonec2machines.rke-machine.cattle.io

/registry/apiextensions.k8s.io/customresourcedefinitions/amazonec2machinetemplates.rke-machine.cattle.io

删除 key

del 命令删除 key,失败返回 0,成功返回 1

# etcdctl del key   
0

# etcdctl del foo
1

监听 key 的变更

使用 etcdctl watch 命令可以监听 key 的变动。每当监听的 key 有变更,命令会监听到变更情况

# etcdctl watch foo
PUT
foo
hello pytohn
PUT
foo
hello pytohn 1

使用选项 --prefix 可以监控指定内容开始的 key 的变更,以下命令监听以 foo 开头的 key 的变更情况

# etcdctl watch foo --prefix
PUT
foo1
this is py
PUT
foo2
this is foo2

etcd 集群备份及恢复

etcd 被设计为能承受机器失败。对于一个有 N 个成员的集群能容许 (N-1)/2 的持续失败,如果集群持续丢失超过 (N-1)/2 的成员,则它最终因法定成员(Quorum)不足而最终失败,一旦法定人数丢失,集群无法达到一致而因此无法继续接收更新。

为了从灾难失败中恢复,etcd v3 提供快照以下备份数据的方式

  • 快照备份(snapshot
  • 文件备份(member/snap/db

快照备份

执行 etcd 集群快照可以作为 etc 键空间的持久备份,通过获取 etcd 成员的数据库的定期快照,etcd 集群可以被恢复到某个有已知良好状态的时间点。[1]

执行备份只需要在一个节点上执行,恢复时所有节点使用同一个备份进行恢复。

# etcdctl snapshot save etcd-102310051318.db
{"level":"info","ts":"2023-10-05T05:18:57.971Z","caller":"snapshot/v3_snapshot.go:65","msg":"created temporary db file","path":"etcd-102310051318.db.part"}
{"level":"info","ts":"2023-10-05T05:18:57.976Z","logger":"client","caller":"v3/maintenance.go:211","msg":"opened snapshot stream; downloading"}
{"level":"info","ts":"2023-10-05T05:18:57.977Z","caller":"snapshot/v3_snapshot.go:73","msg":"fetching snapshot","endpoint":"127.0.0.1:2379"}
{"level":"info","ts":"2023-10-05T05:18:58.685Z","logger":"client","caller":"v3/maintenance.go:219","msg":"completed snapshot read; closing"}
{"level":"info","ts":"2023-10-05T05:18:59.159Z","caller":"snapshot/v3_snapshot.go:88","msg":"fetched snapshot","endpoint":"127.0.0.1:2379","size":"106 MB","took":"1 second ago"}
{"level":"info","ts":"2023-10-05T05:18:59.159Z","caller":"snapshot/v3_snapshot.go:97","msg":"saved","path":"etcd-102310051318.db"}
Snapshot saved at etcd-102310051318.db

查看快照状态

# etcdctl snapshot status etcd-102310051318.db --write-out=table
+----------+-----------+------------+------------+
| HASH | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+-----------+------------+------------+
| 2c3b7c96 | 210126541 | 6844 | 106 MB |
+----------+-----------+------------+------------+

文件备份

文件备份通过拷贝 etcd 数据目录下的 member/snap/db 实现。

恢复集群

集群恢复注意事项说明: [1]

  • 所有的集群成员都必须使用同一份快照文件来恢复
  • 使用 etcdutl snapshot restore 恢复集群是需要创建新的 etcd data directories,或者将已有的目录删除,总之,一般需要一个新的集群或数据目录
  • 集群恢复会覆盖一些快照元数据(特别是 member ID 和 Cluster ID)。集群成员会失去之前的 ID,此元数据覆盖可防止新成员无意中加入现有集群。因此,为了从快照恢复,必须启动一个新的逻辑集群
  • 恢复时可能进行快照完整性检查。如果是 snampshot save 生成的快照,在 snapshot restore 时会自动检查,如果是从数据目录拷贝的备份,因为没有 integrity hash 无法校验,只能在恢复时使用参数 --skip-hash-check 跳过完整性校验。
  • 使用命令 snapshot restore 恢复时使用的参数 --initial-cluster 要确保和启动集群时使用的一致
  1. 参考以下命令,使用快照恢复,恢复时指定一个新的目录,需要在每个节点上执行
    • 节点 k8s-master1

      # etcdctl  \
      snapshot restore k8s-master1.etcd.202310051738.db \
      --endpoints=http://127.0.0.1:2379 \
      --name=k8s-master1 \
      --data-dir /var/lib/etcd/ \
      --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
      --initial-advertise-peer-urls=http://172.17.0.2:2380 \
      --initial-cluster-token etcd



      # etcd \
      --advertise-client-urls=http://172.17.0.2:2379 \
      --data-dir=/var/lib/etcd \
      --initial-advertise-peer-urls=http://172.17.0.2:2380 \
      --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
      --listen-client-urls=http://127.0.0.1:2379,http://172.17.0.2:2379 \
      --listen-peer-urls=http://172.17.0.2:2380 \
      --name=k8s-master1 \
      --initial-cluster-token etcd
    • 节点 k8s-master2

      # etcdctl  \
      snapshot restore k8s-master1.etcd.202310051738.db \
      --endpoints=http://127.0.0.1:2379 \
      --name=k8s-master2 \
      --data-dir /var/lib/etcd/ \
      --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
      --initial-advertise-peer-urls=http://172.17.0.3:2380 \
      --initial-cluster-token etcd

      # etcd \
      --advertise-client-urls=http://172.17.0.3:2379 \
      --data-dir=/var/lib/etcd \
      --initial-advertise-peer-urls=http://172.17.0.3:2380 \
      --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
      --listen-client-urls=http://127.0.0.1:2379,http://172.17.0.3:2379 \
      --listen-peer-urls=http://172.17.0.3:2380 \
      --name=k8s-master2 \
      --initial-cluster-token etcd
    • 节点 k8s-master3

      # etcdctl  \
      snapshot restore k8s-master1.etcd.202310051738.db \
      --endpoints=http://127.0.0.1:2379 \
      --name=k8s-master3 \
      --data-dir /var/lib/etcd/ \
      --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
      --initial-advertise-peer-urls=http://172.17.0.4:2380 \
      --initial-cluster-token etcd

      # etcd \
      --advertise-client-urls=http://172.17.0.4:2379 \
      --data-dir=/var/lib/etcd \
      --initial-advertise-peer-urls=http://172.17.0.4:2380 \
      --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
      --listen-client-urls=http://127.0.0.1:2379,http://172.17.0.4:2379 \
      --listen-peer-urls=http://172.17.0.4:2380 \
      --name=k8s-master3 \
      --initial-cluster-token etcd
  2. 恢复后检查节点状态
    # etcdctl member list
    119f945c36726355, started, k8s-master2, http://172.17.0.3:2380, http://172.17.0.3:2379, false
    92892585e2b66fcc, started, k8s-master3, http://172.17.0.4:2380, http://172.17.0.4:2379, false
    e18d752602ba842f, started, k8s-master1, http://172.17.0.2:2380, http://172.17.0.2:2379, false

    # etcdctl endpoint status --endpoints=$ENDPONTS --write-out=table
    +------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
    +------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
    | http://172.17.0.2:2379 | e18d752602ba842f | 3.5.3 | 106 MB | true | false | 2 | 13 | 13 | |
    | http://172.17.0.3:2379 | 119f945c36726355 | 3.5.3 | 106 MB | false | false | 2 | 13 | 13 | |
    | http://172.17.0.4:2379 | 92892585e2b66fcc | 3.5.3 | 106 MB | false | false | 2 | 13 | 13 | |
    +------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

    # etcdctl endpoint health --endpoints=$ENDPONTS --write-out=table
    +------------------------+--------+------------+-------+
    | ENDPOINT | HEALTH | TOOK | ERROR |
    +------------------------+--------+------------+-------+
    | http://172.17.0.3:2379 | true | 4.125714ms | |
    | http://172.17.0.2:2379 | true | 4.126622ms | |
    | http://172.17.0.4:2379 | true | 4.280457ms | |
    +------------------------+--------+------------+-------+

Kubernetes 中的 etcd 集群的恢复

堆叠(Stacked)etcd 拓扑 的架构下,etcd 集群由 Kubernetes 负责部署维护,Kubernetes 中的关键数据都存储于 etcd 集群中,假如 etcd 集群故障无法使用,则 Kubernetes 集群将完全无法使用,亦无法通过 Kubernetes 去管理恢复 etcd 集群。Kubernetes 堆叠(Stacked)etcd 拓扑 架构有以下特性:

  • Kubernetes 堆叠(Stacked)etcd 拓扑架构下的 etcd 集群配置详情可以查看配置文件 /etc/kubernetes/manifests/etcd.yaml
  • etcd 集群部署在 Kubernetes 集群的 Master 节点上,并且网络模式为 hostNetwork: true,即网络使用主机网络
  • etcd 通过 hostPath 方式挂载了 Kubernetes Master 主机节点目录 /var/lib/etcd,因此,etcd 节点的数据实际保留在 Kubernetes Master 主机节点目录 /var/lib/etcd
  • etcd 集群启用了 TLS 通信,使用的证书通过 hostPath 方式挂载了 Kubernetes Master 主机节点目录 /etc/kubernetes/pki/etcd

此种故障,在保存有有效的 etcd Snapshot 的情况下可以参考本示例进行恢复。

  1. 停止 kubelet ,重启 docker(或者其他 CRI)确保 Kubernetes 管理组件的 Pod 都处于停止状态,防止恢复 etcd 时 Kubernetes 写入数据。此操作在所有 Kubernetes Master 节点上执行

    # systemctl stop kubelet

    # mv /var/lib/etcd /var/lib/etcd.bak

    # systemctl restart docker

    # docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    4fbf7951781e 9fde607b81ff "kube-scheduler --au…" 4 hours ago Exited (0) 3 minutes ago k8s_kube-scheduler_kube-scheduler-k8s-master1_kube-system_cd7b8aef7d147b62355ca33773afd810_9
    0b92dd3ee5ac 8380b6a23fb8 "kube-controller-man…" 4 hours ago Exited (2) 3 minutes ago k8s_kube-controller-manager_kube-controller-manager-k8s-master1_kube-system_a4ce6832517416c082459b497050f74c_12
    02d550737d73 registry.k8s.io/pause:3.6 "/pause" 4 hours ago Exited (0) 3 minutes ago k8s_POD_etcd-k8s-master1_kube-system_80b158fbe6bff3c672202ca1b11d89fa_1
    9bdf17effd5b registry.k8s.io/pause:3.6 "/pause" 4 hours ago Exited (0) 3 minutes ago k8s_POD_kube-scheduler-k8s-master1_kube-system_cd7b8aef7d147b62355ca33773afd810_7
    6a484c46840f registry.k8s.io/pause:3.6 "/pause" 4 hours ago Exited (0) 3 minutes ago k8s_POD_kube-apiserver-k8s-master1_kube-system_f5f8d1635bc67efe34c36af301b7071d_4
    4f606e7dfc32 registry.k8s.io/pause:3.6 "/pause" 4 hours ago Exited (0) 3 minutes ago k8s_POD_kube-controller-manager-k8s-master1_kube-system_a4ce6832517416c082459b497050f74c_8
  2. 在 Kubernetes 的 Master 节点上,使用以下命令启动一个临时 etcd 容器,用来恢复备份数据。此处假设备份文件存在于主机目录 /opt/k8s-backup/此操作在所有 Kubernetes Master 节点上执行

    # docker run --rm --name etcd1 -it \
    -v /var/lib/etcd/:/var/lib/etcd/ \
    -v /opt/k8s-backup/:/opt/ \
    -v /etc/kubernetes/pki/etcd/:/etc/kubernetes/pki/etcd/ \
    k8s.gcr.io/etcd:3.5.3-0 sh
  3. 恢复 etcd 备份数据。分别在对应节点上面的临时 etcd 容器中执行恢复命令,使用的参数参考 etcd 配置 /etc/kubernetes/manifests/etcd.yaml

    • k8s-master1 节点:

      # cd /opt/

      # etcdctl snapshot restore k8s-master1.etcd.202310051738.db \
      --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key \
      --endpoints=https://127.0.0.1:2379 \
      --name=k8s-master1 \
      --data-dir /var/lib/etcd/ \
      --initial-cluster=k8s-master2=https://172.31.30.115:2380,k8s-master3=https://172.31.29.250:2380,k8s-master1=https://172.31.30.123:2380 \
      --initial-advertise-peer-urls=https://172.31.30.123:2380 \
      --initial-cluster-token etcd
    • k8s-master2 节点:

      # cd /opt/

      # etcdctl snapshot restore k8s-master1.etcd.202310051738.db \
      --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key \
      --endpoints=https://127.0.0.1:2379 \
      --name=k8s-master2 \
      --data-dir /var/lib/etcd/ \
      --initial-cluster=k8s-master2=https://172.31.30.115:2380,k8s-master3=https://172.31.29.250:2380,k8s-master1=https://172.31.30.123:2380 \
      --initial-advertise-peer-urls=https://172.31.30.115:2380 \
      --initial-cluster-token etcd
    • k8s-master3 节点:

      # cd /opt/

      # etcdctl snapshot restore k8s-master1.etcd.202310051738.db \
      --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key \
      --endpoints=https://127.0.0.1:2379 \
      --name=k8s-master3 \
      --data-dir /var/lib/etcd/ \
      --initial-cluster=k8s-master2=https://172.31.30.115:2380,k8s-master3=https://172.31.29.250:2380,k8s-master1=https://172.31.30.123:2380 \
      --initial-advertise-peer-urls=https://172.31.29.250:2380 \
      --initial-cluster-token etcd
  4. 启动 kubelet,检查集群是否恢复正常,并检查集群数据是否恢复。恢复备份数据后,可以重启(删除)所有 Pod,避免 Pod 状态和 etcd 中的数据不一致出现无法访问的问题

    # systemctl start kubelet

    # kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    k8s-master1 Ready control-plane 339d v1.24.7
    k8s-master2 Ready control-plane 339d v1.24.7
    k8s-master3 Ready control-plane 339d v1.24.7
    k8s-work1 Ready <none> 339d v1.24.7
    k8s-work2 Ready <none> 339d v1.24.7

常见错误

cluster ID mismatch

使用同一份 Snapshot k8s-master1.etcd.202310051738.db 恢复集群的过程中,etcd 报错,恢复集群过程如下

  1. 节点 k8s-master1 上恢复
    # etcdctl  \
    snapshot restore k8s-master1.etcd.202310051738.db \
    --endpoints=http://127.0.0.1:2379 \
    --name=k8s-master1 \
    --data-dir /var/lib/etcd/ \
    --initial-cluster=k8s-master1=http://172.17.0.2:2380 \
    --initial-advertise-peer-urls=http://172.17.0.2:2380




    # etcd \
    --advertise-client-urls=http://172.17.0.2:2379 \
    --data-dir=/var/lib/etcd \
    --initial-advertise-peer-urls=http://172.17.0.2:2380 \
    --initial-cluster=k8s-master1=http://172.17.0.2:2380 \
    --listen-client-urls=http://127.0.0.1:2379,http://172.17.0.2:2379 \
    --listen-peer-urls=http://172.17.0.2:2380 \
    --name=k8s-master1

  2. 节点 k8s-master2 上恢复
    # etcdctl  \
    snapshot restore k8s-master1.etcd.202310051738.db \
    --endpoints=http://127.0.0.1:2379 \
    --name=k8s-master2 \
    --data-dir /var/lib/etcd/ \
    --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380 \
    --initial-advertise-peer-urls=http://172.17.0.3:2380




    # etcd \
    --advertise-client-urls=http://172.17.0.3:2379 \
    --data-dir=/var/lib/etcd \
    --initial-advertise-peer-urls=http://172.17.0.3:2380 \
    --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380 \
    --listen-client-urls=http://127.0.0.1:2379,http://172.17.0.3:2379 \
    --listen-peer-urls=http://172.17.0.3:2380 \
    --name=k8s-master2

恢复 k8s-master2 后报错 cluster ID mismatch,这通常意味着正在尝试让两个不同的 etcd 集群中的节点相互通信,这是不被允许的

{"level":"warn","ts":"2023-10-06T05:13:08.818Z","caller":"rafthttp/stream.go:653","msg":"request sent was ignored by remote peer due to cluster ID mismatch","remote-peer-id":"69015be41c714f32","remote-peer-cluster-id":"10e5e39849dab251","local-member-id":"660aa483274d103a","local-member-cluster-id":"14dda1c29191f104","error":"cluster ID mismatch"}

要在恢复时确保集群 ID 一致,需要确保每个节点使用相同的初始化配置(--initial-cluster),修改恢复命令如下后正常

  1. 节点 k8s-master1 上恢复
    # etcdctl  \
    snapshot restore k8s-master1.etcd.202310051738.db \
    --endpoints=http://127.0.0.1:2379 \
    --name=k8s-master1 \
    --data-dir /var/lib/etcd/ \
    --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
    --initial-advertise-peer-urls=http://172.17.0.2:2380 \
    --initial-cluster-token etcd



    # etcd \
    --advertise-client-urls=http://172.17.0.2:2379 \
    --data-dir=/var/lib/etcd \
    --initial-advertise-peer-urls=http://172.17.0.2:2380 \
    --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
    --listen-client-urls=http://127.0.0.1:2379,http://172.17.0.2:2379 \
    --listen-peer-urls=http://172.17.0.2:2380 \
    --name=k8s-master1 \
    --initial-cluster-token etcd
  2. 节点 k8s-master2 上恢复
    # etcdctl  \
    snapshot restore k8s-master1.etcd.202310051738.db \
    --endpoints=http://127.0.0.1:2379 \
    --name=k8s-master2 \
    --data-dir /var/lib/etcd/ \
    --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
    --initial-advertise-peer-urls=http://172.17.0.3:2380 \
    --initial-cluster-token etcd

    # etcd \
    --advertise-client-urls=http://172.17.0.3:2379 \
    --data-dir=/var/lib/etcd \
    --initial-advertise-peer-urls=http://172.17.0.3:2380 \
    --initial-cluster=k8s-master1=http://172.17.0.2:2380,k8s-master2=http://172.17.0.3:2380,k8s-master3=http://172.17.0.4:2380 \
    --listen-client-urls=http://127.0.0.1:2379,http://172.17.0.3:2379 \
    --listen-peer-urls=http://172.17.0.3:2380 \
    --name=k8s-master2 \
    --initial-cluster-token etcd

参考链接

etcd 官网

脚注