終於來到倒數兩天啦!
辛苦各位了
之前我們 Day 3 時.認識 Control Plane 時,我們曾經把 etcd 形容成:
etcd
= Kubernetes 的記憶
今天要真的把這份「記憶」給備份下來。
這件事和備份一般 Application 不太一樣。
當 Pod 掛掉時,Deployment 通常可以重新建立;
當 Node 掛掉,Workload 也可能被重新排程。
但如果 etcd 裡面的 Cluster State 整個毀損,而且沒有 Backup,Kubernetes 就可能連「Cluster 原本應該長什麼樣子」都不知道。
所以 etcd Backup / Restore 是 Cluster Administrator 很重要的一項能力。
etcd 是一套 Distributed Key-Value Store(分散式 Key-Value 資料庫)。
你不需要把它想成 MySQL 那種資料庫,可以先把它理解成 Kubernetes 專門保存 Cluster State 的地方。
例如我們建立:
Deployment
Service
ConfigMap
Secret
Node
Role
RoleBinding
CRD
...
這些 Kubernetes API Object 的狀態,最終都會由 Control Plane 保存到 etcd。
概念上可以想成:
kubectl apply
↓
Kubernetes API Server
↓
etcd
↓
記住 Cluster 應該長什麼樣子
所以:
Application Pod 掛掉
和:
etcd 資料整個毀損
是完全不同層級的事故。
前者通常是 Workload Failure;後者則可能是 Cluster State Disaster。
而今天會一直看到一個新名詞:
Snapshot
Snapshot 可以理解成:
就是 etcd 的存檔點 (savepoint),完整凍結某個時間點 整個 k8s 叢集的所有狀態宣告。
Kubernetes 的架構分為 Control Plane 與 Worker Nodes。etcd 是整個叢集唯一的「單一真實數據來源(Single Source of Truth)」。
再次看這張圖,你會更好理解
Source: https://aws.plainenglish.io/kubernetes-architecture-c93cb9c798d8
kubectl apply指令、建立的 Pod、Service、ConfigMap、Secret,全部以 Key-Value 形式存放在 etcd 裡。用時間軸來看 Restore 的前後變化:
Deployment A、Service A、ConfigMap A
Deployment B
A + B
Deployment A,完全沒有 Deployment B 的紀錄。Deployment B Pod。做 Restore Lab 時,一定要分清楚 「Cluster State」 與 「實際應用資料」:
| 項目 | Snapshot 有包含嗎? | Restore 後的狀態 |
|---|---|---|
| K8s 物件設定 (Deployment / Service / Secret) | 有 | 完全回到 Snapshot 當下設定 |
| 容器鏡像與規格 | 有 | 回到當下指定的 Image Tag |
| PV / PVC 宣告 | 有 | 保留當時的 Volume 繫結宣告 |
| PVC 裡儲存的實體檔案 (如 MySQL 磁碟資料) | 沒有 | 不會倒回!實體硬碟資料需依賴 Storage 的備份機制(如 CSI Snapshot、EBS 備份) |
實驗核心目標:
稍後的 Restore Lab,就是要驗證「把 etcd 倒回過去某個時間點後,API Server 與 Controller 能否自動感知差異,將多出來的資源砍掉、少掉的補齊,精準回到備份當時的期望狀態」。
我們目前的 kind Cluster 是:
cka-lab
先找 etcd:
kubectl get pods \
-n kube-system \
-l component=etcd
應該會看到類似:
你可能會疑惑:
etcd 怎麼也是一個 Pod?
因為我們這套 kind Cluster 底層是由 kubeadm 建立 Control Plane,而 kubeadm 預設會把 etcd 建立成 Static Pod。
也就是之前學過的:
/etc/kubernetes/manifests/etcd.yaml
只要放在這個預設的 file 裡面的yaml,
就直接由 kubelet 建立和管理。
我們先把 Pod Name 存起來,後面 Command 比較好用:
ETCD_POD=$(
kubectl get pods \
-n kube-system \
-l component=etcd \
-o jsonpath='{.items[0].metadata.name}'
)
確認:
echo "$ETCD_POD"
看到:
等等連線到 etcd 時,會看到:
https://127.0.0.1:2379
以及:
CA Certificate
Client Certificate
Private Key
所以在繼續之前,要先知道一個新名詞:
TLS
TLS 全名是:
Transport Layer Security

它是一種保護網路連線的安全機制。
平常我們瀏覽網站時看到:
https://
背後通常就是使用 TLS。
TLS 最主要做兩件事情:
第一:把傳輸中的資料加密
第二:驗證連線對象的身分
例如今天:
etcdctl
↓
TLS
↓
etcd Server
我們不希望任何人只要知道:
127.0.0.1:2379
就可以直接讀取或修改 Kubernetes 的 Cluster State。
因此 kubeadm 建立的 etcd 預設會使用憑證來保護連線。
這時就會出現三個東西:
CA Certificate
Client Certificate
Private Key
先不用把 PKI 想得太複雜,可以這樣理解。
CA Certificate 像是:
可信任的發證單位名冊
Client 可以利用 CA Certificate 確認:
我現在連到的 etcd Server
真的是可信任的 Server 嗎?
而:
Client Certificate
+
Private Key
則反過來讓 etcd Server 確認:
現在來連我的 Client
有沒有合法身分?
所以這不是只有:
Client 驗證 Server
而是 etcd 常見的:
雙向 TLS
Mutual TLS
mTLS
概念上可以畫成:
etcdctl
│
│ ① 我先確認你是不是合法的 etcd Server
│
│ ② etcd 也確認我是不是合法 Client
▼
etcd Server
因此等等執行:
--cacert=...
--cert=...
--key=...
並不是多餘的參數,而是在完成這套 TLS 身分驗證。
etcd 的 Client API 通常使用:
2379
但我們不是直接:
http://127.0.0.1:2379
裸連,而是:
https://127.0.0.1:2379
因為 kubeadm 建立的 etcd 預設使用 TLS 保護連線。
kubeadm 產生的 etcd 憑證通常放在:
/etc/kubernetes/pki/etcd/
在我們目前的 kind 環境中,可以直接到 Control Plane Node 查看:
docker exec \
cka-lab-control-plane \
ls -l /etc/kubernetes/pki/etcd/
會看到類似:
ca.crt
healthcheck-client.crt
healthcheck-client.key
peer.crt
peer.key
server.crt
server.key

這次主要會用:
ca.crt
healthcheck-client.crt
healthcheck-client.key
其中:
ca.crt
用來確認 etcd Server 的憑證可信任。
而:
healthcheck-client.crt
healthcheck-client.key
則是 etcdctl 用來向 etcd Server 證明自己的身分。
簡化來看:
etcdctl
│
│ TLS
▼
etcd :2379
雙方不是誰都能隨便連線。
所以等等看到:
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key
就可以理解成:
--cacert
我要確認 etcd Server 是真的
--cert + --key
我要向 etcd Server 證明我是合法 Client
[ etcdctl 指令 ] [ etcd 資料庫 (:2379) ]
│ │
│ ① 敲門:「我要連線!」 │
│ ───────────────────────────────────────────> │
│ │
│ ②「我是 etcd,這是我的身分證 (server.crt)」 │
│ <─────────────────────────────────────────── │
(用 ca.crt 檢查) │
「驗證通過,你真的是官方 etcd!」 │
│ │
│ ③「換我出示證件 (client.crt + client.key)」 │
│ ───────────────────────────────────────────> │
│ (用 ca.crt 檢查)
│ 「證件是真的,指紋也吻合!」
│ │
│ ④ 建立加密連線通道 (TLS) │
│ <══════════════════════════════════════════> │
│ │
│ ⑤ 安全傳送資料:「把 Snapshot 備份給我」 │
接著真正 Backup。
執行:
kubectl exec \
-n kube-system \
"$ETCD_POD" \
-- \
etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
snapshot save /var/lib/etcd/backup.db
成功後通常會看到類似:
Snapshot saved at /var/lib/etcd/backup.db

這條 Command 雖然看起來很長,其實只是在做:
etcdctl
↓
透過 TLS 連線到 etcd
↓
取得目前資料
↓
建立 backup.db
其中:
--endpoints=https://127.0.0.1:2379
代表:
我要連哪一台 etcd Server?
因為 Command 是直接在 etcd Pod 裡執行,所以可以連:
127.0.0.1:2379
接著:
--cacert
用來驗證 etcd Server Certificate。
而:
--cert
--key
則提供 Client Certificate 與 Private Key,讓 etcd 確認這個 Client 的身分。
最後:
snapshot save /var/lib/etcd/backup.db
才是真正把 Snapshot 寫成:
backup.db
這一段最容易搞混,是因為目前其實同時存在三個不同層級:
第一層:你的 Mac
第二層:kind 建立的 Kubernetes Node
第三層:Node 裡運行的 etcd Pod

先把它們一個一個拆開。
最外層就是你現在使用的 Mac。
你平常執行:
docker ps
kubectl get pods
kind get clusters
全部都是從 Mac 上操作。
一般真正的 Kubernetes 環境中,Node 可能是一台:
實體 Server
Virtual Machine
Cloud VM
但是 kind 是專門拿來在本機建立 Kubernetes Cluster 的工具。
kind 的全名其實就是:
Kubernetes IN Docker
它的做法非常特別:
直接用 Docker Container 模擬 Kubernetes Node。
所以你的:
cka-lab-control-plane
雖然從 Kubernetes 的角度來看是一台:
Control Plane Node
但從 Mac 的角度來看,它其實只是一個:
Docker Container
你可以在 Mac 上執行:
docker ps
通常就會看到:
cka-lab-control-plane
cka-lab-worker
cka-lab-worker2
也就是:
Mac
│
├── Docker Container:cka-lab-control-plane
├── Docker Container:cka-lab-worker
└── Docker Container:cka-lab-worker2
這些 Docker Container 對 Kubernetes 來說,就是 Node。
接著,在:
cka-lab-control-plane
這台 Node 裡面,又運行著:
kube-apiserver
kube-scheduler
kube-controller-manager
etcd
這些 Control Plane 元件。
其中 etcd 是:
Static Pod
也就是 kubelet 根據:
/etc/kubernetes/manifests/etcd.yaml
直接建立出來的 Pod。
因此整個關係其實是:
你的 Mac
│
└── Docker Container
cka-lab-control-plane
│
└── Kubernetes Pod
etcd-cka-lab-control-plane
可以理解成:
Mac
包著
kind Node
包著
etcd Pod
剛才我們執行:
etcdctl snapshot save /var/lib/etcd/backup.db
這個 Command 是在:
etcd Pod
裡面執行的。
所以直覺上你可能會認為:
backup.db
只存在 etcd Pod 裡
但這裡有一個非常重要的 Kubernetes 概念:
HostPath
HostPath 是一種 Volume。
它的作用是:
把 Node 上某個真正的資料夾,直接掛進 Pod 裡使用。
etcd Static Pod 就有做這件事情。
它會把 Control Plane Node 上面的:
/var/lib/etcd
掛進 etcd Pod 裡面。
概念上是:
Control Plane Node
/var/lib/etcd
│
│ HostPath
▼
etcd Pod
/var/lib/etcd
注意:
兩邊剛好都叫:
/var/lib/etcd
所以特別容易誤會。
但它們其實分別是:
Node 裡的 /var/lib/etcd
以及:
Pod 裡的 /var/lib/etcd
只是透過 HostPath 連在一起。
當我們在 etcd Pod 裡執行:
etcdctl snapshot save /var/lib/etcd/backup.db (這邊我們指的是Node的)
看起來是寫進:
Pod
/var/lib/etcd/backup.db
但因為:
/var/lib/etcd
其實是從 Node 掛載進來的,所以資料實際上也會寫到:
Node
/var/lib/etcd/backup.db
也就是:
etcd Pod
/var/lib/etcd/backup.db
│
│ 寫入 HostPath
▼
Control Plane Node
/var/lib/etcd/backup.db
所以不是:
Pod 裡有一份
Node 裡又複製一份
而是:
Pod 看到的這個目錄,本來就是 Node 的目錄。
可以把它想像成 Mac 上把一個外接硬碟掛進某個資料夾。
你在那個資料夾裡新增檔案,實際資料其實寫在外接硬碟。
HostPath 的概念也很接近。
前面又提到:
kind Node
本身就是 Docker Container
所以現在完整結構就變成:
你的 Mac
│
│
└── Docker Container
cka-lab-control-plane
│
│
├── /var/lib/etcd/ (Control Plane Node 層級的)
│ │
│ └── backup.db
│
│ ▲
│ │ HostPath
│ │
└── etcd Static Pod
│
└── /var/lib/etcd/ (etcd Pod 自己的)
│
└── backup.db
真正最重要的是這段:
etcd Pod
/var/lib/etcd
│
│ HostPath
▼
kind Control Plane Node
/var/lib/etcd
所以剛才 Snapshot 雖然是從 etcd Pod 裡建立,但檔案其實已經存在:
cka-lab-control-plane
這個 kind Node 裡。
因為目前:
backup.db
還是在:
cka-lab-control-plane
裡。
但不要忘記:
cka-lab-control-plane
從 Mac 的角度來看,只是一個 Docker Container。
所以最後只要使用:
docker cp \
cka-lab-control-plane:/var/lib/etcd/backup.db \
./backup.db

意思就是:
從 Docker Container:
cka-lab-control-plane
裡面的:
/var/lib/etcd/backup.db
Copy 到:
我的 Mac 目前目錄
./backup.db
流程就會變成:
etcd Pod
│
│ snapshot save (在某時間點存檔)
▼
/var/lib/etcd/backup.db (etcd Pod)
│
│ HostPath
▼
kind Control Plane Node
/var/lib/etcd/backup.db
│
│ docker cp
▼
你的 Mac
./backup.db
這樣就很清楚了。
最後可以執行:
ls -lh backup.db

如果真的看到:
backup.db
就代表 Snapshot 已經成功從 Kubernetes Cluster 裡搬到你的 Mac。
第一:
kind 的 Node
其實是 Docker Container
第二:
etcd Pod 的 /var/lib/etcd
透過 HostPath 使用 Control Plane Node 的 /var/lib/etcd
所以:
etcd Pod 建立 backup.db
↓
檔案其實最後是寫在 Control Plane Node Storage
↓
再用 docker cp
↓
把 Snapshot 搬到 Mac
這就是為什麼我們最後可以從:
cka-lab-control-plane:/var/lib/etcd/backup.db
直接把 Snapshot Copy 出來。
剛剛執行的:
docker cp \
cka-lab-control-plane:/var/lib/etcd/backup.db \
./backup.db
再確認:
ls -lh backup.db
現在你的專案目錄應該真的會出現:
backup.db
這一步其實非常重要。
因為如果你說:
我有 Backup
結果 Backup 還放在:
同一台 Control Plane Node
那今天整台 Node Disk 壞掉時:
etcd 沒了
Backup 也一起沒了
這就不是一個很可靠的 Backup Strategy。
Production 通常還需要把 Snapshot 搬到獨立 Storage,例如 Object Storage 或其他受保護的 Backup System。
接下來會出現第二個工具:
etcdutl
它和:
etcdctl
名字非常像,但用途不同。
可以先用一句話記:
etcdctl
=跟正在運作的 etcd Server 溝通
etcdutl
=處理離線的 etcd Data / Snapshot
所以剛才:
snapshot save
需要向正在 Running 的 etcd 取得資料,因此使用:
etcdctl (跟運作中的溝通)
但是接下來:
snapshot status
snapshot restore
是在處理已經存在 Disk 上面的 Snapshot File,因此新版 etcd 使用:
etcdutl (跟離線的做溝通)
這個分工非常重要。
我們不只要確認:
backup.db 存在
還要確認 etcd 工具真的讀得懂它。
為了避免 etcd 版本不同,我們直接取得目前 Cluster 使用的 etcd Image:
ETCD_IMAGE=$(
kubectl get pod \
"$ETCD_POD" \
-n kube-system \
-o jsonpath='{.spec.containers[0].image}'
)
確認:
echo "$ETCD_IMAGE"
可能會看到:
registry.k8s.io/etcd:...

接著直接使用同版本 Image 裡面的 etcdutl:
注意看.用的是 etcdutl !(跟離線的做溝通)
docker run \
--rm \
--entrypoint=etcdutl \
-v "$PWD:/backup" \
"$ETCD_IMAGE" \
snapshot status \
/backup/backup.db \
-w table
其中:
-v "$PWD:/backup"
代表把 Mac 目前這個資料夾:
$PWD
掛進 Temporary Container 的:
/backup
所以 Container 才能讀到剛才建立的:
backup.db
成功後會看到類似:
HASH REVISION TOTAL KEYS TOTAL SIZE
xxxxxxxx 12345 1500 5.2 MB

REVISION 可以先理解成 etcd 資料變更到哪一個版本;TOTAL KEYS 是 Snapshot 裡面有多少 Key;TOTAL SIZE 則是整份 Snapshot 大小。
最重要的是:
etcdutl 可以正常讀取這份 Snapshot。
Restore 就是:
根據 Snapshot,重新建立一份 etcd Data Directory。
今天先不要直接動正在使用中的:
/var/lib/etcd
而是類似解壓縮概念到另一個 Directory,這樣 etcd 才可以真的使用
執行:
docker run \
--rm \
--entrypoint=etcdutl \
-v "$PWD:/backup" \
"$ETCD_IMAGE" \
snapshot restore \
/backup/backup.db \
--data-dir=/backup/etcd-restored
完成後:
ls
應該會看到:
backup.db
etcd-restored/

這裡一定要理解:
backup.db
是:
Snapshot File。
而:
etcd-restored/
則是:
根據 Snapshot 重新建立出來,可以給 etcd 使用的 Data Directory。
關係是:
backup.db
│
│ etcdutl snapshot restore
▼
etcd-restored/
所以 Restore 並不是把:
backup.db
直接丟給 etcd 啟動,而是先把 Snapshot 轉換成可以使用的 etcd Data Directory。
backup.db 就像是一個 .zip 壓縮檔。你不能直接在 .zip 裡面執行軟體。etcd-restored/ 則是把 .zip 解壓縮後得到的 完整安裝資料夾(裡面包含 WAL 日誌、member 結構、db 引擎檔案)。今天做到:
利用 Snapshot,產生了 backup.db
↓
利用 Restore,產生 etcd 可使用的資料型態
↓
新的 etcd Data Directory
但還沒有真的叫目前這個 Cluster 改用它。
真正 Disaster Recovery 大致上會變成:
停止會持續操作 etcd 的 Control Plane 元件
↓
準備 Snapshot
↓
etcdutl snapshot restore
↓
產生新的 Data Directory
↓
讓 etcd Static Pod 改用新的 Data Directory
↓
重新啟動 Control Plane
↓
驗證 Kubernetes Cluster State
例如 kubeadm 建立的:
/etc/kubernetes/manifests/etcd.yaml
通常會有:
volumes:
- hostPath:
path: /var/lib/etcd
name: etcd-data
假設真正 Restore 到 Node 裡:
/var/lib/etcd-from-backup
那就可能需要把 HostPath 改成:
volumes:
- hostPath:
path: /var/lib/etcd-from-backup
name: etcd-data
注意,Container 裡面的 mountPath 仍然可以維持:
/var/lib/etcd
只是背後實際掛進來的 Node Directory 已經換成 Restore 後的資料。
Static Pod Manifest 一改,kubelet 就會重新建立 etcd Pod。
這才是真正把 Kubernetes 的 Cluster State 切回 Snapshot。
今天我們學了:
etcdctl snapshot save
但 Production Backup 真正需要考慮的事情遠遠不只:
Command 有沒有成功。
至少要問:
尤其 etcd Snapshot 裡可能包含 Kubernetes Secret 與其他敏感的 Cluster State,所以 Backup File 本身也必須被當成敏感資料保護。
一份:
每天都有成功產生 backup.db
但從來沒有真正 Restore 過的 Backup,發生 Disaster 時不一定真的救得回來。
所以真正的 Backup Strategy 應該是:
Backup
+
Off-site Storage
+
Security
+
Retention
+
Restore Test
缺一不可。
這幾天其實已經開始從:
Application Engineer
慢慢往:
Cluster Administrator
的角度思考問題。
前面遇到:
Application Failure
↓
Pod / Deployment
接著學到:
Node Failure
↓
kubelet / Container Runtime
再往上是:
Control Plane Failure
↓
Static Pod
今天則來到最底層:
Cluster State Disaster
↓
etcd Backup / Restore
現在你應該不只是知道:
etcd 是 Kubernetes 的記憶
而是更完整地理解:
Kubernetes API Object
↓
etcd
↓
snapshot save
↓
backup.db
↓
snapshot restore
↓
新的 etcd Data Directory
↓
重新提供給 Control Plane
↓
恢復 Cluster State
這才是 etcd Backup / Restore 真正在做的事情。
明天就是最後一天。
Day 30 不再繼續塞新的 Kubernetes 名詞,而是要把前面 29 天學過的:
Workload
Networking
Storage
Security
Scheduling
Observability
Troubleshooting
Control Plane
Backup
全部串起來。
最後再把這套 Lab 從:
CKA 練習環境
真正整理成:
可以放上 GitHub 的 Kubernetes / DevOps Project
讓這 30 天不只是「做完練習」,而是真的留下一個可以展示自己 Kubernetes 能力的完整專案。
我們明天見!