---
# System prepended metadata

title: RKE1 在沒有快照的情況下恢復 etcd
tags: [trouble]

---

# RKE1 在沒有快照的情況下恢復 etcd

## 前言
* 如果群集原有兩個 ETCD 節點而壞掉一個，或是原有三個 ETCD 節點而壞掉兩個。這時候 ETCD 叢集會自動降級，會變成唯讀狀態，這種情況下只能進行 ETCD 叢集恢復。對於早期的 Rancher 版本或沒有開啟自動備份的 Rancher 環境，將需要使用 `/var/lib/etcd` 目錄的 etcd 資料進行還原。
* 當叢集中三個 Control Plane 節點的 ETCD 出現 `request cluster ID mismatch` 問題時，可以保留一個 ETCD 實例透過 `--force-new-cluster` 參數重建集群，然後再將其他兩個節點的 ETCD 實例加入叢集。

## 實作
* 環境檢查
```
$ kubectl get no
NAME     STATUS   ROLES                      AGE   VERSION
rke-m1   Ready    controlplane,etcd,worker   80d   v1.28.12
rke-m2   Ready    controlplane,etcd,worker   80d   v1.28.12
rke-m3   Ready    controlplane,etcd,worker   80d   v1.28.12
rke-w1   Ready    worker                     80d   v1.28.12

$ docker exec -it etcd etcdctl member list
37f57fc09a11af9e, started, etcd-rke-m1, https://192.168.11.102:2380, https://192.168.11.102:2379, false
42c538fc0ebfad32, started, etcd-rke-m2, https://192.168.11.53:2380, https://192.168.11.53:2379, false
84645341c9406c2f, started, etcd-rke-m3, https://192.168.11.54:2380, https://192.168.11.54:2379, false

$ kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE   ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-0               Healthy   ok
```

### 環境模擬
1. 刪除所有其他 etcd 節點，在叢集中只保留一個 etcd 節點，在 rke-m2、rke-m3 節點上刪除 etcd container

```
$ docker rm -f etcd
```

```
$ kubectl  get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS      MESSAGE   ERROR
controller-manager   Healthy     ok
scheduler            Healthy     ok
etcd-0               Unhealthy             error getting data from etcd: context deadline exceeded****
```

## 恢復
1. 在剩下的 etcd 節點上也就是 rke-m1 上執行，將此命令輸出 etcd 的執行命令，保存此命令以供以後使用

```
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock assaflavie/runlike:latest etcd
Unable to find image 'assaflavie/runlike:latest' locally
latest: Pulling from assaflavie/runlike
43c4264eed91: Pull complete
6a9ddd5be51f: Pull complete
4f4fb700ef54: Pull complete
c34748d1a228: Pull complete                                                                                                                             0e631f15cb81: Pull complete
4a380a588d96: Pull complete
a3d222998183: Pull complete
dd05ff3e1671: Pull complete
547955fbd3af: Pull complete
911c38b2b574: Pull complete
d957e402457f: Pull complete
9b631605838e: Pull complete
47096f94733e: Pull complete
8592fececf28: Pull complete
81dfa20479b7: Pull complete
53411a209e61: Pull complete
ac8f6a15e782: Pull complete
66e95e9d7b7a: Pull complete
5a9c0ffdb53a: Pull complete
Digest: sha256:d7ac59e9c60cd817036dc369961f147eeda97db7efab96719c94bee6d6268c07
Status: Downloaded newer image for assaflavie/runlike:latest
docker run --name=etcd --hostname=rke-m1 --user=0 --env=ETCDCTL_API=3 --env=ETCDCTL_CACERT=/etc/kubernetes/ssl/kube-ca.pem --env=ETCDCTL_CERT=/etc/kubernetes/ssl/kube-etcd-192-168-11-102.pem --env=ETCDCTL_KEY=/etc/kubernetes/ssl/kube-etcd-192-168-11-102-key.pem --env=ETCDCTL_ENDPOINTS=https://127.0.0.1:2379 --env=ETCD_UNSUPPORTED_ARCH=x86_64 --volume=/var/lib/etcd:/var/lib/rancher/etcd/ --volume=/etc/kubernetes:/etc/kubernetes --network=host --workdir=/var/lib/etcd --restart=always --label='io.rancher.rke.container.name=etcd' --log-opt max-size=10m --log-opt max-file=5 --runtime=runc --detach=true registry.rancher.com/rancher/mirrored-coreos-etcd:v3.5.10 /usr/local/bin/etcd --name=etcd-rke-m1 --cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 --election-timeout=5000 --initial-advertise-peer-urls=https://192.168.11.102:2380 --initial-cluster=etcd-rke-m3=https://192.168.11.54:2380,etcd-rke-m1=https://192.168.11.102:2380,etcd-rke-m2=https://192.168.11.53:2380 --peer-trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem --key-file=/etc/kubernetes/ssl/kube-etcd-192-168-11-102-key.pem --initial-cluster-state=new --trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem --cert-file=/etc/kubernetes/ssl/kube-etcd-192-168-11-102.pem --client-cert-auth=true --data-dir=/var/lib/rancher/etcd/ --initial-cluster-token=etcd-cluster-1 --listen-client-urls=https://0.0.0.0:2379 --listen-peer-urls=https://0.0.0.0:2380 --peer-client-cert-auth=true --peer-cert-file=/etc/kubernetes/ssl/kube-etcd-192-168-11-102.pem --peer-key-file=/etc/kubernetes/ssl/kube-etcd-192-168-11-102-key.pem --advertise-client-urls=https://192.168.11.102:2379 --heartbeat-interval=500
```

2. 停止在 rke-m1 啟動的 etcd 容器，將其重新命名為 etcd-old

```
$ docker stop etcd

$ docker rename etcd etcd-old
```

3. 修改先前儲存的 ETCD 啟動命令，在 `--initial-cluster` 參數中刪除第二、三台 Control Plane 節點的 ETCD 訊息，並在最後添加 `--force-new-cluster` 參數，修改後在 rke-m1 上執行
```
$ docker run --name=etcd --hostname=rke-m1 --user=0 --env=ETCDCTL_API=3 --env=ETCDCTL_CACERT=/etc/kubernetes/ssl/kube-ca.pem --env=ETCDCTL_CERT=/etc/kubernetes/ssl/kube-etcd-192-168-11-102.pem --env=ETCDCTL_KEY=/etc/kubernetes/ssl/kube-etcd-192-168-11-102-key.pem --env=ETCDCTL_ENDPOINTS=https://127.0.0.1:2379 --env=ETCD_UNSUPPORTED_ARCH=x86_64 --volume=/var/lib/etcd:/var/lib/rancher/etcd/ --volume=/etc/kubernetes:/etc/kubernetes --network=host --workdir=/var/lib/etcd --restart=always --label='io.rancher.rke.container.name=etcd' --log-opt max-size=10m --log-opt max-file=5 --runtime=runc --detach=true registry.rancher.com/rancher/mirrored-coreos-etcd:v3.5.10 /usr/local/bin/etcd --name=etcd-rke-m1 --cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 --election-timeout=5000 --initial-advertise-peer-urls=https://192.168.11.102:2380 --initial-cluster=etcd-rke-m1=https://192.168.11.102:2380 --peer-trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem --key-file=/etc/kubernetes/ssl/kube-etcd-192-168-11-102-key.pem --initial-cluster-state=new --trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem --cert-file=/etc/kubernetes/ssl/kube-etcd-192-168-11-102.pem --client-cert-auth=true --data-dir=/var/lib/rancher/etcd/ --initial-cluster-token=etcd-cluster-1 --listen-client-urls=https://0.0.0.0:2379 --listen-peer-urls=https://0.0.0.0:2380 --peer-client-cert-auth=true --peer-cert-file=/etc/kubernetes/ssl/kube-etcd-192-168-11-102.pem --peer-key-file=/etc/kubernetes/ssl/kube-etcd-192-168-11-102-key.pem --advertise-client-urls=https://192.168.11.102:2379 --heartbeat-interval=500 --force-new-cluster --force-new-cluster
```
4. 在 rke-m1 上執行修改後的指令，啟動完畢後檢查 ETCD 叢集狀態
```
$ docker exec -it -e ETCDCTL_API=3 etcd etcdctl member list -w table
+------------------+---------+-------------+-----------------------------+-----------------------------+------------+
|        ID        | STATUS  |    NAME     |         PEER ADDRS          |        CLIENT ADDRS         | IS LEARNER |
+------------------+---------+-------------+-----------------------------+-----------------------------+------------+
| 37f57fc09a11af9e | started | etcd-rke-m1 | https://192.168.11.102:2380 | https://192.168.11.102:2379 |      false |
+------------------+---------+-------------+-----------------------------+-----------------------------+------------+

$ docker exec -it -e ETCDCTL_API=3 etcd etcdctl endpoint status --cluster -w table
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|          ENDPOINT           |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://192.168.11.102:2379 | 37f57fc09a11af9e |  3.5.10 |   30 MB |      true |      false |        35 |   36484638 |           36484638 |        |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
```

5. 在單一節點啟動並運行之後，需要向叢集添加另外兩個 etcd 節點，在 rke-m1 節點上新增 ETCD Member
```
$ MEMBER_IP=192.168.11.53      # 此為 rke-m2 ip 

$ MEMBER_NAME="rke-m2"

# 執行完指令後，下面的設定需要保留，後續節點啟動 ETCD 時需要使用
$ docker exec -it etcd etcdctl member add etcd-$MEMBER_NAME --peer-urls=https://$MEMBER_IP:2380
Member 86fd8166bc4a7b2d added to cluster 9e1d9b2d2fd350ec

ETCD_NAME="etcd-rke-m2"
ETCD_INITIAL_CLUSTER="etcd-rke-m1=https://192.168.11.102:2380,etcd-rke-m2=https://192.168.11.53:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.11.53:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
```
6. 接著在 rke-m2 節點執行，進行恢復
```
# 備份數據
$ mv /var/lib/etcd /var/lib/etcd_bak

# 設定變數
$ NODE_IP=192.168.11.53
$ ETCD_IMAGE=registry.rancher.com/rancher/mirrored-coreos-etcd:v3.5.10   # 注意自己原本的 etcd 版本
$ ETCD_NAME="etcd-rke-m2"
$ ETCD_INITIAL_CLUSTER="etcd-rke-m1=https://192.168.11.102:2380,etcd-rke-m2=https://192.168.11.53:2380"
$ ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.11.53:2380"
$ ETCD_INITIAL_CLUSTER_STATE="existing"

# 啟動 ETCD
$ docker run --name=etcd --hostname=`hostname` \
--env="ETCDCTL_API=3" \
--env="ETCDCTL_CACERT=/etc/kubernetes/ssl/kube-ca.pem" \
--env="ETCDCTL_CERT=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`.pem" \
--env="ETCDCTL_KEY=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`-key.pem" \
--env="ETCDCTL_ENDPOINTS=https://127.0.0.1:2379" \
--env="ETCD_UNSUPPORTED_ARCH=x86_64" \
--env="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
--volume="/var/lib/etcd:/var/lib/rancher/etcd/:z" \
--volume="/etc/kubernetes:/etc/kubernetes:z" \
--network=host \
--restart=always \
--label io.rancher.rke.container.name="etcd" \
--detach=true \
$ETCD_IMAGE \
/usr/local/bin/etcd \
--peer-client-cert-auth \
--client-cert-auth \
--peer-cert-file=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`.pem \
--peer-key-file=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`-key.pem \
--cert-file=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`.pem \
--trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem \
--initial-cluster-token=etcd-cluster-1 \
--peer-trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem \
--key-file=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`-key.pem \
--data-dir=/var/lib/rancher/etcd/ \
--advertise-client-urls=https://$NODE_IP:2379 \
--listen-client-urls=https://0.0.0.0:2379 \
--listen-peer-urls=https://0.0.0.0:2380 \
--initial-advertise-peer-urls=https://$NODE_IP:2380 \
--election-timeout=5000 \
--heartbeat-interval=500 \
--name=$ETCD_NAME \
--initial-cluster=$ETCD_INITIAL_CLUSTER \
--initial-cluster-state=$ETCD_INITIAL_CLUSTER_STATE
```
* 啟動完後檢查狀態，如果沒問題則可以重複上面步驟新增第三台 etcd 節點
```
$ docker exec -it -e ETCDCTL_API=3 etcd etcdctl member list -w table
+------------------+---------+-------------+-----------------------------+-----------------------------+------------+
|        ID        | STATUS  |    NAME     |         PEER ADDRS          |        CLIENT ADDRS         | IS LEARNER |
+------------------+---------+-------------+-----------------------------+-----------------------------+------------+
| 37f57fc09a11af9e | started | etcd-rke-m1 | https://192.168.11.102:2380 | https://192.168.11.102:2379 |      false |
| 86fd8166bc4a7b2d | started | etcd-rke-m2 |  https://192.168.11.53:2380 |  https://192.168.11.53:2379 |      false |
+------------------+---------+-------------+-----------------------------+-----------------------------+------------+


$ docker exec -it -e ETCDCTL_API=3 etcd etcdctl endpoint status --cluster -w table
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|          ENDPOINT           |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://192.168.11.102:2379 | 37f57fc09a11af9e |  3.5.10 |   30 MB |      true |      false |        36 |   36487134 |           36487134 |        |
|  https://192.168.11.53:2379 | 86fd8166bc4a7b2d |  3.5.10 |   30 MB |     false |      false |        36 |   36487134 |           36487134 |        |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
```

7. 恢復 rke2-m3
```
# 回到 rke-m1 執行
$ MEMBER_IP=192.168.11.54  # 此為 rke-m3 ip 

$ MEMBER_NAME="rke-m3"

# 執行完指令後，下面的設定需要保留，後續節點啟動 ETCD 時需要使用
$ docker exec -it etcd etcdctl member add etcd-$MEMBER_NAME --peer-urls=https://$MEMBER_IP:2380
Member a7b94698b22fb60c added to cluster 9e1d9b2d2fd350ec

ETCD_NAME="etcd-rke-m3"
ETCD_INITIAL_CLUSTER="etcd-rke-m1=https://192.168.11.102:2380,etcd-rke-m2=https://192.168.11.53:2380,etcd-rke-m3=https://192.168.11.54:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.11.54:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
```
8. 在 rke-m3 節點執行，進行恢復
```
# 備份數據
$ mv /var/lib/etcd /var/lib/etcd_bak

# 設定變數
$ NODE_IP=192.168.11.54
$ ETCD_IMAGE=registry.rancher.com/rancher/mirrored-coreos-etcd:v3.5.10   # 注意自己原本的 etcd 版本
$ ETCD_NAME="etcd-rke-m3"
$ ETCD_INITIAL_CLUSTER="etcd-rke-m1=https://192.168.11.102:2380,etcd-rke-m2=https://192.168.11.53:2380,etcd-rke-m3=https://192.168.11.54:2380"
$ ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.11.54:2380"
$ ETCD_INITIAL_CLUSTER_STATE="existing"

# 啟動 ETCD
$ docker run --name=etcd --hostname=`hostname` \
--env="ETCDCTL_API=3" \
--env="ETCDCTL_CACERT=/etc/kubernetes/ssl/kube-ca.pem" \
--env="ETCDCTL_CERT=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`.pem" \
--env="ETCDCTL_KEY=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`-key.pem" \
--env="ETCDCTL_ENDPOINTS=https://127.0.0.1:2379" \
--env="ETCD_UNSUPPORTED_ARCH=x86_64" \
--env="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
--volume="/var/lib/etcd:/var/lib/rancher/etcd/:z" \
--volume="/etc/kubernetes:/etc/kubernetes:z" \
--network=host \
--restart=always \
--label io.rancher.rke.container.name="etcd" \
--detach=true \
$ETCD_IMAGE \
/usr/local/bin/etcd \
--peer-client-cert-auth \
--client-cert-auth \
--peer-cert-file=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`.pem \
--peer-key-file=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`-key.pem \
--cert-file=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`.pem \
--trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem \
--initial-cluster-token=etcd-cluster-1 \
--peer-trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem \
--key-file=/etc/kubernetes/ssl/kube-etcd-`echo $NODE_IP|sed 's/\./-/g'`-key.pem \
--data-dir=/var/lib/rancher/etcd/ \
--advertise-client-urls=https://$NODE_IP:2379 \
--listen-client-urls=https://0.0.0.0:2379 \
--listen-peer-urls=https://0.0.0.0:2380 \
--initial-advertise-peer-urls=https://$NODE_IP:2380 \
--election-timeout=5000 \
--heartbeat-interval=500 \
--name=$ETCD_NAME \
--initial-cluster=$ETCD_INITIAL_CLUSTER \
--initial-cluster-state=$ETCD_INITIAL_CLUSTER_STATE
```
* 確認三台 etcd 節點已恢復
```
$ docker exec -it -e ETCDCTL_API=3 etcd etcdctl member list -w table
+------------------+---------+-------------+-----------------------------+-----------------------------+------------+
|        ID        | STATUS  |    NAME     |         PEER ADDRS          |        CLIENT ADDRS         | IS LEARNER |
+------------------+---------+-------------+-----------------------------+-----------------------------+------------+
| 37f57fc09a11af9e | started | etcd-rke-m1 | https://192.168.11.102:2380 | https://192.168.11.102:2379 |      false |
| 86fd8166bc4a7b2d | started | etcd-rke-m2 |  https://192.168.11.53:2380 |  https://192.168.11.53:2379 |      false |
| a7b94698b22fb60c | started | etcd-rke-m3 |  https://192.168.11.54:2380 |  https://192.168.11.54:2379 |      false |
+------------------+---------+-------------+-----------------------------+-----------------------------+------------+


$ docker exec -it -e ETCDCTL_API=3 etcd etcdctl endpoint status --cluster -w table
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|          ENDPOINT           |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://192.168.11.102:2379 | 37f57fc09a11af9e |  3.5.10 |   30 MB |      true |      false |        36 |   36489375 |           36489375 |        |
|  https://192.168.11.53:2379 | 86fd8166bc4a7b2d |  3.5.10 |   30 MB |     false |      false |        36 |   36489375 |           36489375 |        |
|  https://192.168.11.54:2379 | a7b94698b22fb60c |  3.5.10 |   30 MB |     false |      false |        36 |   36489375 |           36489375 |        |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
```

```
$ kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE   ERROR
scheduler            Healthy   ok
etcd-0               Healthy   ok
controller-manager   Healthy   ok
```


## 參考
https://blog.csdn.net/Ivan_Wz/article/details/117022325?spm=1001.2014.3001.5502