昨天,我們用 cert-manager 自動簽發了 TLS 憑證,也實際看到了 Custom Resource、Controller 與 Secret 之間的運作方式。
但你有沒有想過:當你執行 kubectl get pod、kubectl get deployment 或 kubectl get certificate 時,這些資源的狀態究竟存在哪裡?
答案就是 etcd。
etcd 是 Kubernetes Control Plane 用來保存叢集狀態的重要 Key-Value Store。
不管是 Pod、Service、Deployment,還是 cert-manager 建立的 Certificate、Issuer 與 Secret,這些 Kubernetes API Resource 的狀態最終都會由 API Server 持久化到 etcd。
可以把 etcd 想成整個 Kubernetes 叢集的「記憶核心」:
API Server 負責提供操作入口,而 etcd 負責保存叢集目前知道的狀態。
如果 etcd 發生嚴重故障,Control Plane 就會失去可靠的狀態讀寫能力,新的資源建立、更新與排程等操作都可能受到影響。
今天我們會先理解 etcd 在 Kubernetes 裡的角色與資料流向,接著認識它背後的 Raft 共識機制,再實際查看 etcd 儲存的資料,最後完成備份與還原操作。
今天內容涵蓋:
以下操作皆在 master 節點執行。
etcd 是一個具備強一致性的分散式 Key-Value Store。
簡單來說,可以把 etcd 想成 Kubernetes Control Plane 共用的「狀態資料庫」:
/registry/... 的路徑組織資源資料| 需求 | etcd 怎麼滿足 |
|---|---|
| 叢集狀態要可靠儲存 | 透過 Raft 在多個 Member 之間複製並提交資料 |
| 多個 API Server 要讀寫同一份資料 | 多個 kube-apiserver 可以共用同一個 etcd Cluster |
| Controller 需要監聽資源變化 | etcd 提供 Watch 能力,kube-apiserver 再向其他元件提供 Kubernetes Watch API |
| Control Plane 需要低延遲存取 | etcd 專門針對一致性與 Metadata 類型的工作負載設計 |
💡 連結 Day 27–28
還記得 cert-manager Controller 會監聽
Certificate資源的變化嗎?cert-manager 並不會直接連線 etcd,而是透過 Kubernetes API Server 的 Watch API 接收資源變化。
當你執行:
kubectl apply ↓ kube-apiserver ↓ etcd資源狀態被持久化後,kube-apiserver 會透過 Watch 機制把對應的變化提供給正在監聽的 Controller,cert-manager 再開始進行 Reconciliation。

⚠️ 重要觀念
在標準 Kubernetes 架構中,
kube-apiserver是直接讀寫 etcd 的 Control Plane 元件。
kube-scheduler、kube-controller-manager、kubelet,以及 cert-manager 等 Controller,都是透過 Kubernetes API Server 讀取或修改叢集狀態,而不是直接操作 etcd。
etcd 的高可用與一致性能力,核心來自 Raft 共識演算法。
Raft 讓多個 etcd Member 可以針對資料變更達成一致,即使部分節點發生故障,只要叢集仍然維持 Quorum,就可以繼續運作。
可以把 Raft 想成一艘船上的三種角色:
| 角色 | 做什麼 | 比喻 |
|---|---|---|
| Leader | 負責協調新的寫入,將 Log Entry 複製到其他 Member | 船長 —— 負責下達新的航行指令 |
| Follower | 接收 Leader 的 AppendEntries,並回應投票與複製請求 | 水手 —— 接收船長指令並同步航海日誌 |
| Candidate | Election Timeout 到期後發起新一輪選舉 | 候選船長 —— Leader 失聯後爭取其他節點投票 |

💓 什麼是 Heartbeat?
Leader 當選後,會定期向 Follower 發送
AppendEntriesRPC。當沒有新的 Log Entry 時,這些請求可以是空的,主要用來告訴 Follower:「Leader 還活著」。
如果 Follower 在 Election Timeout 內都沒有收到有效的 Heartbeat,就可能轉成 Candidate 並發起新的選舉。
當有新的 Log Entry 需要同步時,
AppendEntries也會負責將資料複製到 Follower。

當新的寫入發生時,大致流程是:
AppendEntries 將 Log Entry 複製到 FollowerRaft 需要取得 多數節點(Quorum) 才能繼續進行共識操作。
Quorum 可以簡單理解成:
Quorum = floor(N / 2) + 1
| 節點數 | Quorum | 可容忍故障數 | 說明 |
|---|---|---|---|
| 1 | 1 | 0 | 單節點,不具備節點故障容錯 |
| 2 | 2 | 0 | 任一 Member 故障都會失去 Quorum |
| 3 | 2 | 1 | ✅ 常見的 HA 配置 |
| 4 | 3 | 1 | 與 3 個 Member 的故障容忍數相同 |
| 5 | 3 | 2 | ✅ 可以容忍 2 個 Member 故障 |
💡 為什麼通常使用奇數 Member?
因為偶數 Member 通常不會提升故障容忍能力。
例如 3 個 Member 和 4 個 Member 都只能容忍 1 個 Member 故障,但 4 個 Member 需要多維護一份資料複製與網路通訊。
因此 etcd 通常建議使用奇數個 Member,例如 3 或 5 個。
概念講完了,接下來直接看看 etcd 裡實際存了什麼。
在 kubeadm 建立的叢集中,etcd 預設會以 Static Pod 的形式運行在 Control Plane Node 上。
先確認 etcd Pod 是否正常:
kubectl get pods -n kube-system | grep etcd

應該會看到類似:
etcd-master
只要 Pod 顯示為 Running,就代表 etcd 目前有正常運行。
在 kubeadm 環境中,etcd 通常以 Static Pod 的形式運行,主機上不一定會預先安裝 etcdctl 與 etcdutl。
先確認目前 etcd 使用的版本:
kubectl describe pod etcd-master -n kube-system | grep Image:
輸出類似:
Image: registry.k8s.io/etcd:3.6.4-0

接著安裝對應版本的 etcdctl 與 etcdutl:
# Kubernetes 的 etcd Image Tag 可能帶有 "-0" 後綴,
# GitHub Release 則使用 v3.6.4 這類版本號
ETCD_VER=v3.6.4
curl -L \
https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz \
-o /tmp/etcd.tar.gz
tar xzf /tmp/etcd.tar.gz -C /tmp/
sudo mv /tmp/etcd-${ETCD_VER}-linux-amd64/etcdctl /usr/local/bin/
sudo mv /tmp/etcd-${ETCD_VER}-linux-amd64/etcdutl /usr/local/bin/
rm -rf /tmp/etcd.tar.gz /tmp/etcd-${ETCD_VER}-linux-amd64
確認版本:
etcdctl version

⚠️ 版本建議保持一致
實作時建議讓
etcdctl、etcdutl與 etcd Server 使用相同版本,避免因版本差異造成操作行為或功能不一致。
kubeadm 建立的 etcd 預設使用 TLS 保護 Client Connection,因此 etcdctl 需要指定 Endpoint、CA 與 Client Certificate。
export ETCDCTL_ENDPOINTS=https://127.0.0.1:2379
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/healthcheck-client.crt
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/healthcheck-client.key
其中:
ETCDCTL_ENDPOINTS:etcd Client EndpointETCDCTL_CACERT:驗證 etcd Server 憑證的 CAETCDCTL_CERT:etcdctl 使用的 Client CertificateETCDCTL_KEY:Client Certificate 對應的 Private Key可以先測試是否能正常連線:
etcdctl endpoint health
正常情況下會看到類似:
https://127.0.0.1:2379 is healthy
💡 關於
ETCDCTL_API=3不需要再設定:
export ETCDCTL_API=3etcdctl 從 v3.4 起就已經預設使用 v3 API,而 etcd v3.6 也已移除舊的 v2 Server API 支援。
先確認目前是否能正常連線到 etcd:
etcdctl endpoint health
如果連線正常,會看到類似:
https://127.0.0.1:2379 is healthy

如果畫面出現:
unrecognized environment variable "ETCDCTL_API=3"
代表目前 Shell 還保留舊版 etcdctl 使用的環境變數,可以移除:
unset ETCDCTL_API
etcd 3.6 已經不需要再設定 ETCDCTL_API=3。
接著查看 etcd Cluster 的成員:
etcdctl member list -w table

這個 Lab 是單節點 etcd,因此只會看到一個 Member:
master
在多 Control Plane 的 HA 架構中,則通常會看到多個 etcd Member。
接下來直接查看 etcd 裡儲存的 Key:
etcdctl get / --prefix --keys-only | head -30

可以看到 Kubernetes 的資源資料通常會儲存在 /registry/ 這個 Storage Prefix 底下,再依照不同資源類型組織資料。
例如截圖中可以看到:
/registry/apiextensions.k8s.io/customresourcedefinitions/certificates.cert-manager.io
/registry/apiextensions.k8s.io/customresourcedefinitions/certificaterequests.cert-manager.io
/registry/apiextensions.k8s.io/customresourcedefinitions/clusterissuers.cert-manager.io
💡 連結 Day 28
這些 Key 就是 Day 28 安裝 cert-manager 時建立的 CRD 定義。
certificates.cert-manager.io、certificaterequests.cert-manager.io等 CRD 本身也是 Kubernetes Resource,因此它們的狀態同樣會由 kube-apiserver 持久化到 etcd。至於我們實際建立的
Certificate、CertificateRequest等 Custom Resource,也會由 kube-apiserver 儲存在 etcd,只是會位於各自對應的 Storage Key 下。
先直接查看某個 Namespace 在 etcd 中的資料:
etcdctl get /registry/namespaces/default

你會看到輸出中混雜著可讀文字,例如 default、Active,以及一些看起來像亂碼的二進位內容。
這是因為 kube-apiserver 在 etcd 中儲存許多內建 Kubernetes Resource 時,通常會使用 Protocol Buffers(Protobuf) 進行序列化,而不是直接存成 JSON。
Protobuf 是一種緊湊的二進位格式,因此:
💡 補充
Custom Resource 的儲存格式可能不同,CRD 建立的資源通常不會使用和內建資源完全相同的 Protobuf Schema。
接著看看目前 etcd 裡總共有多少 Key:
etcdctl get / --prefix --keys-only | wc -l

這個數字代表目前 etcd 中儲存的 Key 數量。
它不完全等於 Kubernetes Resource 的數量,但可以讓你感受到一個叢集背後其實保存了大量的狀態資料。
etcd 保存了 Kubernetes 叢集的重要狀態資料,因此定期建立 Snapshot 是 Control Plane 維運中非常重要的一環。
先建立備份目錄:
mkdir -p /opt/etcd-backup
接著使用 etcdctl snapshot save 建立 Snapshot:
etcdctl snapshot save /opt/etcd-backup/snapshot-$(date +%Y%m%d).db

成功後會看到類似:
Snapshot saved at /opt/etcd-backup/snapshot-20260306.db
如果要確認 Snapshot 的內容,可以使用 etcdutl snapshot status:
etcdutl snapshot status \
/opt/etcd-backup/snapshot-$(date +%Y%m%d).db \
-w table
輸出會包含 Snapshot 的 Hash、Revision、Key 數量與檔案大小。
為了驗證 Snapshot 還原是否真的有效,我們先建立一個測試用的 Deployment,接著再把它刪除,模擬資源被誤刪的情境。
# 記錄目前的 Deployment 列表
kubectl get deployments -A

建立一個測試用的 Deployment:
kubectl create deployment etcd-test \
--image=nginx \
--replicas=2
確認 Deployment 已正常建立:
kubectl get deployment etcd-test

接著再建立一份 Snapshot,確保這份備份中包含 etcd-test:
etcdctl snapshot save /opt/etcd-backup/snapshot-with-test.db
現在模擬資源被誤刪:
kubectl delete deployment etcd-test
確認它已經不存在:
kubectl get deployment etcd-test
預期會看到:
Error from server (NotFound): deployments.apps "etcd-test" not found

⚠️ 注意
還原 etcd 會讓叢集狀態回到 Snapshot 建立時的時間點,Snapshot 之後建立或修改的 Kubernetes Resource 都可能消失。
正式環境操作前,務必確認 Snapshot、etcd Member 設定與還原流程都正確。
先停止 kube-apiserver 與 etcd 的 Static Pod:
sudo mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/
sudo mv /etc/kubernetes/manifests/etcd.yaml /tmp/
等待 kubelet 停止對應容器:
sleep 10
sudo crictl ps | grep -E "etcd|kube-apiserver"
如果沒有輸出,代表兩個容器都已經停止。
接著保留目前的 etcd Data Directory:
sudo mv /var/lib/etcd /var/lib/etcd.bak
使用剛才包含 etcd-test Deployment 的 Snapshot 進行還原:
sudo etcdutl snapshot restore \
/opt/etcd-backup/snapshot-with-test.db \
--name=master \
--data-dir=/var/lib/etcd \
--initial-cluster=master=https://10.140.0.33:2380 \
--initial-advertise-peer-urls=https://10.140.0.33:2380
💡 為什麼要指定這些參數?
snapshot restore會建立新的 etcd Member 與 Cluster identity,因此這裡要讓還原後的 Member 設定與 kubeadm 的 etcd Static Pod 保持一致。可以從
/etc/kubernetes/manifests/etcd.yaml查看目前使用的--name、--initial-cluster與--initial-advertise-peer-urls。
還原完成後,把 Static Pod Manifest 放回去:
sudo mv /tmp/etcd.yaml /etc/kubernetes/manifests/
sudo mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
等待 Control Plane 恢復:
sleep 30
💡 正式 Kubernetes 災難復原的補充
如果是從較舊的 Snapshot 還原,etcd 官方建議考慮使用
--bump-revision與--mark-compacted,避免 Kubernetes Controller / Informer 因 etcd Revision 倒退而保留過期 Cache。
還原完成後,先重新設定 etcdctl 的連線資訊:
unset ETCDCTL_API
export ETCDCTL_ENDPOINTS=https://127.0.0.1:2379
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/healthcheck-client.crt
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/healthcheck-client.key
先確認 etcd 是否恢復健康:
etcdctl endpoint health
接著確認 Kubernetes Control Plane 是否已恢復:
kubectl get nodes
最後驗證剛才被刪除的 Deployment 是否重新出現:
kubectl get deployment etcd-test

如果一切正常,你會再次看到 etcd-test Deployment。
這代表叢集狀態已經回到 Snapshot 建立時的時間點,也驗證了這次 etcd Snapshot Restore 成功。

etcd 的延遲與健康狀態,會直接影響 Kubernetes Control Plane 的反應速度。
最後我們來看看幾個常用的健康檢查方式,以及日常維運時需要注意的效能指標。
# 查看 Endpoint 健康狀態
etcdctl endpoint health -w table
# 執行簡單的效能測試
etcdctl check perf

endpoint health 主要用來確認 etcd 是否能正常處理請求。
check perf 則會對目前 Endpoint 做簡單的寫入效能測試,適合快速確認磁碟與 etcd 寫入表現是否明顯異常。
| 指標 | 觀察重點 |
|---|---|
| WAL fsync 延遲 | 越低越好;如果長期升高,通常要優先檢查磁碟 I/O |
| Backend commit 延遲 | 反映 Backend 寫入延遲,持續升高可能代表磁碟效能不足 |
| Leader 選舉次數 | 應該維持低且穩定;頻繁選舉可能代表網路、CPU 或磁碟不穩定 |
| DB Size | 持續增長時要注意 Backend Quota、歷史 Revision 與碎片空間 |
💡 Backend Quota
etcd 的 Backend Database 有大小限制,常見預設值約為 2 GiB,但實際上可以透過
--quota-backend-bytes調整,因此應以目前叢集設定為準。
etcd 使用 MVCC 保存歷史 Revision。
隨著資料持續更新,舊 Revision 會逐漸累積,因此可以透過 Compaction 移除不再需要的舊歷史版本。
先取得目前 Revision:
rev=$(etcdctl endpoint status -w json | \
python3 -c "import sys,json; print(json.loads(sys.stdin.read())[0]['Status']['header']['revision'])")
執行 Compaction:
etcdctl compact "$rev"
Compaction 只會讓舊 Revision 不再可讀,並不一定會立刻縮小實際的 Backend Database 檔案。
如果要進一步回收碎片空間,可以再執行:
etcdctl defrag

⚠️ Defrag 會造成短暫阻塞
生產環境如果是多 Member etcd Cluster,不要同時對所有 Member 執行 Defrag。
建議逐個 Member 進行,並先確認叢集健康狀態。
💡 etcd 維運重點
- 使用 SSD 或其他低延遲儲存
- etcd Member 之間保持穩定、低延遲的網路
- 定期建立 Snapshot,並實際驗證還原流程
- 監控 DB Size、WAL fsync、Backend commit 與 Leader Election
- 生產環境通常使用 3 或 5 個 etcd Member
今天我們深入了 Kubernetes 的資料核心 —— etcd,從基本概念一路做到資料探索、Snapshot 備份與還原。
| 學到的東西 | 一句話總結 |
|---|---|
| etcd | Kubernetes Control Plane 用來持久化叢集狀態的重要 Key-Value Store |
| Raft 共識演算法 | 透過 Leader 選舉、Log Replication 與 Quorum 維持多節點一致性 |
| 奇數節點原則 | etcd Cluster 通常採奇數 Member,3 或 5 個最常見 |
| etcdctl 探索 | 可以直接查看 etcd 中的 Storage Key 與 Kubernetes 原始儲存內容 |
| 備份與還原 | 使用 etcdctl snapshot save 備份,再透過 etcdutl snapshot restore 還原 |
| 效能與維運 | 低延遲儲存、穩定網路、監控指標與適當的 Compaction / Defrag 都很重要 |
從 Day 27 的 CRD 與 Operator、Day 28 的 cert-manager,到今天的 etcd,我們已經把 Kubernetes API、Controller 與底層狀態儲存之間的關係串了起來。
明天就是系列的最後一天,我們會實戰 Kubernetes 叢集升級,使用 kubeadm 將 Day 01 建立的 v1.34 叢集升級到 v1.35,完整走過一次 Control Plane 與 Worker Node 的升級流程!