Troubleshooting: Pod lỗi thì nhìn vào đâu
Tiếp đi: debug khi Pod lỗi.
Đừng chạy lệnh ngẫu nhiên. Có hai trục để đi: trạng thái Pod và đường đi của request.
1. Bài toán
Vừa deploy payment-service 2.0, người dùng báo POST /orders lỗi 502:
kubectl get pods -n shop
NAME READY STATUS RESTARTS
order-service-6d9f7b8c4-k2m8x 1/1 Running 0
payment-service-7c84c6f789-abc12 0/1 CrashLoopBackOff 5
payment-service-5b7d9e2a1-x7k2p 0/1 ImagePullBackOff 0
payment-service-5b7d9e2a1-m9q4z 0/1 Pending 0
postgres-0 1/1 Running 0
2. Vấn đề
- Mỗi STATUS có nguyên nhân khác nhau và chỗ xem khác nhau.
kubectl logskhông giúp gì khi container chưa từng start (Pending, ImagePullBackOff).- Image production thường distroless,
kubectl exec -- shbáo không có shell. - Pod Running nhưng gọi không tới: lỗi nằm ở Service, DNS, hoặc NetworkPolicy, không ở Pod.
3. Giải pháp: bộ lệnh và bảng tra trạng thái
Something broken
↓
kubectl get pods → trạng thái nào?
↓
kubectl describe pod → Events ở cuối: scheduler, kubelet, probe nói gì?
↓
kubectl logs [--previous] → app nói gì?
↓
kubectl get events → toàn namespace, theo thời gian
↓
kubectl exec / debug → vào trong kiểm tra mạng, file, env
| Trạng thái | Nghĩa | Nguyên nhân hay gặp | Xem ở đâu |
|---|---|---|---|
| Pending | Chưa được đặt lên node | Thiếu CPU/RAM theo request, nodeSelector/taint không khớp, PVC chưa bound | describe pod → Events FailedScheduling |
| ImagePullBackOff / ErrImagePull | Không kéo được image | Sai tên/tag, registry private thiếu imagePullSecrets, node không ra Internet | describe pod → Events Failed to pull image |
| CreateContainerConfigError | Không dựng được container | ConfigMap/Secret/key được tham chiếu không tồn tại | describe pod |
| CrashLoopBackOff | Start rồi chết liên tục | Thiếu config, sai DB URL, exception lúc start, liveness quá gắt | logs --previous, exit code trong describe |
| OOMKilled | Bị kernel giết vì vượt memory limit | Limit thấp, heap JVM đặt sai, leak | describe → Last State, exit code 137 |
| Running, READY 0/1 | Chạy nhưng readiness fail | DB chưa kết nối được, endpoint sai path/port | describe → Readiness probe failed |
| Terminating mãi | Không xóa xong | Finalizer, node mất liên lạc | get pod -o yaml → finalizers |
| Evicted | Bị node đuổi | Node thiếu memory/disk | describe pod → Reason |
4. Code từng bước
Bước 1: Khoanh vùng
kubectl get pods -n shop -o wide # node nào, IP nào
kubectl get events -n shop --sort-by=.lastTimestamp | tail -20
kubectl rollout status deployment/payment-service -n shop
Bước 2: Đọc từng Pod lỗi
# Pending
kubectl describe pod payment-service-5b7d9e2a1-m9q4z
# Warning FailedScheduling 0/3 nodes are available: 3 Insufficient memory.
# ImagePullBackOff
kubectl describe pod payment-service-5b7d9e2a1-x7k2p
# Failed to pull image "myregistry/payment-service:2.0.1": not found
# CrashLoopBackOff
kubectl logs payment-service-7c84c6f789-abc12 --previous
# APPLICATION FAILED TO START
# Failed to configure a DataSource: 'url' attribute is not specified
kubectl describe pod payment-service-7c84c6f789-abc12
# Last State: Terminated Reason: Error Exit Code: 1
Exit code giúp đoán nhanh: 1 lỗi app, 137 SIGKILL (OOM hoặc hết grace period), 143 SIGTERM.
Bước 3: Vào trong Pod
kubectl exec -it deploy/order-service -- sh
env | grep DB_
wget -qO- http://payment-service/actuator/health
# Image distroless: gắn ephemeral container có công cụ, dùng chung process namespace
kubectl debug -it payment-service-7c84c6f789-abc12 \
--image=nicolaka/netshoot --target=payment
# Pod crash quá nhanh để exec: tạo bản sao với command khác
kubectl debug payment-service-7c84c6f789-abc12 -it --copy-to=payment-debug \
--container=payment -- sh
Bước 4: Debug mạng bằng một debug pod
kubectl run netshoot -n shop --rm -it --image=nicolaka/netshoot --restart=Never -- bash
# 1. DNS có phân giải được không?
nslookup payment-service
nslookup payment-service.shop.svc.cluster.local
# 2. Service IP có trả lời không?
curl -sv http://payment-service/actuator/health
# 3. Gọi thẳng Pod IP (bỏ qua Service)
curl -sv http://10.1.0.12:8080/actuator/health
# 4. Postgres có mở port không?
nc -zv postgres 5432
kubectl get endpointslices -n shop -l kubernetes.io/service-name=payment-service
kubectl get networkpolicy -n shop
kubectl port-forward svc/payment-service 8080:80 -n shop # thử từ máy mình
kubectl top pods -n shop
5. Cách hoạt động: đi theo request path
Client
↓
Ingress 1. Rule đúng host/path? ingressClassName? TLS Secret có? (describe ingress, log controller)
↓
Service 2. Service đúng tên/port? (get svc)
↓
Endpoints 3. Selector khớp label Pod? Có endpoint không? (get endpointslices, --show-labels)
↓
Pod 4. Running? 5. Ready? 6. Restart nhiều? (get pods, describe)
↓
Container 7. Logs? 8. Env/ConfigMap/Secret đúng? (logs, exec env)
↓
Spring Boot 9. targetPort đúng port app nghe? (curl Pod IP)
↓
postgres 10. DNS + port + NetworkPolicy + credentials? (debug pod: nslookup, nc)
Cách đọc kết quả debug pod:
curl Pod IP OK, curl Service FAIL → Service: selector, port, endpoints
nslookup FAIL → CoreDNS, tên/namespace sai
curl Pod IP FAIL (timeout) → NetworkPolicy chặn, app không nghe đúng port
curl Pod IP connection refused → app chưa start / nghe 127.0.0.1 thay vì 0.0.0.0
6. So sánh công cụ
| Lệnh | Dùng khi | Không dùng được khi |
|---|---|---|
describe | Mọi trạng thái, đặc biệt trước khi container start | Events chỉ giữ khoảng 1 giờ |
logs | Container đã chạy | Pending, ImagePullBackOff |
logs --previous | CrashLoopBackOff, OOMKilled | Pod đã bị thay mới (cần log tập trung) |
exec | Image có shell, Pod đang chạy | Distroless, Pod crash liên tục |
debug | Distroless, cần công cụ mạng | Cluster cũ hoặc policy cấm |
port-forward | Thử service từ máy mình, bỏ qua Ingress | Không phản ánh đường đi thật qua Ingress |
7. Khi nào nên dùng / không nên dùng
- Bắt đầu bằng
getvàdescribe, đọc Events trước khi đoán. - Không debug production bằng cách
execrồi sửa file trong container; mọi thay đổi mất khi Pod restart và không ai review. - Không xóa Pod liên tục để "thử lại". CrashLoopBackOff sẽ quay lại, mất luôn log
--previous. - Log và metric phải đi về hệ thống tập trung; kubectl chỉ là công cụ khi đang có sự cố.
Image distroless không có shell thì mình exec vào debug kiểu gì?
Đúng là kubectl exec -- sh sẽ báo executable file not found. Dùng ephemeral container:
kubectl debug -it payment-service-7c84c6f789-abc12 \
--image=nicolaka/netshoot --target=payment
- K8s gắn thêm một container tạm vào Pod đang chạy, không restart Pod.
- Container tạm dùng chung network namespace, nên
curl localhost:8080chạm thẳng app. --target=paymentcho dùng chung process namespace với container app:psthấy process Java, đọc được/proc/1/rootđể xem file của app.
Ephemeral container không xóa được khỏi Pod, nó chỉ dừng khi bạn thoát. Pod cũ sẽ biến mất ở lần deploy sau. Quyền chạy kubectl debug nên giới hạn bằng RBAC vì nó gần như quyền exec.
Pod Running, Ready đầy đủ, endpoint có, nhưng order gọi payment vẫn timeout. Còn chỗ nào nữa nhỉ?
Khi mọi thứ ở tầng K8s đều xanh, nghi ngờ theo thứ tự:
- NetworkPolicy: namespace có policy default-deny ingress mà chưa cho phép từ order sang payment. Triệu chứng là timeout (gói tin bị drop), không phải connection refused.
kubectl get networkpolicy -n shop kubectl describe networkpolicy -n shop - App chậm thật: payment chờ Bank API hoặc pool DB cạn. Lúc này K8s không có lỗi gì; cần timeout và circuit breaker ở order (xem bài 4.6.6) và tracing để tìm chỗ chậm.
- CPU throttling: Pod chạm CPU limit nên chậm. Xem metric
container_cpu_cfs_throttled_periods_total. - DNS chậm: mỗi lần gọi tốn vài giây vì search domain và
ndots:5. Dùng FQDN có dấu chấm cuối hoặc tăng cache.
Cách tách nhanh: từ debug pod trong namespace của order, curl -w "%{time_connect} %{time_total}" tới Service và tới Pod IP. Connect nhanh mà total chậm là app chậm; connect treo là mạng/policy.
Phỏng vấn hỏi cách debug sự cố trên Kubernetes thì trả lời sao?
Q1. Pod của bạn không chạy. Bạn debug theo thứ tự nào?
🇻🇳 Tiếng Việt:
Em xem
kubectl get podsđể biết trạng thái, rồikubectl describe podđọc Events vì đó là nơi scheduler và kubelet báo lỗi. Pending thì thường là thiếu tài nguyên theo request hoặc PVC chưa bound; ImagePullBackOff là sai image hoặc thiếu quyền registry; CrashLoopBackOff thì đọckubectl logs --previousvà exit code. Nếu cần kiểm tra bên trong em dùngexec, hoặckubectl debugvới image distroless.
🇬🇧 English:
I start with
kubectl get podsfor the status, thenkubectl describe podand read the Events, because that's where the scheduler and kubelet report problems. Pending usually means not enough resources for the requests or an unbound PVC; ImagePullBackOff means a wrong image or missing registry credentials; for CrashLoopBackOff I readkubectl logs --previousand the exit code. If I need to look inside I useexec, orkubectl debugfor distroless images.
Q2. Service không gọi được tới Pod, bạn kiểm tra gì?
🇻🇳 Tiếng Việt:
Em đi theo request path. Đầu tiên xem endpoints của Service: rỗng thì hoặc selector không khớp label Pod, hoặc Pod không Ready. Có endpoint thì kiểm tra
targetPortcó đúng port app nghe không. Sau đó chạy một debug pod như netshoot trong cùng namespace để thửnslookuptên Service,curlService IP vàcurlthẳng Pod IP. So sánh kết quả cho biết lỗi nằm ở DNS, Service, NetworkPolicy hay chính app.
🇬🇧 English:
I follow the request path. First I check the Service endpoints: if they're empty, either the selector doesn't match the Pod labels or the Pods aren't Ready. If there are endpoints, I check that
targetPortmatches the port the app listens on. Then I run a debug Pod such as netshoot in the same namespace tonslookupthe Service name,curlthe Service IP andcurla Pod IP directly. Comparing the results tells me whether the problem is DNS, the Service, a NetworkPolicy or the app itself.
Q3. Exit code 137 và 143 khác nhau thế nào?
🇻🇳 Tiếng Việt:
Exit code trên 128 nghĩa là process bị signal giết, trừ 128 ra là số signal. 143 là SIGTERM, tức tắt bình thường khi Pod bị xóa hoặc rolling update. 137 là SIGKILL: hoặc vượt memory limit và bị OOMKilled, hoặc app không thoát kịp trong
terminationGracePeriodSeconds.describe podsẽ ghi Reason là OOMKilled nếu là trường hợp đầu.
🇬🇧 English:
An exit code above 128 means the process was killed by a signal, and subtracting 128 gives the signal number. 143 is SIGTERM, a normal shutdown when a Pod is deleted or replaced during a rollout. 137 is SIGKILL: either the container exceeded its memory limit and was OOMKilled, or the app didn't exit within
terminationGracePeriodSeconds.describe podshows Reason OOMKilled in the first case.
Q4. Rolling update bị kẹt, bạn làm gì?
🇻🇳 Tiếng Việt:
Em xem
kubectl rollout statusvàkubectl get podsđể thấy Pod mới đang ở trạng thái nào, rồidescribevàlogsPod mới để tìm nguyên nhân: image sai, thiếu config, readiness không pass. Nếu đang ảnh hưởng người dùng hoặc chưa rõ nguyên nhân, emkubectl rollout undo(hoặchelm rollback) trước rồi mới điều tra, vì ưu tiên là khôi phục dịch vụ. VớimaxUnavailable: 0thì Pod cũ vẫn phục vụ nên thường có thời gian điều tra.
🇬🇧 English:
I check
kubectl rollout statusandkubectl get podsto see what state the new Pods are in, thendescribeandlogson a new Pod to find the cause: a bad image, missing config, or a readiness probe that never passes. If users are affected or the cause isn't obvious, I runkubectl rollout undo(orhelm rollback) first and investigate afterwards, because restoring service comes first. WithmaxUnavailable: 0the old Pods keep serving, so there's usually time to investigate.
Cách nhớ ngắn gọn
- get → describe (Events) → logs --previous → events → exec/debug.
- Pending = scheduler; ImagePull = registry; CrashLoop = app; OOMKilled = memory; 0/1 = readiness.
- Gọi không tới: endpoints → label → readiness → targetPort → DNS → NetworkPolicy.
- Distroless:
kubectl debug --target. Mạng: debug pod netshoot. - Đang ảnh hưởng người dùng: rollback trước, điều tra sau.
Tự kiểm tra
Pod Pending, vì sao kubectl logs không có gì?
Container chưa từng được tạo. Xem Events bằng kubectl describe pod.
CreateContainerConfigError thường do đâu?
ConfigMap/Secret hoặc key được tham chiếu trong env không tồn tại.
curl Pod IP được nhưng curl Service thì không. Lỗi ở đâu?
Ở Service: selector, port/targetPort, hoặc endpoints rỗng.
Gọi giữa hai Pod bị timeout (không phải refused). Nghi gì đầu tiên?
NetworkPolicy drop gói tin.
Lệnh xem events toàn namespace theo thời gian?
kubectl get events -n <ns> --sort-by=.lastTimestamp.