---
# System prepended metadata

title: 把 K8S ETCD Leader 那台 VM 給砍了，在從虛擬化那層重新 Restore VM 回來會發生什麼事
tags: [K8S]

---

# 把 K8S ETCD Leader 那台 VM 給砍了，在從虛擬化那層 Restore VM 回來會發生什麼事

## 測試環境
* K8S: RKE2 (3M2W)
* OS: SLES15-sp5
* 虛擬化平台: Proxmox
## 先備知識
### Etcd 名詞
* Raft : 強一致性的算法。
* Node : 一個 Raft State Machine 實例。
* Member : 一個 etcd 實例。它管理著一個 Node，並且可以為客戶端請求提供服務。
* Peer : 對同一個 etcd 集群中另外一個 Member 的稱呼。
* WAL : 預寫式日誌，etcd 用於持久化儲存的日誌格式。
* snapshot : etcd防止WAL文件過多而設置的快照，儲存 etcd 資料狀態。
* Proxy : etcd 的一種模式，為 etcd 集群提供反向代理服務。
* Leader : Raft 算法中通過競選而產生的處理所有資料提交的節點。
* Follower : 競選失敗的節點作為 Raft 中的附屬節點，為算法提供強一致性保證。
* Candidate : 當 Follower 超過一定時間接收不到 Leader 的心跳時轉變為 Candidate 並且開始競選 Leader。
* Term : 某個節點成為 Leader 到下一次競選時間，稱為一個 Term，可以理解為一個任期。
* Index : 資料編號。 Raft 中通過 Term 和 Index 來定位資料。
### 選舉方法
1. 初始啟動時，節點處於 Follower 狀態並且會被設定一個 election timeout ，如果在這一個時間周期內沒有收到 Leader 的 heartbeat ，節點將會發起選舉，將自己切換為 candidate 之後，並向集群中其他 Follower 節點發送，詢問其是否選舉自己為 Leader 。
2. 當收到來自集群中超過半數節點的接受頭票後，節點成為 Leader ，開始接收保存 client 的數據，並向其他的 Follower 節點同步日誌。如果沒有達成一致，則 candidate 隨機選擇等待間隔 (150ms ~ 300ms) 再次發起投票，得到集群中的半數以上 Follower 接受的 candidate 將成為 Leader 。
3. Leader 節點依靠定時向 Follower 發送 heartbeat 來保持其地位。
4. 任何時候如果其他 Follower 在 election timeout 期間都沒有收到來自 Leader 的 heartbeat，同樣會將自己的狀態切換為 candidate 並發起選舉。每成功選舉一次，新 Leader 的任期 (Term) 都會比之前 Leader 的任期大 1。
5. Leader 完整性 (Leader Completeness) ： 指 Leader 日誌的完整性，當 Log 在任期 Term1 被 commit 後，那麼以後的任期，Leader 都必須包含該 Log ，Raft 在選舉階段就使用 Term 的判斷用於保證完整性，當請求投票的該 Candidate 的 Term 較大或 Term 相同 Index 較大則投票，否則拒絕該請求。

> EtcdIsVoter 是節點 condition 狀態中的一個字段，kubelet 會定期檢查節點的 condition 狀態。對於 etcd 節點，EtcdIsVoter 總是為 true，這並不代表 etcd 發生 leader 更換，僅表示該節點具備投票權。
## 先確認誰是 etcd leader
* rms2 是 ETCD 第 21 屆的 Leader
```
$ kubectl get no
NAME   STATUS   ROLES                       AGE   VERSION
rms    Ready    control-plane,etcd,master   46d   v1.27.10+rke2r1
rms2   Ready    control-plane,etcd,master   46d   v1.27.10+rke2r1
rms3   Ready    control-plane,etcd,master   46d   v1.27.10+rke2r1

$ kubectl -n kube-system exec -it etcd-rms -- bash

/ # ETCDCTL_ENDPOINTS='https://127.0.0.1:2379,https://192.168.11.131:2379,https://192.168.11.132:2379' ETCDCTL_CACERT='/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt' ETCDCTL_CERT='/var/lib/rancher/rke2/server/tls/etcd/server-client.crt' ETCDCTL_KEY='/var/lib/rancher/rke2/server/tls/etcd/server-client.key' ETCDCTL_API=3 etcdctl endpoint status -w table
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|          ENDPOINT           |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|      https://127.0.0.1:2379 | d0a8c90042e3b75e |   3.5.9 |   32 MB |     false |      false |        21 |   40023286 |           40023284 |        |
| https://192.168.11.131:2379 | a8abb01f57049108 |   3.5.9 |   42 MB |      true |      false |        21 |   40023286 |           40023286 |        |
| https://192.168.11.132:2379 | 59dccaa13dda85f3 |   3.5.9 |   31 MB |     false |      false |        21 |   40023290 |           40023262 |        |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
```
* 將 rms2 關機後再次檢查 ETCD Leader，rms3 已被選為第 24 屆 Leader。
```
/ # ETCDCTL_ENDPOINTS='https://127.0.0.1:2379,https://192.168.11.132:2379' ETCDCTL_CACERT='/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt' ETCDCTL_CERT='/var/lib/rancher/rke2/server/tls/etcd/server-client.crt' ETCDCTL_KEY='/var/lib/rancher/rke2/server/tls/etcd/server-client.key' ETCDCTL_API=3 etcdctl endpoint status -w table
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|          ENDPOINT           |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|      https://127.0.0.1:2379 | d0a8c90042e3b75e |   3.5.9 |   33 MB |     false |      false |        24 |   40027055 |           40027055 |        |
| https://192.168.11.132:2379 | 59dccaa13dda85f3 |   3.5.9 |   33 MB |      true |      false |        24 |   40027064 |           40027055 |        |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
```
* 將 rms2 那台 Node 關機，並建立硬碟的備份。
* 再從 Proxmox node Storgae -> Backups -> Restore 一台新的 VM 出來，並開機。
![image](https://hackmd.io/_uploads/S184UCxlR.png)

## 把 rms2 Restore 後檢查誰是新的 leader
* 確認 rms 已被重新選舉為新的 leader
```
$ / # ETCDCTL_ENDPOINTS='https://127.0.0.1:2379,https://192.168.11.131:2379,https://192.168.11.132:2379' ETCDCTL_CACERT='/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt' ETCDCTL_CERT='/var/lib/rancher/rke2/server/tls/etcd/server-client.crt' ETCDCTL_KEY='/var/lib/rancher/rke2/server/tls/etcd/server-client.key' ETCDCTL_API=3 etcdctl endpoint status -w table
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|          ENDPOINT           |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|      https://127.0.0.1:2379 | d0a8c90042e3b75e |   3.5.9 |   48 MB |      true |      false |        25 |   40033847 |           40033846 |        |
| https://192.168.11.131:2379 | a8abb01f57049108 |   3.5.9 |   48 MB |     false |      false |        25 |   40033847 |           40033847 |        |
| https://192.168.11.132:2379 | 59dccaa13dda85f3 |   3.5.9 |   47 MB |     false |      false |        25 |   40033848 |           40033844 |        |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
```

## 檢視 rms3 ETCD pod logs
* Term : 某個節點成為 Leader 到下一次競選時間，稱為一個 Term，可以理解為一個任期。
* 可以看到在第 25 任期，rms3 這台 node 由 leader 變成 follower ，新任 leader 為 rms。
```
$ kubectl -n kube-system logs etcd-rms3 | less
{"level":"info","ts":"2024-04-08T03:28:07.685661Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"59dccaa13dda85f3 [term: 24] received a MsgHeartbeat message with higher term from d0a8c90042e3b75e [term: 25]"}
{"level":"info","ts":"2024-04-08T03:28:07.685725Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"59dccaa13dda85f3 became follower at term 25"}
{"level":"info","ts":"2024-04-08T03:28:07.68577Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"raft.node: 59dccaa13dda85f3 changed leader from 59dccaa13dda85f3 to d0a8c90042e3b75e at term 25"}
```

## 參考連結
https://alanzhan.dev/post/2022-02-28-kubetnetes-etcd/

https://etcd.io/docs/v3.6/op-guide/runtime-configuration/#add-a-new-member