Xử lý lỗi Kubernetes: Phần 2 - "Nỗi ám ảnh" về Resource, Network & Storage

0 0 0

Người đăng: Minh Hoàng

Theo Viblo Asia

Mở đầu: Tiếp tục hành trình gỡ rối Kubernetes

Chào mừng anh em quay trở lại với series "Xử lý lỗi Kubernetes"!

Phần 1, chúng ta đã cùng nhau "bóc tách" 5 lỗi phổ biến liên quan đến quá trình khởi tạo và cấu hình Pod, bao gồm CrashLoopBackOff, ImagePullBackOff, Pod Pending, PodInitializing, và CreateContainerConfigError. Đó là những vấn đề nền tảng mà hầu hết anh em DevOps/SRE đều gặp phải khi mới làm quen hoặc trong quá trình vận hành hàng ngày với Kubernetes.

Tuy nhiên, hành trình làm chủ Kubernetes không dừng lại ở đó. Khi các Pod đã vượt qua được giai đoạn "chớm nở", chúng ta lại phải đối mặt với những thách thức mới liên quan đến việc sử dụng tài nguyên, duy trì trạng thái khỏe mạnh, kết nối mạng và quản lý dữ liệu lâu dài. Đây thường là những "nỗi ám ảnh" phức tạp hơn, đòi hỏi sự hiểu biết sâu sắc hơn về kiến trúc và các thành phần của K8s.

Trong Phần 2 này, chúng ta sẽ đi sâu vào 5 lỗi "khó nhằn" tiếp theo:

  • OOMKilled: Khi container "ngốn" quá nhiều RAM và bị hệ thống "trảm".
  • Liveness/Readiness Probe Failure: Những "cảm biến" sức khỏe hoạt động sai hoặc ứng dụng thực sự gặp vấn đề.
  • Node Not Ready: Khi Worker Node "đình công" và không thể nhận thêm Pod.
  • Network Policies / Service Connectivity Issues: Mê cung kết nối mạng và những cạm bẫy tiềm ẩn.
  • PersistentVolumeClaim (PVC) Errors: Những rắc rối với việc lưu trữ dữ liệu stateful.

Anh em cùng mình tiếp tục khám phá và trang bị thêm những "vũ khí" cần thiết để tự tin đối mặt và xử lý những lỗi này nhé!


6. OOMKilled: Khi Container "ăn" quá nhiều Memory

image.png

OOMKilled là một trạng thái không mong muốn khác, báo hiệu rằng một container trong Pod của bạn đã bị hệ điều hành (OS) trên node "thẳng tay" kết liễu vì sử dụng bộ nhớ (RAM) vượt quá giới hạn (limit) đã được cấp phát cho nó.

Mô tả lỗi:

Pod của bạn đột ngột bị restart mà không có dấu hiệu rõ ràng trong logs ứng dụng. Khi bạn chạy kubectl describe pod <tên-pod-vừa-restart>, bạn sẽ thấy trong phần State của container có trạng thái TerminatedReasonOOMKilled. Đồng thời, Last State cũng sẽ hiển thị thông tin tương tự. Điều này xác nhận rằng container đã bị kill do hết bộ nhớ.

Nguyên nhân:

Có hai nguyên nhân chính dẫn đến tình trạng OOMKilled:

  • Memory Limit quá thấp: Đây là trường hợp phổ biến. Bạn đặt giá trị spec.containers[].resources.limits.memory trong manifest quá thấp so với nhu cầu bộ nhớ thực tế của ứng dụng khi hoạt động ở mức tải bình thường hoặc cao điểm. Khi ứng dụng cần nhiều RAM hơn giới hạn cho phép, cơ chế Out-of-Memory Killer của kernel Linux trên node sẽ can thiệp và kill tiến trình đó để bảo vệ sự ổn định của toàn bộ node.
  • Ứng dụng bị Memory Leak: Ứng dụng của bạn có lỗi trong code khiến nó tiêu thụ bộ nhớ ngày càng tăng theo thời gian mà không giải phóng đúng cách. Dù ban đầu memory limit có vẻ đủ, nhưng dần dần lượng RAM sử dụng sẽ chạm ngưỡng và cuối cùng dẫn đến OOMKilled. Đây là một vấn đề nghiêm trọng hơn cần được sửa ở tầng code.

Cách khắc phục:

  1. Xác nhận lý do Restart: Luôn bắt đầu bằng việc kiểm tra describe pod để chắc chắn lý do là OOMKilled.

    kubectl describe pod <tên-pod-bị-restart>
    # Tìm trong phần Containers -> State hoặc Last State -> Reason: OOMKilled
    
  2. Phân tích Resource Usage: Sử dụng các công cụ giám sát (ví dụ: Prometheus + Grafana, Datadog, hoặc kubectl top pod) để xem lịch sử sử dụng memory của Pod trước khi nó bị kill. Điều này giúp bạn hiểu được ứng dụng thực sự cần bao nhiêu memory.

    kubectl top pod <tên-pod-bị-restart> --containers
    
  3. Tăng Memory Limit (Cẩn thận): Nếu xác định limit hiện tại rõ ràng là quá thấp so với nhu cầu bình thường, bạn có thể tăng giá trị resources.limits.memory trong manifest và áp dụng lại.

    # Ví dụ tăng limit
    resources: requests: memory: "128Mi" cpu: "250m" limits: memory: "512Mi" # Tăng từ giá trị cũ, ví dụ 256Mi cpu: "500m"
    

    Lưu ý: Việc chỉ đơn thuần tăng limit mà không hiểu rõ nguyên nhân có thể che giấu vấn đề memory leak và gây lãng phí tài nguyên cluster.

  4. Điều tra Memory Leak: Nếu nghi ngờ có memory leak (ví dụ: memory usage tăng dần theo thời gian không rõ lý do), bạn cần sử dụng các công cụ profiling bộ nhớ phù hợp với ngôn ngữ lập trình của ứng dụng (ví dụ: pprof cho Go, JProfiler/VisualVM cho Java, memory-profiler cho Python) để xác định và sửa lỗi trong code.

  5. Kiểm tra Memory Requests: Đảm bảo resources.requests.memory cũng được đặt ở mức hợp lý. Mặc dù OOMKilled liên quan trực tiếp đến limits, nhưng requests ảnh hưởng đến việc scheduling và Quality of Service (QoS) class của Pod. Nếu requests quá thấp, Pod có thể bị kill sớm hơn trong trường hợp node bị memory pressure.

Mẹo phòng tránh:

  • Profiling ứng dụng: Trước khi triển khai, hãy profiling ứng dụng dưới tải mô phỏng để xác định nhu cầu memory thực tế và đặt requests/limits phù hợp.
  • Đặt Limits cao hơn Requests một chút: Cho phép ứng dụng có một khoảng "thở" khi xử lý các tác vụ đột biến, nhưng không nên đặt limit quá cao so với request nếu không cần thiết.
  • Monitor Memory Usage liên tục: Theo dõi sát sao biểu đồ sử dụng memory của các Pod quan trọng để phát hiện sớm các dấu hiệu bất thường hoặc xu hướng tăng dần của memory leak.
  • Sử dụng Horizontal Pod Autoscaler (HPA): Nếu ứng dụng có thể scale ngang, HPA có thể giúp tạo thêm replica khi tải tăng, thay vì để một Pod duy nhất chịu trận và bị OOMKilled.
  • Thực hiện Code Review kỹ lưỡng: Chú trọng vào việc quản lý bộ nhớ trong quá trình review code để phát hiện sớm các nguy cơ memory leak.

7. Liveness/Readiness Probe Failure: Khi "bộ cảm biến" sức khỏe báo lỗi

image.png

Liveness và Readiness Probes là những cơ chế cực kỳ hữu ích trong Kubernetes, giúp hệ thống tự động kiểm tra và duy trì "sức khỏe" của ứng dụng. Tuy nhiên, chính những "bộ cảm biến" này đôi khi lại gây ra rắc rối nếu không được cấu hình đúng cách.

Mô tả lỗi:

Có hai kịch bản chính:

  • Liveness Probe Failure: Pod của bạn bị restart liên tục một cách không mong muốn. Khi kiểm tra kubectl describe pod <tên-pod>, bạn thấy trong Events có các thông báo như Liveness probe failed: ..., dẫn đến việc Kubelet quyết định kill và khởi động lại container.
  • Readiness Probe Failure: Pod vẫn chạy (Running) nhưng không nhận được traffic từ Kubernetes Service. Lệnh kubectl get endpoints <tên-service> cho thấy Pod đó không nằm trong danh sách ENDPOINTS sẵn sàng, hoặc kubectl describe pod <tên-pod> có event Readiness probe failed: ....

Nguyên nhân:

Việc probe thất bại có thể do nhiều yếu tố:

  • Cấu hình Probe sai:
    • Sai port, path (cho HTTP probe), hoặc command (cho exec probe).
    • Timeout (timeoutSeconds) quá ngắn: Ứng dụng không kịp phản hồi trong thời gian cho phép, đặc biệt khi hệ thống đang chịu tải.
    • initialDelaySeconds quá ngắn: Probe bắt đầu kiểm tra trước khi ứng dụng kịp khởi động hoàn toàn và sẵn sàng phản hồi.
    • periodSeconds quá ngắn hoặc failureThreshold quá thấp: Probe kiểm tra quá thường xuyên hoặc quá nhạy cảm với các lỗi tạm thời.
  • Ứng dụng thực sự bị lỗi/treo: Đây là trường hợp probe hoạt động đúng. Ứng dụng không phản hồi đúng cách tại endpoint/command được kiểm tra do lỗi nội tại, deadlock, hoặc không thể kết nối tới dependency (database, service khác).
  • Thiếu tài nguyên: Node hoặc Pod bị thiếu CPU/Memory, khiến ứng dụng hoặc chính tiến trình probe chạy chậm chạp và không phản hồi kịp thời gian timeout.
  • Vấn đề Network: Probe (đặc biệt là HTTP) không thể kết nối tới chính container của nó do cấu hình mạng nội bộ Pod có vấn đề (hiếm gặp) hoặc Network Policy chặn kết nối loopback (cũng hiếm).

Cách khắc phục:

  1. Kiểm tra Events của Pod: Luôn bắt đầu với describe pod để xem chi tiết lỗi của probe (failed HTTP request, command exit code, timeout...).

    kubectl describe pod <tên-pod-bị-lỗi-probe>
    
  2. Kiểm tra lại cấu hình Probe: Soi kỹ các tham số livenessProbereadinessProbe trong manifest:

    livenessProbe: httpGet: path: /healthz # Đường dẫn đúng chưa? port: 8080 # Port đúng chưa? initialDelaySeconds: 15 # Có đủ thời gian khởi động? periodSeconds: 10 timeoutSeconds: 5 # Timeout có quá ngắn? failureThreshold: 3
    readinessProbe: exec: command: - cat - /tmp/healthy # Command đúng chưa? File có tồn tại? initialDelaySeconds: 5 periodSeconds: 5
    
  3. Thử chạy Probe thủ công:

    • HTTP Probe: exec vào Pod và dùng curl để gọi tới đúng path/port.
      kubectl exec <tên-pod> -- curl -v http://localhost:8080/healthz
      
    • Exec Probe: exec vào Pod và chạy trực tiếp command.
      kubectl exec <tên-pod> -- cat /tmp/healthy
      # Kiểm tra exit code: echo $?
      
    • TCP Probe: exec vào Pod và dùng nc (netcat) để kiểm tra port.
      kubectl exec <tên-pod> -- nc -zv localhost 8080
      

    Nếu chạy thủ công cũng lỗi, vấn đề nằm ở ứng dụng hoặc môi trường bên trong container.

  4. Kiểm tra Logs ứng dụng: Xem logs của container để tìm nguyên nhân tại sao nó không phản hồi probe đúng cách.

    kubectl logs <tên-pod-bị-lỗi-probe>
    
  5. Điều chỉnh tham số Probe: Nếu ứng dụng cần nhiều thời gian khởi động hơn, hãy tăng initialDelaySeconds. Nếu ứng dụng đôi khi phản hồi chậm do tải, hãy tăng timeoutSeconds hoặc failureThreshold.

  6. Kiểm tra tài nguyên: Dùng kubectl top pod hoặc các công cụ monitoring khác để xem Pod có bị thiếu CPU/Memory không.

Mẹo phòng tránh:

  • Thiết kế Endpoint/Command cho Probe hiệu quả: Endpoint/command dùng cho probe nên nhẹ nhàng, phản hồi nhanh và chỉ kiểm tra các thành phần cốt lõi của ứng dụng. Tránh các kiểm tra phức tạp hoặc phụ thuộc vào nhiều yếu tố bên ngoài.
  • Phân biệt rõ Liveness và Readiness: Readiness kiểm tra xem ứng dụng có thể xử lý request mới hay không (ví dụ: đã kết nối DB, cache đã warm up). Liveness kiểm tra xem ứng dụng có cần restart hay không (ví dụ: bị deadlock). Đừng dùng Liveness Probe cho các kiểm tra phụ thuộc vào bên ngoài mà chỉ nên tập trung vào trạng thái nội tại của tiến trình.
  • Đặt tham số Probe thực tế: Dựa vào thời gian khởi động và đặc tính phản hồi của ứng dụng để đặt initialDelaySeconds, timeoutSeconds, periodSeconds, failureThreshold phù hợp. Đừng đặt quá chặt chẽ.
  • Sử dụng Startup Probe (từ K8s v1.18+): Nếu ứng dụng khởi động rất chậm, Startup Probe cho phép trì hoãn việc kích hoạt Liveness và Readiness Probe cho đến khi ứng dụng thực sự sẵn sàng, tránh bị kill oan trong quá trình khởi động.

8. Node Not Ready: Khi Worker Node "mất liên lạc"

image.png

Một trong những tình huống đáng lo ngại nhất khi vận hành Kubernetes là khi một hoặc nhiều Worker Node chuyển sang trạng thái NotReady. Điều này có nghĩa là Control Plane không thể giao tiếp hoặc xác nhận trạng thái khỏe mạnh của Node đó, dẫn đến việc các Pod mới sẽ không được schedule lên Node này và các Pod đang chạy trên đó có thể bị ảnh hưởng (ví dụ: bị Evict sau một thời gian).

Mô tả lỗi:

Khi bạn chạy lệnh kubectl get nodes, một hoặc nhiều Node hiển thị STATUSNotReady. Đôi khi, trạng thái có thể là Unknown nếu Control Plane hoàn toàn mất liên lạc với Node trong một khoảng thời gian dài.

Nguyên nhân:

Việc Node trở nên NotReady có thể xuất phát từ nhiều tầng khác nhau, từ phần cứng, hệ điều hành, đến chính các thành phần của Kubernetes trên Node đó:

  • Kubelet không hoạt động: Kubelet là agent chính chạy trên mỗi Worker Node, chịu trách nhiệm giao tiếp với API Server và quản lý các container. Nếu tiến trình Kubelet bị dừng, bị treo, hoặc gặp lỗi nghiêm trọng, nó sẽ không thể gửi tín hiệu "heartbeat" (Node Lease) về cho Control Plane, dẫn đến trạng thái NotReady.
  • Vấn đề Network giữa Node và Control Plane: Kết nối mạng giữa Worker Node và API Server bị gián đoạn. Nguyên nhân có thể do:
    • Firewall chặn kết nối (trên Node, trên Control Plane, hoặc ở tầng mạng trung gian).
    • Sự cố mạng vật lý (đứt cáp, lỗi switch...).
    • Cấu hình mạng sai trên Node (sai IP, gateway, DNS...).
    • Vấn đề định tuyến (routing).
  • Node bị quá tải tài nguyên (Node Pressure): Node cạn kiệt tài nguyên nghiêm trọng, đặc biệt là Memory (MemoryPressure) hoặc Disk (DiskPressure). Khi đó, Kubelet có thể hoạt động không ổn định hoặc không thể thực hiện các tác vụ cần thiết, và nó sẽ báo cáo trạng thái áp lực này lên Control Plane, có thể dẫn đến NotReady nếu tình trạng quá tệ.
  • Lỗi hệ điều hành hoặc phần cứng: Kernel panic, lỗi ổ cứng, lỗi card mạng, hoặc các vấn đề phần cứng/OS khác khiến Node không hoạt động bình thường.
  • Vấn đề với Container Runtime (Docker, containerd, CRI-O): Container runtime trên Node gặp lỗi, khiến Kubelet không thể quản lý các container.
  • Nâng cấp hoặc bảo trì: Node đang trong quá trình nâng cấp OS, Kubelet, hoặc container runtime và tạm thời không sẵn sàng.
  • Vấn đề với Cloud Provider (nếu dùng Managed K8s): Sự cố từ phía nhà cung cấp cloud ảnh hưởng đến máy ảo hoặc kết nối mạng của Node.

Cách khắc phục:

Việc khắc phục đòi hỏi phải điều tra từ nhiều phía:

  1. Kiểm tra trạng thái Kubelet trên Node: Đây thường là điểm bắt đầu. SSH vào Node đang bị NotReady và kiểm tra:

    sudo systemctl status kubelet
    # Kiểm tra logs của Kubelet
    sudo journalctl -u kubelet -f --since "10 minutes ago"
    

    Tìm kiếm các thông báo lỗi hoặc lý do tại sao Kubelet không thể giao tiếp với API Server.

  2. Kiểm tra tài nguyên Node:

    top # hoặc htop
    df -h
    free -h
    

    Xem Node có bị cạn kiệt CPU, Memory, hay Disk không.

  3. Kiểm tra Network Connectivity từ Node tới API Server:

    • Lấy địa chỉ API Server (thường có trong file config của Kubelet, ví dụ /etc/kubernetes/kubelet.conf hoặc /var/lib/kubelet/kubeconfig).
    • Thử ping, traceroute, curl hoặc nc từ Node tới địa chỉ và port của API Server.
      # Ví dụ
      curl -k https://<api-server-ip>:<port>/healthz
      nc -zv <api-server-ip> <port>
      
    • Kiểm tra cấu hình DNS trên Node (/etc/resolv.conf).
    • Kiểm tra firewall rules trên Node (sudo iptables -L, sudo ufw status).
  4. Kiểm tra trạng thái Container Runtime:

    # Ví dụ với containerd
    sudo systemctl status containerd
    sudo journalctl -u containerd -f
    # Ví dụ với Docker
    sudo systemctl status docker
    sudo journalctl -u docker -f
    
  5. Kiểm tra logs hệ thống: Xem /var/log/syslog hoặc journalctl để tìm các lỗi liên quan đến kernel, phần cứng, hoặc các dịch vụ hệ thống khác.

  6. Kiểm tra Cloud Provider Console (nếu có): Nếu chạy trên cloud, kiểm tra console của nhà cung cấp (AWS EC2, GCP Compute Engine, Azure VM) xem có thông báo lỗi nào về máy ảo hoặc network không.

  7. Khởi động lại Kubelet/Node: Nếu không tìm ra nguyên nhân rõ ràng, việc khởi động lại Kubelet (sudo systemctl restart kubelet) hoặc thậm chí khởi động lại toàn bộ Node có thể giải quyết các vấn đề tạm thời (nhưng cần tìm hiểu gốc rễ nếu lỗi lặp lại).

Mẹo phòng tránh:

  • Monitoring toàn diện: Giám sát chặt chẽ trạng thái Node (Ready/NotReady), tài nguyên (CPU, Memory, Disk, Network I/O), trạng thái Kubelet và container runtime.
  • Cấu hình Node Problem Detector: Triển khai Node Problem Detector để tự động phát hiện các vấn đề phổ biến ở tầng Node và báo cáo chúng dưới dạng Node Conditions hoặc Events.
  • Đảm bảo Network ổn định: Thiết kế mạng tin cậy giữa Worker Nodes và Control Plane, cấu hình firewall đúng đắn.
  • Quản lý tài nguyên hiệu quả: Đặt resource requests/limits hợp lý cho Pods để tránh gây quá tải cho Node. Sử dụng Cluster Autoscaler để tự động thêm Node khi cần.
  • Bảo trì định kỳ: Thực hiện nâng cấp OS, Kubelet, container runtime một cách có kế hoạch và kiểm soát (ví dụ: nâng cấp từng Node một).

9. Network Policies / Service Connectivity Issues: "Lạc trôi" giữa mạng lưới Kubernetes

image.png

Kết nối mạng trong Kubernetes là một chủ đề phức tạp, và các vấn đề liên quan đến nó có thể rất khó gỡ rối. Lỗi thường biểu hiện dưới dạng một Pod không thể giao tiếp với Pod khác, không thể truy cập Service, hoặc không thể kết nối ra bên ngoài cluster, mặc dù mọi thứ có vẻ đang chạy bình thường.

Mô tả lỗi:

  • Ứng dụng trong Pod báo lỗi timeout hoặc connection refused khi cố gắng kết nối tới một Service hoặc Pod khác trong cùng cluster.
  • Pod không thể phân giải tên miền (DNS lookup) của các Service nội bộ hoặc các địa chỉ bên ngoài.
  • Service có tồn tại (kubectl get svc) nhưng không có Endpoints nào được liệt kê (kubectl get endpoints <tên-service>), hoặc Endpoints có nhưng traffic không đến được các Pod backend.
  • Pod không thể kết nối tới các dịch vụ bên ngoài cluster (ví dụ: database ngoài, API của bên thứ ba).

Nguyên nhân:

Vấn đề kết nối mạng trong K8s có thể bắt nguồn từ nhiều lớp:

  • Network Policy: Đây là "nghi phạm" hàng đầu nếu bạn đang sử dụng Network Policies để kiểm soát traffic giữa các Pod. Một policy quá chặt chẽ hoặc cấu hình sai có thể vô tình chặn các kết nối hợp lệ (ingress hoặc egress).
  • Lỗi cấu hình Service:
    • selector của Service không khớp với labels của các Pod backend.
    • Sai port hoặc targetPort trong định nghĩa Service.
    • Loại Service (ClusterIP, NodePort, LoadBalancer) không phù hợp với cách truy cập.
  • Vấn đề DNS (CoreDNS/kube-dns):
    • Các Pod CoreDNS (hoặc kube-dns) bị lỗi, quá tải, hoặc không thể kết nối tới upstream DNS server.
    • Cấu hình DNS của Pod (/etc/resolv.conf bên trong Pod) bị sai.
    • Lỗi trong việc tự động đăng ký Service vào DNS.
  • Lỗi CNI Plugin: Container Network Interface (CNI) plugin (như Calico, Flannel, Cilium, Weave Net) chịu trách nhiệm thiết lập mạng cho Pod và Node. Lỗi trong CNI plugin hoặc cấu hình sai có thể gây ra đủ loại vấn đề kết nối.
  • Vấn đề ở tầng Node/Infrastructure:
    • Firewall trên Node chặn traffic giữa các Pod hoặc tới Service IP.
    • Sự cố mạng ở tầng hạ tầng vật lý hoặc cloud provider.
    • Vấn đề định tuyến (routing) giữa các Node.
  • kube-proxy có vấn đề: Thành phần này chạy trên mỗi Node, chịu trách nhiệm duy trì các rule (thường là iptables hoặc IPVS) để chuyển tiếp traffic từ Service IP tới các Pod backend. Nếu kube-proxy lỗi hoặc cấu hình sai, Service sẽ không hoạt động.

Cách khắc phục:

Gỡ rối mạng K8s đòi hỏi cách tiếp cận từng lớp:

  1. Kiểm tra Network Policies: Nếu đang sử dụng, hãy kiểm tra kỹ các policy áp dụng cho Pod nguồn và Pod đích.

    # Liệt kê các policy trong namespace
    kubectl get networkpolicy -n <tên-namespace>
    # Xem chi tiết một policy
    kubectl describe networkpolicy <tên-policy> -n <tên-namespace>
    

    Thử tạm thời xóa policy đáng nghi để xem kết nối có được khôi phục không.

  2. Kiểm tra Service và Endpoints:

    kubectl describe svc <tên-service> -n <tên-namespace>
    # Kiểm tra Selector có khớp với label của Pod không
    # Kiểm tra Ports (port, targetPort)
    kubectl get endpoints <tên-service> -n <tên-namespace>
    # Đảm bảo có địa chỉ IP của các Pod khỏe mạnh trong danh sách Endpoints
    

    Nếu không có Endpoints, kiểm tra lại label của Pod và Readiness Probe của chúng (xem Lỗi 7).

  3. Kiểm tra DNS Resolution từ bên trong Pod:

    # Exec vào Pod nguồn
    kubectl exec -it <tên-pod-nguồn> -n <tên-namespace> -- /bin/sh
    # Thử nslookup/dig tới Service đích hoặc địa chỉ bên ngoài
    nslookup <tên-service-đích>.<tên-namespace>
    nslookup kubernetes.default.svc.cluster.local # Kiểm tra DNS nội bộ
    nslookup google.com # Kiểm tra DNS bên ngoài
    # Kiểm tra file resolv.conf
    cat /etc/resolv.conf
    

    Nếu DNS lỗi, kiểm tra trạng thái và logs của các Pod CoreDNS (kubectl get pods -n kube-system, kubectl logs <coredns-pod> -n kube-system).

  4. Kiểm tra kết nối trực tiếp tới Pod IP: Bỏ qua Service, thử kết nối trực tiếp từ Pod nguồn tới IP của Pod đích (lấy IP bằng kubectl get pod <tên-pod-đích> -o wide).

    # Exec vào Pod nguồn
    kubectl exec <tên-pod-nguồn> -- curl http://<IP-pod-đích>:<port>
    kubectl exec <tên-pod-nguồn> -- ping <IP-pod-đích> # Nếu Pod có ping
    

    Nếu kết nối trực tiếp thành công nhưng qua Service thì lỗi, vấn đề có thể ở Service hoặc kube-proxy.

  5. Kiểm tra Logs CNI và kube-proxy: Xem logs của các agent CNI (ví dụ: calico-node, flanneld) và kube-proxy trên các Node liên quan.

    # Tìm tên Pod CNI/kube-proxy trên Node
    kubectl get pods -n <namespace-của-cni/kube-system> -o wide | grep <tên-node>
    # Xem logs
    kubectl logs <tên-pod-cni/kube-proxy> -n <namespace> 
  6. Sử dụng công cụ gỡ rối mạng chuyên dụng: Các công cụ như ksniff (để capture traffic từ Pod), hoặc các tính năng troubleshooting của CNI (ví dụ: calicoctl node status) có thể rất hữu ích.

Mẹo phòng tránh:

  • Thiết kế Network Policy cẩn thận: Bắt đầu với policy permisive và siết chặt dần. Sử dụng labels một cách nhất quán để dễ dàng quản lý policy.
  • Kiểm tra kỹ cấu hình Service: Double-check selectors và ports.
  • Monitor CoreDNS và CNI: Theo dõi sức khỏe và hiệu năng của các thành phần mạng cốt lõi.
  • Hiểu rõ CNI Plugin đang sử dụng: Mỗi CNI có kiến trúc và cách hoạt động riêng, hiểu rõ nó giúp gỡ rối hiệu quả hơn.
  • Sử dụng Service Mesh (nếu cần): Các công cụ như Istio, Linkerd cung cấp khả năng quan sát và kiểm soát traffic mạnh mẽ hơn, giúp gỡ rối dễ dàng hơn trong các hệ thống phức tạp (nhưng cũng tăng thêm độ phức tạp tổng thể).

10. PersistentVolumeClaim (PVC) Errors: Rắc rối với lưu trữ dữ liệu

image.png

Đối với các ứng dụng stateful (cần lưu trữ dữ liệu lâu dài), hệ thống lưu trữ Persistent Volumes (PV) và Persistent Volume Claims (PVC) của Kubernetes là vô cùng quan trọng. Tuy nhiên, đây cũng là một nguồn gốc phổ biến của các lỗi khiến Pod không thể khởi động hoặc hoạt động đúng cách.

Mô tả lỗi:

Các vấn đề về PVC có thể biểu hiện theo nhiều cách:

  • PVC bị kẹt ở trạng thái Pending khi chạy kubectl get pvc.
  • Pod sử dụng PVC bị kẹt ở trạng thái Pending hoặc ContainerCreating.
  • Lệnh kubectl describe pod <tên-pod> hiển thị các Events lỗi như FailedAttachVolume (không thể gắn volume vào node) hoặc FailedMount (không thể mount volume vào container sau khi đã gắn vào node).
  • Ứng dụng bên trong Pod báo lỗi không thể đọc/ghi vào đường dẫn đã được mount.

Nguyên nhân:

Lỗi liên quan đến PVC/PV thường phức tạp do liên quan đến nhiều thành phần, từ định nghĩa của người dùng, Kubernetes control plane, đến storage system thực tế (local, NFS, cloud storage...):

  • Không tìm thấy PV phù hợp (với Static Provisioning hoặc StorageClass sai):
    • Bạn tạo PVC yêu cầu một StorageClass cụ thể hoặc các thuộc tính (dung lượng, access mode) nhất định, nhưng không có PV nào (được tạo thủ công hoặc bởi dynamic provisioner) khớp với yêu cầu đó.
    • PVC không chỉ định StorageClass (storageClassName: "") và không có StorageClass nào được đánh dấu là default trong cluster.
  • StorageClass không tồn tại hoặc cấu hình sai: PVC chỉ định một storageClassName, nhưng không có StorageClass nào với tên đó tồn tại, hoặc StorageClass đó có cấu hình provisioner hoặc parameters sai.
  • Dynamic Provisioner (CSI Driver) lỗi:
    • Container Storage Interface (CSI) driver cho loại storage bạn đang dùng chưa được cài đặt hoặc các pod của nó (controller, node plugin) đang bị lỗi/crash.
    • CSI driver không có đủ quyền (RBAC) để tương tác với storage system hoặc Kubernetes API.
  • Hết tài nguyên lưu trữ: Không còn đủ dung lượng trống trên storage system để tạo PV mới theo yêu cầu của PVC.
  • Giới hạn của Cloud Provider/Storage System:
    • Đạt đến giới hạn số lượng volume có thể gắn vào một Node (ví dụ: giới hạn EBS volume trên EC2 instance).
    • Volume đang được gắn vào một Node khác trong khi PVC yêu cầu Access Mode là ReadWriteOnce (RWO), không cho phép gắn đồng thời vào nhiều node.
  • Vấn đề về Zone/Region: PV được tạo ở một availability zone khác với zone của Node mà Pod đang được schedule lên (thường gặp với cloud storage).
  • Lỗi ở tầng Node: Kubelet hoặc các tiến trình liên quan đến mount trên Node gặp lỗi (ví dụ: thiếu package nfs-common cho NFS volume).

Cách khắc phục:

Việc gỡ rối cần đi từ PVC ngược lại các thành phần liên quan:

  1. Kiểm tra trạng thái và Events của PVC:

    kubectl get pvc <tên-pvc> -n <tên-namespace>
    kubectl describe pvc <tên-pvc> -n <tên-namespace>
    

    Events của PVC thường sẽ chỉ ra lý do tại sao nó Pending (ví dụ: no persistent volumes available for this claim, storageclass.storage.k8s.io "<tên-sc>" not found).

  2. Kiểm tra PV liên quan (nếu PVC đã Bound hoặc có PV tĩnh):

    kubectl get pv <tên-pv>
    kubectl describe pv <tên-pv>
    

    Kiểm tra Status (Available, Bound, Released, Failed), StorageClassName, Capacity, AccessModes, ReclaimPolicy, và các thông tin về zone/region (nếu có).

  3. Kiểm tra StorageClass:

    kubectl get sc
    kubectl describe sc <tên-storageclass>
    

    Đảm bảo StorageClass mà PVC yêu cầu tồn tại, có Provisioner đúng, và Parameters hợp lệ.

  4. Kiểm tra Events của Pod: Nếu PVC đã Bound nhưng Pod vẫn lỗi, kiểm tra describe pod để xem lỗi FailedAttachVolume hay FailedMount.

    kubectl describe pod <tên-pod-dùng-pvc> -n <tên-namespace>
    

    Thông báo lỗi thường sẽ cung cấp chi tiết hơn (ví dụ: timeout khi attach, lỗi mount do sai định dạng, sai tùy chọn mount).

  5. Kiểm tra Logs của CSI Driver Pods: Các Pod của CSI driver (thường nằm trong namespace kube-system hoặc namespace riêng) chứa thông tin quan trọng về quá trình provisioning, attaching, và mounting. Tìm các Pod controller và node plugin trên Node liên quan và xem logs của chúng.

  6. Kiểm tra giới hạn và trạng thái Volume ở tầng hạ tầng: Truy cập console của cloud provider hoặc giao diện quản lý storage system để kiểm tra trạng thái thực tế của volume, số lượng volume đang gắn vào node.

  7. Kiểm tra các thành phần cần thiết trên Node: SSH vào Node và kiểm tra xem các package cần thiết cho loại volume đang dùng đã được cài đặt chưa (ví dụ: nfs-common, cifs-utils). Kiểm tra logs Kubelet (journalctl -u kubelet).

Mẹo phòng tránh:

  • Ưu tiên Dynamic Provisioning: Sử dụng StorageClass và dynamic provisioning bất cứ khi nào có thể để đơn giản hóa việc quản lý PV.
  • Định nghĩa StorageClass rõ ràng: Cấu hình StorageClass cẩn thận, phù hợp với yêu cầu về hiệu năng, độ bền, và chi phí.
  • Hiểu rõ Access Modes: Chọn Access Mode (ReadWriteOnce, ReadOnlyMany, ReadWriteMany) phù hợp với workload và khả năng của storage backend. RWO là phổ biến nhất nhưng cũng dễ gây lỗi nhất khi Pod bị reschedule sang node khác.
  • Monitor dung lượng PV và Storage System: Theo dõi dung lượng sử dụng để tránh hết chỗ.
  • Đảm bảo CSI Driver hoạt động ổn định: Cài đặt đúng phiên bản CSI driver tương thích với phiên bản K8s và storage system, monitor trạng thái của các pod CSI.
  • Sử dụng Volume Binding Mode WaitForFirstConsumer: Đối với các loại storage phụ thuộc vào zone (như EBS, GCE PD), đặt volumeBindingMode: WaitForFirstConsumer trong StorageClass để trì hoãn việc tạo PV cho đến khi Pod đầu tiên sử dụng PVC được schedule. Điều này đảm bảo PV được tạo ở đúng zone của Node.
  • Sử dụng Storage Quotas: Đặt ResourceQuota để giới hạn tổng dung lượng hoặc số lượng PVC mà một namespace có thể yêu cầu.

Kết luận: Chủ động làm chủ Kubernetes

Tổng kết qua hai phần của series này, chúng ta đã cùng nhau đi qua 10 lỗi Kubernetes phổ biến, từ những vấn đề cơ bản khi khởi tạo Pod cho đến những thách thức phức tạp hơn về tài nguyên, mạng lưới và lưu trữ. Hy vọng rằng những chia sẻ thực tế này đã cung cấp cho anh em những góc nhìn hữu ích và các bước hành động cụ thể để tự tin hơn khi đối mặt với sự cố.

Việc vận hành Kubernetes không chỉ dừng lại ở việc triển khai ứng dụng thành công. Nó là một quá trình liên tục, đòi hỏi sự giám sát chặt chẽ, khả năng phân tích và xử lý sự cố nhanh nhạy. Hiểu rõ các lỗi phổ biến và cách khắc phục chúng chính là chìa khóa để chúng ta chủ động hơn trong việc đảm bảo tính ổn định và hiệu năng của hệ thống, thay vì bị động chờ đợi sự cố xảy ra.

Troubleshooting không phải là một công việc dễ dàng, nhưng nó cũng là cơ hội để chúng ta hiểu sâu hơn về cách Kubernetes hoạt động. Mỗi lần xử lý thành công một lỗi là một lần chúng ta nâng cao kỹ năng và kinh nghiệm của bản thân.

Tuy nhiên, danh sách 10 lỗi này chắc chắn chưa thể bao quát hết mọi tình huống thực tế. Mỗi môi trường, mỗi ứng dụng lại có những đặc thù riêng. Vì vậy, rất mong nhận được sự chia sẻ thêm từ cộng đồng DevOps/Kubernetes tại Việt Nam. Anh em đã từng gặp những lỗi "xương xẩu" nào khác? Cách xử lý và bài học rút ra là gì? Hãy cùng nhau xây dựng một cộng đồng chia sẻ kiến thức vững mạnh, giúp chúng ta cùng nhau làm chủ công nghệ phức tạp nhưng đầy tiềm năng này.

Chúc anh em luôn giữ được "cái đầu lạnh" và sự bình tĩnh khi đối mặt với các lỗi Kubernetes!

Tham khảo

Bình luận

Bài viết tương tự

- vừa được xem lúc

Đề thi interview DevOps ở Châu Âu

Well. Chào mọi người, mình là Rice - một DevOps Engineers ở đâu đó tại Châu Âu.

0 0 100

- vừa được xem lúc

In calculus, love also means zero.

Mình nhớ hồi năm 2 đại học, thầy giáo môn calculus, trong một giây phút ngẫu hứng, đã đưa ra cái definition này. Lúc đấy mình cũng không nghĩ gì nhiều.

0 0 70

- vừa được xem lúc

Chuyện thay đổi

Thay đổi là một thứ gì đó luôn luôn đáng sợ. Cách đây vài tháng mình có duyên đi làm cho một banking solution tên là X.

0 0 55

- vừa được xem lúc

Pet vs Cattle - Thú cưng và gia súc

Khái niệm. Pets vs Cattle là một khái niệm cơ bản của DevOps. Bài viết này sẽ nói về sự phát triển của các mô hình dịch vụ từ cốt lõi Pets and Cattle. 1.

0 0 42

- vừa được xem lúc

Git workflow được Google và Facebook sử dụng có gì hay ho

Với developer thì Git hẳn là công cụ rất quen thuộc và không thể thiếu rồi. Thế nhưng có mấy ai thực sự hiểu được Git.

0 0 94

- vừa được xem lúc

Kubernetes - Học cách sử dụng Kubernetes Namespace cơ bản

Namespace trong Kubernetes là gì. Tại sao nên sử dụng namespace.

0 0 124