Troubleshoot node NotReady và kubelet
Mục tiêu bài học
Sau bài này, bạn phải làm được 4 việc trong phòng thi:
- Đọc đúng
Conditionscủa node để biết nên đi theo hướng nào (kubelet, tài nguyên, hay mạng). - SSH vào node, kiểm tra
kubeletvàcontainer runtimebằngsystemctlvàjournalctl. - Nhận diện 5 nguyên nhân NotReady hay gặp nhất trong đề CKA: kubelet chết, kubelet không
enable, swap bật, runtime chết, certificate/config sai. - Đưa node về
Readyvà xác nhận bằngkubectl get nodes -w.
[!NOTE] Đây là bài thực hành. Bạn nên mở sẵn một cluster lab 1 control plane + 1 worker (dựng ở Module 2) và SSH được vào worker. Mọi thao tác dưới đây đều làm được trên cluster đó.
Ôn nhanh bài trước
Bài trước chốt quy trình 4 dòng: event → describe → log → sửa. Với node, thứ tự này giữ nguyên, chỉ đổi "log" thành log của kubelet trên node chứ không phải log container. Nhắc lại: kubectl describe cho bạn Events ở cấp object, còn journalctl -u kubelet cho bạn lý do thật sự nằm ở tầng systemd.
Bản đồ quy trình
flowchart TD
Start["🖥️ Node báo NotReady"] --> Get["kubectl get nodes -w"]
Get --> Desc["kubectl describe node node01"]
Desc --> Cond{"Condition nào đang xấu?"}
Cond -->|"Ready = Unknown / KubeletNotReady"| Kube["ssh node01 + systemctl status kubelet"]
Cond -->|"DiskPressure / MemoryPressure"| Res["df -h, du -sh /var/lib/containerd"]
Cond -->|"NetworkUnavailable = True"| Cni["Kiểm tra CNI trong /etc/cni/net.d"]
Kube --> Jrnl["journalctl -u kubelet -n 100"]
Jrnl --> Rt["systemctl status containerd + crictl ps"]
Rt --> Fix["Sửa swap / config / cert / flag"]
Cni --> Fix
Res --> Fix
Fix --> Reload["systemctl daemon-reload"]
Reload --> Restart["systemctl restart kubelet"]
Restart --> Verify["kubectl get nodes -w"]
Ví dụ mẫu: đưa node01 từ NotReady về Ready
Tình huống: sau khi reboot worker, node node01 đứng ở NotReady, pod trên đó bị đẩy sang node khác. Đây là màn bạn sẽ gặp.
1. Xác nhận triệu chứng: `kubectl get nodes`. Thấy `node01` ở trạng thái `NotReady`.
2. Xem lý do ở cấp API: `kubectl describe node node01 | sed -n '/Conditions:/,/Addresses:/p'`.
3. Đọc `Events` cuối output: thường là `NodeNotReady ... Kubelet stopped posting node status` hoặc `KubeletNotReady ... container runtime is down`.
4. SSH vào node: `ssh node01` rồi `sudo -i`.
5. Kiểm tra dịch vụ: `systemctl status kubelet` và `systemctl status containerd`.
6. Đọc log kubelet: `journalctl -u kubelet -n 100 --no-pager`.
7. Sửa nguyên nhân gốc (swap, config, flag, cert, runtime), sau đó `systemctl daemon-reload` nếu có sửa file systemd.
8. Khởi động lại: `systemctl enable --now kubelet`.
9. Xác nhận từ máy control plane: `kubectl get nodes -w`, chờ `Ready` (khoảng 40 giây).
Output mẫu của bước 2 và 3:
Conditions:
Type Status LastHeartbeatTime Reason
---- ------ ----------------- ------
MemoryPressure False Mon, 12 Aug ... KubeletHasSufficientMemory
DiskPressure False Mon, 12 Aug ... KubeletHasNoDiskPressure
PIDPressure False Mon, 12 Aug ... KubeletHasSufficientPID
Ready Unknown Mon, 12 Aug ... KubeletNotReady
Events:
Warning NodeNotReady node/node01 Kubelet stopped posting node status.
Đọc đúng hai dòng này là bạn đã khoanh vùng được 80% vấn đề: Ready = Unknown + KubeletNotReady nghĩa là kubelet không chạy hoặc không nói chuyện được với API server, không phải pod của bạn lỗi.
| Condition | Ý nghĩa thực tế | Đi kiểm tra gì |
|---|---|---|
Ready = Unknown |
kubelet ngừng gửi heartbeat (mặc định quá ~40s) | systemctl status kubelet, journalctl -u kubelet |
DiskPressure = True |
phân vùng image/root gần đầy | df -h, du -sh /var/lib/containerd |
MemoryPressure = True |
node thiếu RAM | free -h, kubectl describe node phần Allocated resources |
PIDPressure = True |
quá nhiều process | `ps aux |
NetworkUnavailable = True |
CNI chưa sẵn sàng | ls /etc/cni/net.d, pod CNI trong kube-system |
Thực hành có hướng dẫn
Mỗi bài dưới đây bạn tự phá rồi tự sửa. Làm xong mỗi bài, ghi lại lệnh đã dùng vào một cheat sheet riêng, vì phòng thi không cho mở tab thứ hai.
[!WARNING] Chỉ làm trên cluster lab. Các lệnh dưới đây dừng dịch vụ và có thể khiến pod bị evict tạm thời.
Bài 1: kubelet bị stop (bài cơ bản nhất, hay xuất hiện trong đề)
Mục tiêu: nhận ra Ready = Unknown và khôi phục trong dưới 2 phút.
# Trên node01
sudo -i
systemctl stop kubelet
Quan sát từ control plane: kubectl get nodes -w (chờ khoảng 40 giây mới đổi trạng thái).
Dấu hiệu cần thấy: describe node ghi Kubelet stopped posting node status.
Khôi phục:
systemctl start kubelet
systemctl status kubelet --no-pager
Xem thêm: nếu start xong vẫn NotReady
Xem log ngay: journalctl -u kubelet -n 50 --no-pager | grep -iE "error|failed". Đừng đoán, log luôn nói rõ.
Bài 2: kubelet không được enable sau reboot
Mục tiêu: học phản xạ kiểm tra enabled chứ không chỉ active.
systemctl disable kubelet
systemctl stop kubelet
systemctl status kubelet --no-pager # Active: inactive (dead)
Khôi phục đúng cách:
systemctl enable --now kubelet
[!TIP] Trong đề CKA, lỗi "node NotReady sau reboot" thường chỉ là kubelet bị
disabled. Một lệnhsystemctl enable --now kubeletlà xong, đừng đi sâu vào certificate.
Bài 3: swap đang bật
Mục tiêu: đọc được thông báo lỗi kinh điển của kubelet.
fallocate -l 1G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
systemctl restart kubelet
journalctl -u kubelet -n 30 --no-pager
Dấu hiệu cần thấy: log có dòng tương tự failed to run Kubelet: running with swap on is not supported, please disable swap.
Khôi phục:
swapoff -a
# xóa hoặc comment dòng swap trong /etc/fstab để không bật lại sau reboot
rm -f /swapfile
systemctl restart kubelet
Bài 4: container runtime chết
Mục tiêu: phân biệt lỗi kubelet và lỗi runtime.
systemctl stop containerd
systemctl status kubelet --no-pager
journalctl -u kubelet -n 30 --no-pager | grep -i "runtime"
Dấu hiệu cần thấy: kubelet báo không kết nối được tới CRI socket, something như failed to get container runtime status hoặc connection refused tới /run/containerd/containerd.sock.
Khôi phục:
systemctl enable --now containerd
systemctl restart kubelet
crictl ps # phải trả về danh sách container, không lỗi
Nếu `crictl` báo không tìm thấy endpoint
Kiểm tra /etc/crictl.yaml hoặc thêm flag --runtime-endpoint unix:///run/containerd/containerd.sock. Ở cluster kubeadm, endpoint thật nằm trong /var/lib/kubelet/kubeadm-flags.env.
Bài 5: config kubelet bị sửa sai
Mục tiêu: biết file nào là nguồn cấu hình thật, tránh sửa nhầm.
/var/lib/kubelet/config.yamllà nội dungKubeletConfiguration./var/lib/kubelet/kubeadm-flags.envlà các flag kubeadm truyền vào (KUBELET_KUBEADM_ARGS)./etc/systemd/system/kubelet.service.d/10-kubeadm.conflà drop-in của systemd.
# gây lỗi: trỏ sai runtime endpoint
cp /var/lib/kubelet/kubeadm-flags.env /root/kubeadm-flags.env.bak
sed -i 's#containerd.sock#containerd-typo.sock#' /var/lib/kubelet/kubeadm-flags.env
systemctl daemon-reload
systemctl restart kubelet
journalctl -u kubelet -n 20 --no-pager
Khôi phục:
cp /root/kubeadm-flags.env.bak /var/lib/kubelet/kubeadm-flags.env
systemctl daemon-reload
systemctl restart kubelet
[!IMPORTANT] Sửa file cấu hình mà không
daemon-reload(với drop-in systemd) hoặc khôngrestart kubeletthì thay đổi không có hiệu lực. Đây là lỗi làm mất thời gian nhiều nhất trong phòng thi.
Bài 6 (mở rộng, làm nếu còn thời gian): DiskPressure
df -h /var/lib/containerd
fallocate -l 5G /tmp/fill && ls -lh /tmp/fill # điều chỉnh theo dung lượng đĩa lab
kubectl describe node node01 | grep -i disk
Khôi phục: rm -f /tmp/fill, dọn image không dùng crictl rmi --prune, giới hạn log journalctl --vacuum-size=100M, rồi chờ kubelet tự cập nhật condition (không cần restart).
Bảng kiểm sau khi node đã Ready
| Kiểm tra | Lệnh | Kỳ vọng |
|---|---|---|
| Node Ready | kubectl get nodes |
Ready |
| Kubelet chạy và enable | systemctl is-active kubelet, systemctl is-enabled kubelet |
active, enabled |
| Runtime chạy | systemctl is-active containerd |
active |
| CRI hoạt động | crictl ps |
danh sách container, không lỗi |
| Pod hệ thống ổn | kubectl get pods -n kube-system -o wide |
pod trên node đó Running |
| Không còn event lỗi | kubectl get events -A --sort-by=.lastTimestamp |
không còn NodeNotReady mới |
Lỗi thường gặp & cách khắc phục
| Lỗi | Triệu chứng | Cách xử lý |
|---|---|---|
Sửa nhầm /etc/kubernetes/kubelet.conf khi cần sửa /var/lib/kubelet/config.yaml |
Sửa xong không có gì thay đổi | kubelet.conf là kubeconfig kết nối API, config.yaml là cấu hình hành vi |
Quên daemon-reload |
Đã sửa drop-in mà log vẫn như cũ | systemctl daemon-reload rồi restart |
| Certificate kubelet hết hạn | Log có certificate has expired, Unable to register node |
Kiểm tra openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates; đảm bảo rotateCertificates: true và symlink kubelet-client-current.pem còn đúng |
| CNI chưa cài | NetworkUnavailable = True, node có thể vẫn Ready nhưng pod không có IP |
ls /etc/cni/net.d, kiểm tra pod CNI trong kube-system |
| Sai giờ hệ thống | Certificate bị coi là chưa/hết hiệu lực | timedatectl, bật chronyd hoặc systemd-timesyncd |
| Firewall chặn 10250 | Control plane không lấy được status | Kiểm tra kết nối tới port 10250 (kubelet API) và 6443 |
[!CAUTION] Đừng xóa
ca.crthaykubelet.confđể "làm lại từ đầu" trong phòng thi. Bạn sẽ mất nhiều thời gian hơn lỗi ban đầu. Ưu tiên đọc log, sửa đúng một dòng, restart.
Góc chuyên gia (Expert Corner)
- Đọc log theo thứ tự ưu tiên: dòng
failed to run Kubeletxuất hiện gần cuối log thường là nguyên nhân gốc; các dòng phía trên chỉ là hệ quả. Dùngjournalctl -u kubelet -n 200 --no-pager | grep -iE "error|fail|expired|swap"để lọc nhanh. - Ngưỡng thời gian cần nhớ: kubelet gửi status mỗi 10 giây, control plane đánh dấu NotReady sau khoảng 40 giây (
node-monitor-grace-period). Pod có toleration mặc định chonot-readykhoảng 300 giây mới bị evict. Vì vậy cứ bình tĩnh sửa, đừng hoảng khi thấy pod còn nằm trên node lỗi. - Không có SSH? Dùng
kubectl debug node/node01 -it --image=busyboxđể vào namespace của node, hoặc kiểm tra quakubectl get --raw /api/v1/nodes/node01/proxy/configzđể xem cấu hình kubelet đang chạy. - Quy tắc 3 lệnh: mọi bài node NotReady trong đề đều bắt đầu bằng
kubectl describe node,systemctl status kubelet,journalctl -u kubelet. Thuộc ba lệnh này là đủ để mở khóa bài toán. - Kiểm tra runtime trước khi nghi kubelet: nếu
crictl pstreo hoặc lỗi, sửa runtime trước, vì kubelet không thể Ready khi CRI không trả lời. - Ghi lại mọi thay đổi: trong CKA, nếu bạn sửa 4 file mà không nhớ file nào, việc rollback sẽ tốn thời gian gấp đôi. Backup bằng
cp file file.baktrước khi sửa.
Tóm tắt & bước tiếp theo
Chốt lại trong bốn dòng:
Ready = Unknown/KubeletNotReadylà bài toán của kubelet, không phải của workload.- Đi theo thứ tự:
describe node→systemctl status kubelet→journalctl -u kubelet→crictl ps. - Bốn nguyên nhân phổ biến: kubelet không chạy, không
enable, swap bật, runtime chết. - Luôn kết thúc bằng
systemctl daemon-reload(nếu sửa file) vàkubectl get nodes -wđể xác nhận.
Điền nhanh các ô trống sau để tự kiểm tra:
Node chuyển sang NotReady sau khoảng {{c1::40}} giây kể từ khi kubelet ngừng gửi heartbeat. Lệnh xem log kubelet trên node là {{c2::journalctl -u kubelet}}, còn công cụ CLI nói chuyện với container runtime qua CRI là {{c3::crictl}}. Kubelet mặc định yêu cầu swap phải {{c4::tắt}} (swapoff -a). Nếu condition NetworkUnavailable bằng True thì thường do {{c5::CNI chưa được cài hoặc pod CNI đang lỗi}}. Sau khi sửa file drop-in của systemd, phải chạy {{c6::systemctl daemon-reload}} trước khi restart kubelet.
Bài tiếp theo: Troubleshoot pod với CrashLoopBackOff, ImagePullBackOff và Pending. Khi node đã Ready mà pod vẫn không lên, vấn đề chuyển sang image, resource, scheduling và probe. Chúng ta sẽ dùng lại đúng quy trình này nhưng ở cấp pod.
[
{ "front": "Node ở trạng thái NotReady với Ready = Unknown thường do đâu?", "back": "Kubelet ngừng gửi heartbeat hoặc không kết nối được API server." },
{ "front": "Lệnh đầu tiên để xem Conditions và Events của một node?", "back": "kubectl describe node <ten-node>" },
{ "front": "Lệnh xem log của kubelet trên node?", "back": "journalctl -u kubelet -n 100 --no-pager" },
{ "front": "Mặc định mất bao lâu để node bị đánh dấu NotReady sau khi kubelet dừng?", "back": "Khoảng 40 giây (node-monitor-grace-period)." },
{ "front": "File cấu hình KubeletConfiguration trên node kubeadm nằm ở đâu?", "back": "/var/lib/kubelet/config.yaml" },
{ "front": "File chứa các flag kubeadm truyền cho kubelet?", "back": "/var/lib/kubelet/kubeadm-flags.env" },
{ "front": "Kubelet báo 'running with swap on is not supported' thì sửa thế nào?", "back": "swapoff -a, xóa dòng swap trong /etc/fstab, rồi restart kubelet." },
{ "front": "Công cụ CLI làm việc với container runtime qua CRI?", "back": "crictl (ví dụ crictl ps, crictl info)." },
{ "front": "Condition NetworkUnavailable = True thường do gì?", "back": "CNI chưa được cài hoặc pod CNI trong kube-system đang lỗi." },
{ "front": "Sau khi sửa systemd drop-in của kubelet phải làm gì trước khi restart?", "back": "Chạy systemctl daemon-reload." },
{ "front": "Lệnh kiểm tra hạn certificate kubelet client?", "back": "openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates" },
{ "front": "Làm sao đảm bảo kubelet tự chạy lại sau khi node reboot?", "back": "systemctl enable kubelet (hoặc enable --now kubelet)." },
{ "front": "Node báo DiskPressure thì kiểm tra gì đầu tiên?", "back": "df -h và dung lượng /var/lib/containerd, sau đó dọn image bằng crictl rmi --prune." },
{ "front": "Không có SSH vào node, dùng lệnh nào để vào namespace của node?", "back": "kubectl debug node/<ten-node> -it --image=busybox" },
{ "front": "Làm sao theo dõi node chuyển về Ready theo thời gian thực?", "back": "kubectl get nodes -w" }
]