Troubleshoot PVC Pending và lỗi mount volume
PVC Pending là dạng bài gần như chắc chắn xuất hiện trong đề CKA, và nó cũng là dạng bài mà nhiều bạn mất điểm oan vì... đi sửa lung tung trước khi đọc event. Bài này luyện đúng một kỹ năng: đọc event để tìm nguyên nhân, rồi sửa đúng chỗ.
Thời lượng gợi ý: 25 phút. Trong đó 15 phút bạn ngồi lab thật, đừng chỉ đọc.
Mục tiêu bài học
Sau bài này, bạn phải làm được:
- Phân biệt pod ở
Pendingvà pod ởContainerCreating, vì hai trạng thái này dẫn tới hai nhánh troubleshoot khác nhau. - Đọc
kubectl describe pvcvàkubectl describe podđể lấy đúng dòng event quyết định. - Nhận diện 6 nguyên nhân PVC Pending phổ biến nhất và cách sửa từng loại.
- Nhận diện lỗi mount volume: Multi-Attach, thiếu Secret/ConfigMap, provisioner chết, CSI/node plugin lỗi.
- Biết khi nào PVC Pending không phải là lỗi.
Ôn nhanh bài trước
Bài trước bạn đã học StorageClass và dynamic provisioning: PVC chỉ cần khai báo storageClassName, provisioner sẽ tự tạo PV. StorageClass có nhãn (default) sẽ được dùng khi PVC không ghi storageClassName, và volumeBindingMode: WaitForFirstConsumer làm PVC chờ có pod tiêu thụ rồi mới bind.
Hai chi tiết đó chính là nguồn gốc của phần lớn lỗi trong bài này.
Ví dụ có hướng dẫn
Ví dụ 1: PVC Pending vì gõ sai tên StorageClass
Bước 1, xác nhận trạng thái:
kubectl get pvc -n demo
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-app Pending fast-ssd 45s
Bước 2, đọc event. Đây là bước quyết định, và event luôn nằm ở cuối output:
kubectl describe pvc data-app -n demo
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning ProvisioningFailed 10s persistentvolume-controller storageclass.storage.k8s.io "fast-ssd" not found
Bước 3, đối chiếu với danh sách StorageClass có thật:
kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
fast (default) rancher.io/local-path Delete WaitForFirstConsumer false 12d
Không có class nào tên fast-ssd. Nguyên nhân đã rõ: gõ sai tên.
Bước 4, sửa. storageClassName là field gần như bất biến, bạn không patch được, phải xóa và tạo lại PVC:
kubectl delete pvc data-app -n demo
kubectl apply -f pvc-data-app.yaml
kubectl get pvc -n demo -w
Kết quả mong đợi: data-app Bound pvc-... 1Gi RWO fast 5s.
[!NOTE] Nếu PVC đang bị pod sử dụng, lệnh delete sẽ treo ở
Terminatingvì finalizerkubernetes.io/pvc-protection. Phải xóa pod trước, hoặc đợi pod dừng hẳn.
Ví dụ 2: Pod kẹt ContainerCreating vì Multi-Attach
Bước 1, xem pod đang ở đâu:
kubectl get pod -n demo -o wide
NAME READY STATUS RESTARTS AGE NODE
web-7c9f-1 0/1 ContainerCreating 0 2m node01
web-7c9f-2 0/1 ContainerCreating 0 2m node02
Bước 2, đọc event của pod:
kubectl describe pod web-7c9f-2 -n demo | tail -n 15
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedAttachVolume 1m attachdetach-controller Multi-Attach error for volume "pvc-3f2a9c" Volume is already exclusively attached to one node and can't be attached to another
Bước 3, hiểu nguyên nhân: PVC là ReadWriteOnce, tức là chỉ mount được trên một node tại một thời điểm. Deployment có 2 replicas và scheduler đặt 2 pod lên 2 node khác nhau, nên pod thứ hai không attach được volume.
Bước 4, sửa bằng một trong các cách sau, chọn cách nhanh nhất trong phòng thi:
# Cách 1: chỉ chạy 1 replica
kubectl scale deploy web -n demo --replicas=1
# Cách 2: ép mọi pod về cùng một node
kubectl patch deploy web -n demo --type=merge -p \
'{"spec":{"template":{"spec":{"affinity":{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"kubernetes.io/hostname","operator":"In","values":["node01"]}]}]}}}}}}}'
Sau khi sửa, xóa pod đang kẹt để nó được tạo lại, rồi theo dõi bằng kubectl get pod -w.
Thực hành có hướng dẫn
Chuẩn bị: một cluster lab có StorageClass mặc định (local-path hoặc tương tự), namespace demo.
kubectl create ns demo
Bài 1: PV không khớp capacity (5 phút)
Tạo PV và PVC lệch nhau về dung lượng:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-manual
spec:
capacity:
storage: 1Gi
accessModes: ["ReadWriteOnce"]
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /mnt/data-manual
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-big
namespace: demo
spec:
storageClassName: manual
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
Việc bạn làm:
- Apply file trên.
- Chạy
kubectl get pv,pvc -n demo. Ghi lại trạng thái của từng object. - Chạy
kubectl describe pvc pvc-big -n demovà chép nguyên văn dòngMessagetrong Events. - Sửa lỗi bằng cách xóa PVC và tạo lại với
storage: 1Gi, rồi kiểm tra lại.
Kết quả mong đợi: PVC Pending, PV ở trạng thái Available (chưa bind). Event báo không có PV phù hợp hoặc không tìm thấy StorageClass manual (vì manual chỉ là tên class ghi trong PV, không phải object StorageClass có thật).
Câu hỏi tự kiểm tra: vì sao PV Available mà PVC vẫn Pending?
Bài 2: PVC Pending do gõ sai storageClassName (3 phút)
- Tạo PVC với
storageClassName: standar(thiếu chữd). - Chạy
kubectl describe pvcvà đọc event. - Xóa, tạo lại với
storageClassName: standard(hoặc tên class mặc định trong cluster của bạn).
Kết quả mong đợi: event storageclass.storage.k8s.io "standar" not found, sau khi sửa thì PVC Bound.
Bài 3: PVC Pending nhưng không phải lỗi (3 phút)
- Tạo PVC dùng StorageClass có
volumeBindingMode: WaitForFirstConsumer. kubectl get pvc: PVCPending. Đừng hoảng.- Đọc event, bạn sẽ thấy
waiting for first consumer to be created before binding. - Tạo một pod mount PVC đó, rồi xem lại PVC: nó chuyển sang
Boundngay sau khi pod được schedule.
Đây là điểm rất dễ mất điểm trong đề thi: thí sinh thấy Pending liền đi sửa PVC, trong khi hành vi đúng là tạo pod tiêu thụ.
Bài 4: Pod ContainerCreating vì thiếu Secret (4 phút)
Tạo pod mount một Secret không tồn tại:
apiVersion: v1
kind: Pod
metadata:
name: pod-secret-missing
namespace: demo
spec:
containers:
- name: app
image: busybox:1.36
command: ["sleep", "3600"]
volumeMounts:
- name: cfg
mountPath: /etc/cfg
volumes:
- name: cfg
secret:
secretName: app-config
Các bước:
- Apply, rồi
kubectl get pod -n demo. Pod sẽ ởContainerCreating. kubectl describe pod pod-secret-missing -n demo | tail -n 20, tìm dòngMountVolume.SetUp failedhoặcsecret "app-config" not found.- Sửa bằng
kubectl create secret generic app-config -n demo --from-literal=key=value. - Đợi khoảng 30 đến 60 giây, kubelet sẽ tự thử lại và pod chuyển
Running. Không cần xóa pod.
Bài 5: Bài tổng hợp, không có gợi ý (5 phút)
Giảng viên hoặc đồng đội tạo ngẫu nhiên một trong các tình huống sau, bạn không được xem YAML:
- PVC trỏ tới StorageClass không tồn tại.
- PVC xin
ReadWriteManynhưng PV chỉReadWriteOnce. - Pod mount PVC mà PVC chưa Bound.
- Provisioner pod trong
kube-systembị scale về 0.
Nhiệm vụ: trong 5 phút, xác định nguyên nhân và khắc phục. Chỉ dùng 4 lệnh: get, describe, get sc, get events.
Gợi ý quy trình chuẩn:
kubectl get pod -n demo
kubectl describe pod <pod> -n demo | tail -n 20
kubectl get pvc -n demo
kubectl describe pvc <pvc> -n demo | tail -n 15
kubectl get sc
kubectl get events -n demo --sort-by=.lastTimestamp | tail -n 15
Bảng tra nhanh event
| Thông báo trong event | Xuất hiện ở | Nguyên nhân thường gặp | Cách sửa |
|---|---|---|---|
storageclass.storage.k8s.io "x" not found |
PVC | Sai tên StorageClass, hoặc SC chưa tạo | kubectl get sc, sửa và tạo lại PVC |
no persistent volumes available for this claim and no storage class is set |
PVC | PVC không có storageClassName, không có PV khớp |
Tạo PV khớp, hoặc thêm storageClassName |
waiting for first consumer to be created before binding |
PVC | SC dùng WaitForFirstConsumer |
Không phải lỗi, tạo pod tiêu thụ |
failed to provision volume with StorageClass "x" |
PVC | Provisioner/CSI controller lỗi hoặc bị scale về 0 | Xem log pod provisioner trong kube-system |
pod has unbound immediate PersistentVolumeClaims |
Pod | PVC chưa Bound | Xử lý PVC trước, pod tự chạy lại |
Multi-Attach error for volume "pvc-..." |
Pod | Volume RWO đang gắn ở node khác | Scale về 1 replica hoặc gom pod về cùng node |
MountVolume.SetUp failed ... secret "x" not found |
Pod | Secret/ConfigMap chưa tồn tại | Tạo Secret/ConfigMap, chờ kubelet retry |
Unable to attach or mount volumes ... timed out waiting for the condition |
Pod | CSI node plugin lỗi, node NotReady, hết đĩa | Kiểm tra CSI pod và kubelet trên node |
[!TIP] Câu thần chú của bài này: ==luôn đọc Events ở cuối output của kubectl describe==. 90% thời gian bạn sẽ thấy nguyên nhân nằm ở đó, không cần suy đoán.
Lỗi thường gặp và cách xử lý
- Nhầm
PendingvớiContainerCreating. PodPendingthường là chưa schedule được hoặc PVC chưa Bound. PodContainerCreatinglà đã có node, đang kéo image và mount volume. Đọc sai trạng thái là đi sai cả nhánh. - Đi sửa pod khi lỗi gốc nằm ở PVC. Pod chỉ là nạn nhân. Nếu event của pod ghi
unbound immediate PersistentVolumeClaims, mọi thao tác trên pod đều vô nghĩa. - Cố patch
storageClassNamecủa PVC đang tồn tại. Không được. Phải xóa và tạo lại. - Xóa PVC khi pod còn chạy. PVC sẽ treo ở
Terminatingvì finalizer bảo vệ. Xóa pod trước. - Quên PV ở trạng thái
Released. VớireclaimPolicy: Retain, sau khi PVC bị xóa, PV chuyểnReleasedvà vẫn giữclaimRefcũ, nên không bind cho PVC mới. Phải xóaclaimRefhoặc tạo lại PV. - Quên namespace.
kubectl describe pvckhông có-nsẽ raNotFoundvà bạn tưởng PVC chưa được tạo. - Event đã hết hạn. Event mặc định chỉ giữ 1 giờ. Nếu cụm lab đã chạy lâu, hãy xóa pod (không phải PVC) để tạo lại và sinh event mới.
- Xóa PV có
Retainđể "làm lại cho nhanh". Trong đề thi, thao tác này có thể làm mất dữ liệu của bài khác. Đọc kỹ reclaim policy trước khi xóa.
Tự kiểm tra nhanh bằng cloze:
PVC chỉ bind được với PV khi khớp cả ba yếu tố: capacity, {{c1::accessModes}} và storageClassName.
Với volumeBindingMode: {{c2::WaitForFirstConsumer}}, PVC sẽ ở trạng thái Pending cho tới khi có pod sử dụng.
PV ở trạng thái {{c3::Released}} vẫn giữ claimRef cũ nên không bind cho PVC mới.
Lỗi {{c4::Multi-Attach}} xảy ra khi volume ReadWriteOnce đang được gắn ở một node khác.
Event "MountVolume.SetUp failed" thường do thiếu {{c5::Secret}} hoặc ConfigMap được mount vào pod.
Pod Pending với event "pod has unbound immediate PersistentVolumeClaims" nghĩa là {{c6::PVC chưa Bound}}.
Góc chuyên gia
Vài thói quen mình rút ra sau khi thi và sau nhiều lần làm hỏng cluster lab:
- Ba lệnh đầu tiên, theo đúng thứ tự:
kubectl get pod,kubectl describe pvc,kubectl get sc. Đừng sáng tạo thêm trước khi chạy đủ ba lệnh này. - Đọc từ dưới lên. Trong
kubectl describe, event mới nhất ở cuối. Đọc từ dưới lên gặp nguyên nhân trước, tiết kiệm thời gian. - Phân biệt "lỗi cấu hình" và "lỗi hạ tầng". Sai tên class, sai access mode, sai capacity là lỗi cấu hình, sửa trong 1 phút. Provisioner chết, CSI pod lỗi, node NotReady là lỗi hạ tầng, thường tốn thời gian hơn. Nếu rơi vào nhóm thứ hai trong phòng thi, đánh dấu và đi tiếp, quay lại sau.
- Quy tắc 5 phút. Một câu storage không đáng để bạn mất 15 phút. CKA có khoảng 15 đến 20 câu trong 2 giờ, trung bình 6 đến 7 phút mỗi câu.
- Kiểm tra bằng hành vi, không bằng cảm giác. Sau khi sửa, phải thấy
Boundvà podRunningvớikubectl get ... -w, đừng chỉ tin vào việc apply thành công. - Tạo PVC nhanh bằng heredoc, vì
kubectl createkhông có generator cho PVC:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
namespace: demo
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
EOF
- Ghi nhớ cặp đôi kinh điển: PVC Pending thì nhìn StorageClass và PV; Pod ContainerCreating thì nhìn event mount và node.
Tóm tắt và bước tiếp theo
Chốt lại trong bốn dòng:
- PVC Pending: đọc event của PVC, kiểm tra ba thứ capacity, accessModes, storageClassName.
WaitForFirstConsumerlàm PVC Pending có chủ đích, không phải lỗi.- Pod
ContainerCreating: đọc event của pod, tìmFailedMount,Multi-Attach,MountVolume.SetUp failed. - Sửa PVC thì thường phải xóa và tạo lại; sửa pod thì chỉ cần xóa pod.
Bộ thẻ ôn tập cho bài này, bạn nên chạy lại sau 1 ngày và sau 3 ngày:
[
{ "front": "PVC ở trạng thái Pending, lệnh đầu tiên nên chạy là gì?", "back": "kubectl describe pvc <tên>, đọc phần Events ở cuối output." },
{ "front": "Event 'storageclass.storage.k8s.io \"fast-ssd\" not found' nghĩa là gì?", "back": "storageClassName trong PVC không tồn tại, phải sửa tên class hoặc tạo StorageClass đó." },
{ "front": "PVC Pending với event 'waiting for first consumer to be created before binding' có phải lỗi không?", "back": "Không. StorageClass dùng WaitForFirstConsumer nên PVC chỉ bind khi có pod tiêu thụ." },
{ "front": "PV ở trạng thái Released có bind được cho PVC mới không?", "back": "Không. PV còn giữ claimRef cũ, phải xóa và tạo lại PV hoặc gỡ claimRef." },
{ "front": "Pod Pending với event 'pod has unbound immediate PersistentVolumeClaims' nghĩa là gì?", "back": "PVC mà pod dùng chưa Bound, phải xử lý PVC trước khi sửa pod." },
{ "front": "Lỗi 'Multi-Attach error for volume' xảy ra khi nào?", "back": "Khi volume ReadWriteOnce đang gắn ở một node khác, pod mới không attach được." },
{ "front": "Thiếu Secret được mount vào pod thì event của pod nói gì?", "back": "MountVolume.SetUp failed, kèm thông báo secret không tìm thấy." },
{ "front": "Làm sao biết StorageClass nào là mặc định?", "back": "kubectl get sc, tên class mặc định có hậu tố (default)." },
{ "front": "PVC đã tạo có sửa được storageClassName không?", "back": "Không, spec gần như bất biến, phải xóa và tạo lại PVC." },
{ "front": "Lệnh xem event của riêng một PVC?", "back": "kubectl get events --field-selector involvedObject.name=<pvc> --sort-by=.lastTimestamp" },
{ "front": "PV 1Gi nhưng PVC xin 2Gi thì chuyện gì xảy ra?", "back": "PVC Pending vì không có PV khớp về capacity." },
{ "front": "PVC xin ReadWriteMany nhưng PV chỉ hỗ trợ ReadWriteOnce?", "back": "Không bind được, PVC ở trạng thái Pending." },
{ "front": "Provisioner pod trong kube-system bị chết thì PVC ra sao?", "back": "PVC Pending với event failed to provision volume with StorageClass." },
{ "front": "Vì sao xóa PVC khi pod còn dùng lại bị treo ở Terminating?", "back": "Finalizer kubernetes.io/pvc-protection giữ PVC cho tới khi không còn pod sử dụng." },
{ "front": "Khác nhau cốt lõi giữa pod Pending và pod ContainerCreating?", "back": "Pending là chưa schedule hoặc PVC chưa Bound, ContainerCreating là đã có node và đang mount volume." }
]
Bài tiếp theo: Checkpoint: Storage. Đó là bài kiểm tra tổng hợp cả module 5, gồm tạo volume từ ConfigMap/Secret, PV/PVC binding, StorageClass và đúng dạng troubleshoot bạn vừa luyện. Trước khi sang đó, hãy chắc rằng bạn làm được Bài 5 mà không cần gợi ý.