Phần trước chụp snapshot. Bài này thật sự khôi phục từ nó — trên một cụm dựng riêng để phá — và đo từng bước.

Dòng thời gian khôi phục, cái bẫy thiếu etcdutl, và kết quả hai chiều

Chuẩn bị: đặt dấu mốc

Trước khi chụp, tôi tạo namespace truoc với một Deployment 2 bản, một ConfigMap ghi khi=truoc-snapshot, và một Secret mk=abc123 — tổng 7 đối tượng.

Snapshot: 1.134.624 byte.

Sau khi chụp, tôi phá: xoá truoc, tạo namespace sau với một ConfigMap ghi khi=sau-snapshot.

Hai dấu mốc này trả lời hai câu hỏi khác nhau: cái mất có về không, và cái mới có mất không.

Dòng thời gian

t =  0,1 s   chuyển kube-apiserver.yaml và etcd.yaml ra khỏi manifests/
t = 30,5 s   xác nhận API đã chết
t = ~60  s   etcdutl snapshot restore -> thư mục dữ liệu mới
t = 71,0 s   trả hai manifest về chỗ cũ
t = 77,0 s   API SỐNG LẠI

77 giây cho toàn bộ quy trình trên một cụm một node.

Cách dừng control plane đáng nói riêng: không có systemctl stop kube-apiserver vì nó không phải dịch vụ hệ thống. Nó là static pod, và kubelet tạo nó từ tệp trong /etc/kubernetes/manifests/. Chuyển tệp đi là kubelet xoá pod; chuyển về là nó tạo lại. Đó là nút bật/tắt duy nhất.

Kubelet quét thư mục đó theo chu kỳ, nên có độ trễ — đo được API vẫn còn sống ở giây đầu và đã chết khi kiểm tra ở giây 30,5.

Cái bẫy làm hỏng cả quy trình

ls /usr/local/bin/etcdutl  ->  No such file or directory

etcdctletcdutl không có trên node. Chúng nằm trong ảnh container etcd — tức là đúng cái container bạn vừa tắt ở bước 1.

Ở phần trước tôi chạy etcdctl bằng kubectl exec vào pod etcd. Bây giờ pod đó không còn, và API server cũng không còn để nhận lệnh exec.

Lối ra:

docker run --rm --volumes-from rs-control-plane \
  --entrypoint etcdutl registry.k8s.io/etcd:3.6.5-0 \
  snapshot restore /var/lib/etcd/moc.db --data-dir /var/lib/etcd-moi

Chạy chính ảnh etcd như một container rời, gắn cùng volume với node. Trên cụm thật (không phải kind) thì tương đương là cài gói etcd-client lên node, hoặc ctr run từ ảnh đã có sẵn trên node.

Đây là kiểu trở ngại mà không ai gặp cho tới lúc thật sự khôi phục — và lúc đó thì đang mất dịch vụ. Lý do nên diễn tập trước, không phải để nhớ các bước mà để phát hiện những thứ như thế này.

Một chi tiết nữa: thư mục khôi phục ra 63 MB từ một snapshot 1,1 MB. etcd cấp phát sẵn WAL và ánh xạ bộ nhớ ban đầu. Đừng nhìn con số đó rồi tưởng có gì sai.

Bốn bước, viết gọn

# 1. Dừng API server và etcd trên MỌI node control plane
mkdir -p /tmp/mf
mv /etc/kubernetes/manifests/{kube-apiserver,etcd}.yaml /tmp/mf/

# 2. Khôi phục ra thư mục MỚI (đừng ghi đè cái cũ)
etcdutl snapshot restore /duong/dan/snap.db --data-dir /var/lib/etcd-moi

# 3. Đổi chỗ
mv /var/lib/etcd /var/lib/etcd-cu
mv /var/lib/etcd-moi /var/lib/etcd

# 4. Trả manifest về
mv /tmp/mf/*.yaml /etc/kubernetes/manifests/

Bước 2 và 3 tách nhau là có lý do: giữ lại thư mục cũ. Khôi phục hỏng thì còn đường lùi. Đổi tên tốn không tới một giây, còn ghi đè thẳng thì mất trạng thái hiện tại vĩnh viễn — kể cả những thay đổi bạn vừa nhận ra là muốn giữ.

Với cụm nhiều node control plane, mỗi node phải khôi phục cùng snapshot đó và mỗi node cần --initial-cluster khai lại đúng danh sách thành viên. Dừng tất cả trước rồi mới khôi phục — để một node etcd sống sót là nó sẽ nhân bản trạng thái cũ đè lên bản vừa khôi phục.

Kết quả: cỗ máy thời gian cho cả cụm

Cái tạo trước khi chụp — về đủ:

ns truoc                     khôi phục
7 đối tượng                  khôi phục
configmap dau-moc  = truoc-snapshot
secret bimat       = abc123

Secret giải mã ra đúng abc123. Deployment tự dựng lại pod, cụm về Ready sau khoảng hai phút.

Cái tạo sau khi chụp — mất sạch:

kubectl get ns sau
-> Error from server (NotFound): namespaces "sau" not found

Không có cảnh báo, không có bản ghi. Namespace đó chưa từng tồn tại theo mọi thứ cụm còn nhớ.

Đây là điều quan trọng nhất cần hiểu về khôi phục etcd: nó không chọn lọc được. Khôi phục để cứu một namespace bị xoá nhầm đồng nghĩa vứt bỏ mọi thay đổi kể từ lúc chụp — mọi deploy, mọi Secret mới, mọi thứ đội khác vừa làm.

Với sự cố "xoá nhầm một thứ", cách đúng gần như luôn là khôi phục có chọn lọc từ bản xuất YAML hoặc từ Velero. Snapshot etcd để dành cho sự cố "cả control plane hỏng".

Ba thứ snapshot không mang về

Dữ liệu trong volume. Đã đo ở phần trước: 50 MB dữ liệu làm snapshot tăng 0,26%. Khôi phục xong PVC trỏ vào PV rỗng.

Trạng thái thật của node. etcd nhớ pod nào nên chạy ở đâu. Nếu node đã bị thay giữa lúc chụp và lúc khôi phục thì mọi pod trên node đó phải xếp lịch lại.

Chứng chỉ. /etc/kubernetes/pki không nằm trong etcd. Mất nó thì có snapshot cũng không dựng lại được control plane — sao lưu riêng, nó chỉ vài chục KB.

Thử ba mươi giây

Đây là bài duy nhất trong loạt mà phần thử không nên chạy trên cụm thật. Hãy dựng một cụm bỏ đi:

kind create cluster --name dien-tap
kubectl create ns truoc
kubectl create configmap moc -n truoc --from-literal=khi=truoc
# chụp snapshot theo phần 49, rồi:
kubectl delete ns truoc
# ... khôi phục theo bốn bước ở trên ...
kubectl get cm -n truoc moc -o jsonpath='{.data.khi}'
kind delete cluster --name dien-tap

Mười lăm phút. Cái bạn thu được không phải là các bước — chúng nằm sẵn trong tài liệu — mà là danh sách những thứ thiếu trên node của bạn, và cảm giác biết rằng lệnh đó thật sự chạy được.

Phần sau: nâng cấp cụm — đo thứ tự đúng và chỗ thường vỡ.