⬆️ Upgrade cluster bằng kubeadm và drain node

Cluster của bạn đang chạy v1.30, đề thi yêu cầu nâng lên v1.31. Nghe thì đơn giản, nhưng đây là dạng bài mà người mới hay mất điểm nhất, không phải vì lệnh khó, mà vì sai thứ tự hoặc quên uncordon. Bài này tôi đi qua đúng trình tự tôi vẫn làm trong phòng thi, kèm những chỗ tôi từng làm hỏng cluster lab của mình.

Ôn lại nhanh

Bài trước bạn đã biết cách etcdctl snapshot save và restore. Nhắc lại một câu: backup etcd là bước 0 bắt buộc trước khi upgrade. Nếu kubeadm upgrade apply hỏng giữa chừng và control plane không lên được, snapshot etcd là đường lui duy nhất của bạn.

[!WARNING] Trong môi trường thi thật, bạn thường không cần backup etcd vì cluster lab dùng được ngay. Nhưng nếu đề có yêu cầu backup trước khi upgrade, hãy làm trước, đừng làm sau.

Mục tiêu học tập

Sau bài này, bạn phải làm được:

  1. Đọc đúng kubeadm upgrade plan và xác định phiên bản đích hợp lệ (chỉ nhảy một minor version).
  2. Nâng cấp control plane node đúng thứ tự: kubeadm, apply, drain, kubelet, uncordon.
  3. Nâng cấp từng worker node bằng kubeadm upgrade node và đưa node về trạng thái phục vụ.
  4. Xử lý được 4 lỗi drain/upgrade thường gặp nhất trong phòng thi.

Nguyên tắc không được phá

Nguyên tắc Chi tiết
Thứ tự Control plane trước, worker sau, mỗi lần một node
Số minor Mỗi lần chỉ nâng {{c1::một}} minor version (1.30 → 1.31, không nhảy 1.30 → 1.32)
Version skew kubelet không được mới hơn kube-apiserver, chỉ được cũ hơn tối đa 3 minor; kubectl lệch tối đa 1 minor
Ai nâng gì kubeadm upgrade nâng control plane components và image; kubelet/kubectl do bạn cài bằng package manager
flowchart TD
    Start["🚀 Bắt đầu"] --> Plan["📋 kubeadm upgrade plan"]
    Plan --> CP1["🖥️ Control plane đầu tiên<br/>kubeadm upgrade apply"]
    CP1 --> CPN["🖥️ Control plane còn lại<br/>kubeadm upgrade node"]
    CPN --> W["⚙️ Từng worker node<br/>drain → upgrade node → uncordon"]
    W --> Done["✅ kubectl get nodes"]

Ví dụ mẫu: nâng cấp cụm 1 control plane + 2 worker

Bối cảnh: controlplane, node01, node02 đều đang chạy v1.30.5, mục tiêu là v1.31.4. Bạn đang đứng trên controlplane.

Bước 1: Kiểm tra hiện trạng

kubectl get nodes -o wide
kubeadm version -o short
kubectl version --short

Ghi lại phiên bản hiện tại. Nếu các node lệch nhau sẵn (ví dụ node02 còn 1.29), xử lý node đó trước.

Bước 2: Xem phiên bản có thể cài

apt-get update
apt-cache madison kubeadm | head

Bạn sẽ thấy các dòng kiểu kubeadm | 1.31.4-1.1 | https://pkgs.k8s.io/.... Ghi nhớ chuỗi 1.31.4-1.1, đó là tên package đầy đủ cần dùng.

Bước 3: Nâng kubeadm trên control plane đầu tiên

apt-mark unhold kubeadm
apt-get install -y kubeadm=1.31.4-1.1
apt-mark hold kubeadm
kubeadm version

Nếu package đang bị hold mà bạn quên unhold, apt sẽ báo lỗi và gợi ý --allow-change-held-packages. Cả hai cách đều được.

Bước 4: Xem kế hoạch

kubeadm upgrade plan

Đọc kỹ output: nó liệt kê component nào sẽ lên version nào, và nhắc rằng kubelet phải nâng thủ công. Nếu thấy dòng cảnh báo version skew, dừng lại kiểm tra trước khi apply.

Bước 5: Nâng cấp control plane

kubeadm upgrade apply v1.31.4

Chờ tới dòng [upgrade/successful] SUCCESS!. Lệnh này restart các static pod kube-apiserver, kube-controller-manager, kube-scheduler, etcd bằng image mới. Nếu fail giữa chừng, đọc log, sửa nguyên nhân rồi chạy lại, lệnh này tương đối an toàn khi chạy lại.

Bước 6: Drain control plane node

kubectl drain controlplane --ignore-daemonsets

Bước 7: Nâng kubelet và kubectl

apt-mark unhold kubelet kubectl
apt-get install -y kubelet=1.31.4-1.1 kubectl=1.31.4-1.1
apt-mark hold kubelet kubectl
systemctl daemon-reload
systemctl restart kubelet

Bước 8: Đưa control plane về phục vụ

kubectl uncordon controlplane
kubectl get nodes

controlplane phải hiện v1.31.4 và không còn chữ SchedulingDisabled.

Bước 9: Nâng worker node01

Từ control plane, đẩy pod ra khỏi node:

kubectl drain node01 --ignore-daemonsets --delete-emptydir-data

SSH vào node01 rồi làm 3 việc:

apt-mark unhold kubeadm && apt-get install -y kubeadm=1.31.4-1.1 && apt-mark hold kubeadm
kubeadm upgrade node
apt-mark unhold kubelet kubectl && apt-get install -y kubelet=1.31.4-1.1 kubectl=1.31.4-1.1 && apt-mark hold kubelet kubectl
systemctl daemon-reload && systemctl restart kubelet

Quay lại control plane:

kubectl uncordon node01
kubectl get nodes

Bước 10: Lặp lại với node02 và kiểm tra cuối

kubectl get nodes
kubectl -n kube-system get pods
kubectl get pods -A | grep -v Running

Cả 3 node phải cùng version, mọi pod hệ thống Running, không còn node nào SchedulingDisabled.

[!NOTE] Nếu cụm có nhiều control plane node, các node phụ dùng kubeadm upgrade node (không phải apply), giống worker. Chỉ node control plane đầu tiên mới dùng kubeadm upgrade apply.

Luyện tập có hướng dẫn

Làm lần lượt, đừng mở đáp án trước khi thử. Mỗi bài đều có bước tự kiểm tra.

Bài 1: Đọc hiện trạng và lập kế hoạch

  1. Chạy kubectl get nodes -o wide và ghi ra giấy: tên node, role, version.
  2. Chạy kubectl get node <node> -o jsonpath='{.status.nodeInfo.kubeletVersion}' cho từng node.
  3. Chạy kubeadm upgrade plan và trả lời: version đích là gì, kubelet có tự lên theo không.
Gợi ý và đáp án
  • kubectl get nodes -o wide cho bạn cột VERSION, nhưng dùng jsonpath thì chắc chắn hơn khi đề yêu cầu version chính xác của kubelet.
  • kubeadm upgrade plan in ra bảng các component (kube-apiserver, kube-controller-manager, kube-scheduler, kube-proxy, CoreDNS) và version đích.
  • kubelet không tự lên. Bạn phải cài package và restart kubelet thủ công.
  • Nếu output có dòng Unable to check for upgrade hoặc lỗi mạng, kiểm tra repo pkgs.k8s.io trong /etc/apt/sources.list.d/.

Bài 2: Drain an toàn một worker node

  1. Chạy thử trước: kubectl drain node01 --ignore-daemonsets --delete-emptydir-data --dry-run=client.
  2. Chạy thật, rồi kiểm tra trạng thái node.
  3. Kiểm tra pod đã được chuyển sang node khác chưa.
kubectl get nodes
kubectl get pods -A -o wide | grep node01
Gợi ý và đáp án
  • Sau khi drain, node hiện SchedulingDisabled trong cột STATUS, đó là bình thường.
  • Pod còn lại trên node chỉ nên là DaemonSet pod (log agent, kube-proxy) và static pod nếu có.
  • Nếu drain báo cannot delete Pods with local storage, thêm --delete-emptydir-data.
  • Nếu drain báo cannot delete DaemonSet-managed Pods, thêm --ignore-daemonsets.
  • Nếu có pod "trần" (không do controller quản lý), drain sẽ chặn, dùng --force sau khi chắc chắn pod đó bỏ được.

Bài 3: Nâng cấp control plane node

Thực hiện Bước 3 đến Bước 8 ở ví dụ mẫu trên cluster lab của bạn. Tự kiểm tra bằng:

kubectl get nodes
kubectl -n kube-system get pods
Gợi ý và đáp án
  • Sau kubeadm upgrade apply, các static pod trong kube-system sẽ restart, đợi 1 đến 2 phút rồi mới kết luận là lỗi.
  • Nếu kubectl get nodes vẫn hiện version cũ cho control plane, gần như chắc chắn bạn quên systemctl restart kubelet.
  • Kiểm tra nhanh image đang chạy: kubectl -n kube-system get pod kube-apiserver-controlplane -o jsonpath='{.spec.containers[0].image}'.

Bài 4: Nâng cấp worker node và uncordon

Làm đủ 3 bước trên node01: drain từ control plane, kubeadm upgrade node + cài kubelet trên node, uncordon từ control plane.

Gợi ý và đáp án
  • Nhớ systemctl daemon-reload trước restart kubelet, thiếu bước này kubelet có thể vẫn dùng binary cũ.
  • Trên worker không bắt buộc cài kubectl, nhưng trong đề thi cứ cài cho đồng bộ.
  • Sau uncordon, chạy kubectl get nodes tới khi node trả về Ready và version mới.

Bài 5: Tình huống lỗi tổng hợp

Cluster lab của bạn đang ở trạng thái: node02 hiện SchedulingDisabled, version vẫn 1.30.5, và kubectl drain node02 --ignore-daemonsets báo lỗi về local storage. Hãy đưa node02 lên 1.31.4 và trở lại phục vụ.

Gợi ý và đáp án

Trình tự đúng:

  1. kubectl drain node02 --ignore-daemonsets --delete-emptydir-data (thêm flag còn thiếu).
  2. SSH vào node02, nâng kubeadm, chạy kubeadm upgrade node.
  3. Nâng kubelet và kubectl, sau đó systemctl daemon-reload && systemctl restart kubelet.
  4. kubectl uncordon node02.
  5. Kiểm tra kubectl get nodes. Node phải Ready và v1.31.4, không còn SchedulingDisabled.

Lỗi hay gặp ở bài này: drain xong nhưng quên uncordon, hoặc uncordon sai node.

Lỗi thường gặp và cách xử lý

Triệu chứng Nguyên nhân Cách sửa
cannot delete DaemonSet-managed Pods Thiếu flag Thêm --ignore-daemonsets
cannot delete Pods with local storage Pod dùng emptyDir Thêm --delete-emptydir-data
Drain treo ở pod không có controller Pod "trần" Thêm --force
Node cứ ở SchedulingDisabled Chưa uncordon kubectl uncordon <node>
kubectl get nodes vẫn version cũ Chưa restart kubelet hoặc chưa cài package systemctl daemon-reload && systemctl restart kubelet
kubeadm upgrade apply báo version skew Nhảy quá một minor, hoặc kubelet mới hơn apiserver Nâng tuần tự từng minor, control plane trước
Node NotReady sau upgrade kubelet không khởi động được systemctl status kubelet, journalctl -u kubelet -n 50
kubeadm upgrade plan lỗi kết nối Repo package chưa đúng Kiểm tra file trong /etc/apt/sources.list.d/

[!TIP] Trước khi drain thật, luôn chạy kubectl drain <node> --dry-run=client để biết ngay thiếu flag nào. Mất 5 giây, tiết kiệm vài phút.

Góc chuyên gia

Vài thứ tôi ước mình biết trước khi thi:

  • Học thuộc 3 lệnh, không học thuộc 30 lệnh: kubeadm upgrade plan, kubeadm upgrade apply, kubeadm upgrade node. Chỉ vậy thôi. Phần còn lại là apt-get install và kubectl drain/uncordon.
  • Đề cho sẵn repo package. Đừng mất thời gian cấu hình pkgs.k8s.io, chỉ cần apt-cache madison kubeadm là thấy version.
  • apt-mark hold là bạn của bạn. Nếu bạn từng hold kubelet kubeadm kubectl để tránh apt upgrade tự nâng, nhớ unhold trước khi cài.
  • Luôn kết thúc bằng kubectl get nodes. Đây là bước chấm điểm thực tế: node đúng version và không SchedulingDisabled.
  • Đừng drain cả 3 node cùng lúc. Nếu cluster chỉ có 1 worker và bạn drain nó, pod hệ thống có thể không có chỗ chạy.
  • Thời gian hợp lý: đề 1 control plane + 2 worker nên xong trong 10 đến 12 phút. Nếu quá 15 phút, kiểm tra xem có đang drain nhầm node hay đợi pod eviction quá lâu (--timeout=60s).
  • Nếu bị kẹt ở kubeadm upgrade apply, đọc dòng log cuối cùng. Phần lớn lỗi là do etcd không healthy hoặc certificate hết hạn, cả hai đều đã học ở bài trước.

Thẻ ôn tập nhanh

[
  { "front": "Thứ tự nâng cấp chuẩn của một cluster kubeadm?", "back": "Control plane node đầu tiên, rồi các control plane node còn lại, cuối cùng là từng worker node." },
  { "front": "Lệnh nâng cấp control plane node đầu tiên?", "back": "kubeadm upgrade apply v1.31.x" },
  { "front": "Lệnh nâng cấp một worker node hoặc control plane node phụ?", "back": "kubeadm upgrade node" },
  { "front": "Trước khi nâng cấp kubelet trên một node phải làm gì với pod?", "back": "kubectl drain node đó để đẩy pod sang node khác." },
  { "front": "Flag bắt buộc khi drain node có DaemonSet?", "back": "--ignore-daemonsets" },
  { "front": "Flag cần thêm khi pod trên node dùng emptyDir?", "back": "--delete-emptydir-data" },
  { "front": "Sau khi nâng cấp xong trên node, lệnh nào đưa node nhận pod trở lại?", "back": "kubectl uncordon <node>" },
  { "front": "kubectl cordon khác kubectl drain ở điểm nào?", "back": "cordon chỉ đánh dấu SchedulingDisabled; drain cordon rồi evict pod ra khỏi node." },
  { "front": "Có được nhảy từ 1.29 lên 1.31 trong một lần không?", "back": "Không, mỗi lần chỉ nâng một minor version, phải làm hai lần." },
  { "front": "Thành phần nào không được nâng cấp bởi kubeadm upgrade apply?", "back": "kubelet và kubectl, phải cài bằng package manager rồi restart kubelet." },
  { "front": "Lệnh xem kế hoạch nâng cấp trước khi thực hiện?", "back": "kubeadm upgrade plan" },
  { "front": "Sau khi cài kubelet bản mới cần chạy gì để kubelet dùng binary mới?", "back": "systemctl daemon-reload && systemctl restart kubelet" },
  { "front": "Quy tắc version skew giữa kubelet và kube-apiserver?", "back": "kubelet không được mới hơn kube-apiserver, chỉ được cũ hơn tối đa 3 minor version." },
  { "front": "Node hiển thị SchedulingDisabled nghĩa là gì?", "back": "Node đang bị cordon, không nhận pod mới cho tới khi uncordon." }
]

Kiểm tra nhanh bằng cloze

Điền từ còn thiếu:

  1. Để đẩy pod ra khỏi node trước khi nâng cấp, ta dùng kubectl {{c2::drain}}.
  2. Control plane node đầu tiên dùng kubeadm upgrade {{c3::apply}}.
  3. Các node còn lại dùng kubeadm upgrade {{c4::node}}.
  4. Sau khi nâng cấp xong, phải chạy kubectl {{c5::uncordon}} để node nhận pod trở lại.
  5. Node có DaemonSet cần thêm flag --{{c6::ignore-daemonsets}} khi drain.

Tóm tắt và bước tiếp theo

Tóm gọn trong 5 dòng:

  1. kubeadm upgrade plan trước, kubeadm upgrade apply cho control plane đầu tiên, kubeadm upgrade node cho phần còn lại.
  2. Drain trước khi chạm vào kubelet, uncordon sau khi xong. Không bao giờ bỏ qua bước cuối.
  3. kubeadm lo control plane, kubelet và kubectl do bạn cài tay.
  4. Mỗi lần một minor version, control plane luôn đi trước.
  5. Kết thúc bằng kubectl get nodes: đúng version, Ready, không SchedulingDisabled.

Bài tiếp theo là Checkpoint: Cluster architecture và upgrade, một bài tổng hợp gồm dựng cluster bằng kubeadm, backup/restore etcd và nâng cấp cluster, đúng dạng bài bạn sẽ gặp trong phần 25% của đề CKA. Trước khi sang đó, hãy tự chạy lại Bài 5 mà không mở đáp án.