Troubleshooting: Pod lỗi thì nhìn vào đâu

4 câu hỏi phỏng vấn~25 phútTừ khóa: kubectl get, describe, logs --previous, events, exec, port-forward, debug, ephemeral container, Pending, ImagePullBackOff, CrashLoopBackOff, OOMKilled, CreateContainerConfigError, endpoints, DNS, NetworkPolicy

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 đề

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áiNghĩaNguyên nhân hay gặpXem ở đâu
PendingChưa được đặt lên nodeThiếu CPU/RAM theo request, nodeSelector/taint không khớp, PVC chưa bounddescribe pod → Events FailedScheduling
ImagePullBackOff / ErrImagePullKhông kéo được imageSai tên/tag, registry private thiếu imagePullSecrets, node không ra Internetdescribe pod → Events Failed to pull image
CreateContainerConfigErrorKhông dựng được containerConfigMap/Secret/key được tham chiếu không tồn tạidescribe pod
CrashLoopBackOffStart rồi chết liên tụcThiếu config, sai DB URL, exception lúc start, liveness quá gắtlogs --previous, exit code trong describe
OOMKilledBị kernel giết vì vượt memory limitLimit thấp, heap JVM đặt sai, leakdescribe → Last State, exit code 137
Running, READY 0/1Chạy nhưng readiness failDB chưa kết nối được, endpoint sai path/portdescribe → Readiness probe failed
Terminating mãiKhông xóa xongFinalizer, node mất liên lạcget pod -o yaml → finalizers
EvictedBị node đuổiNode thiếu memory/diskdescribe 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ệnhDùng khiKhông dùng được khi
describeMọi trạng thái, đặc biệt trước khi container startEvents chỉ giữ khoảng 1 giờ
logsContainer đã chạyPending, ImagePullBackOff
logs --previousCrashLoopBackOff, OOMKilledPod đã bị thay mới (cần log tập trung)
execImage có shell, Pod đang chạyDistroless, Pod crash liên tục
debugDistroless, cần công cụ mạngCluster cũ hoặc policy cấm
port-forwardThử service từ máy mình, bỏ qua IngressKhông phản ánh đường đi thật qua Ingress

7. Khi nào nên dùng / không nên dùng

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

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ự:

  1. 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
  2. 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.
  3. CPU throttling: Pod chạm CPU limit nên chậm. Xem metric container_cpu_cfs_throttled_periods_total.
  4. 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ồi kubectl 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ì đọc kubectl logs --previous và exit code. Nếu cần kiểm tra bên trong em dùng exec, hoặc kubectl debug với image distroless.

🇬🇧 English:

I start with kubectl get pods for the status, then kubectl describe pod and 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 read kubectl logs --previous and the exit code. If I need to look inside I use exec, or kubectl debug for 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 targetPort có đúng port app nghe không. Sau đó chạy một debug pod như netshoot trong cùng namespace để thử nslookup tên Service, curl Service IP và curl thẳ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 targetPort matches the port the app listens on. Then I run a debug Pod such as netshoot in the same namespace to nslookup the Service name, curl the Service IP and curl a 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 pod sẽ 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 pod shows 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 status và kubectl get pods để thấy Pod mới đang ở trạng thái nào, rồi describe và logs Pod 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, em kubectl rollout undo (hoặc helm rollback) trước rồi mới điều tra, vì ưu tiên là khôi phục dịch vụ. Với maxUnavailable: 0 thì Pod cũ vẫn phục vụ nên thường có thời gian điều tra.

🇬🇧 English:

I check kubectl rollout status and kubectl get pods to see what state the new Pods are in, then describe and logs on 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 run kubectl rollout undo (or helm rollback) first and investigate afterwards, because restoring service comes first. With maxUnavailable: 0 the old Pods keep serving, so there's usually time to investigate.

Cách nhớ ngắn gọn


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.