Sửa lỗi 'dial tcp: i/o timeout' trong Kubernetes do Network Policy gây ra

intermediate☸️ Kubernetes2026-05-23| Kubernetes 1.24+ với CNI plugin hỗ trợ NetworkPolicy (Calico, Cilium, Weave, hoặc Azure CNI)

Error Message

dial tcp <pod-ip>:<port>: i/o timeout
#kubernetes#network-policy#networking#timeout#pod-to-pod

Kẻ Giết Kết Nối Thầm Lặng

Ba tiếng đồng hồ. Đó là khoảng thời gian tôi mất để debug một microservice không thể kết nối đến database. Log tràn ngập một lỗi duy nhất, nhưng nhìn phía ứng dụng thì chẳng có gì sai. Pod vẫn chạy. Service vẫn truy cập được. DNS phân giải bình thường.

dial tcp 10.244.1.45:5432: i/o timeout

Trong Kubernetes, lỗi i/o timeout có nghĩa là gói tin đã được gửi đi nhưng không nhận được phản hồi — hoặc bị âm thầm drop giữa đường. Đây là điểm khác biệt then chốt so với connection refused — cái đó có nghĩa là cổng bị đóng và máy đích chủ động từ chối. Timeout thường chỉ ra firewall hoặc Network Policy đang nuốt gói tin mà không gửi TCP Reset về.

Cách Tôi Debug Lỗi Timeout

Với timeout giữa các pod, tôi chạy qua một checklist ngắn để thu hẹp nguyên nhân. Bắt đầu từ đơn giản rồi đi sâu dần.

1. Kiểm Tra Pod Đích Có Thực Sự Đang Lắng Nghe Không

Bước đầu tiên: pod đích có đang sống và lắng nghe trên cổng đó không? Tôi tạo một pod debug netshoot trong cùng namespace và kết nối thẳng đến IP — không qua DNS, không qua service layer.

kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot -- /bin/bash
# Bên trong pod:
nc -zv 10.244.1.45 5432

Lại timeout. Vẫn lỗi đó. Điều này loại trừ hoàn toàn code ứng dụng — đây là vấn đề ở tầng network.

2. Kiểm Tra Network Policy Hiện Có

Tiếp theo, tôi kiểm tra các policy đang áp dụng cho namespace.

kubectl get networkpolicy -n production

Thấy ngay: một policy tên default-deny-ingress. Và đây là bẫy nhiều developer mắc phải: ngay khi một pod được chọn bởi bất kỳ NetworkPolicy nào, nó sẽ vào chế độ từ chối ngầm định cho toàn bộ traffic không được liệt kê tường minh. Ai đó đã khóa namespace vì lý do bảo mật nhưng quên thêm exception cho service mới của tôi.

Cách Sửa: Viết Rule Allow Cụ Thể

Đừng xóa deny policy. Đó là thụt lùi về bảo mật. Cách đúng là viết một allow rule hẹp, chỉ mở đúng traffic bạn cần.

Policy "Allow Ingress"

Tôi viết một NetworkPolicy nhắm vào pod database (bên nhận) để cho phép traffic từ pod API trên cổng 5432.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-db-access
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: web-api
    ports:
    - protocol: TCP
      port: 5432

Đừng Quên Egress

Timeout cũng có thể xảy ra ở phía gửi. Nếu pod nguồn có policy Egress, nó có thể bị chặn ngay từ đầu khi khởi tạo kết nối. Pod API của tôi cũng vậy — egress rule của nó cũng hạn chế tương tự. Tôi thêm đoạn này:

# Đoạn bổ sung cho Egress policy của API pod
egress:
- to:
  - podSelector:
      matchLabels:
        app: postgres
  ports:
  - protocol: TCP
    port: 5432

Kiểm Tra Sau Khi Sửa

Quay lại pod netshoot, chạy thêm một lần:

$ nc -zv 10.244.1.45 5432
Connection to 10.244.1.45 5432 port [tcp/postgresql] succeeded!

Thành công ngay lập tức. Không còn timeout nữa.

Những Thay Đổi Tôi Áp Dụng Về Sau

Network trong Kubernetes vô hình cho đến khi nó hỏng. Sau sự cố này, tôi hình thành ba thói quen:

  • Chuẩn hóa label cho pod: Mọi pod đều được gắn label app, roleenv từ đầu. NetworkPolicy chỉ select được những gì nó tìm thấy.
  • Ghi chép rõ deny policy: Default deny là lựa chọn đúng cho môi trường production. Nhưng bạn cần checklist onboarding rõ ràng để service mới không bị timeout âm thầm ngay lần deploy đầu tiên.
  • Xác minh dải CIDR trước khi commit: Khi policy liên quan đến IP ngoài hoặc subnet cụ thể, tôi kiểm tra lại dải địa chỉ bằng Subnet Calculator trên ToolCraft. Gõ nhầm CIDR — ví dụ viết /24 khi muốn /32 — có thể vô tình cho phép thêm 254 IP hoặc chặn cả một phân đoạn mạng.

Vẫn còn thấy dial tcp: i/o timeout sau tất cả những bước trên? Hãy đào sâu vào log CNI trên các worker node (Calico và Cilium đều có log riêng từng node). Đôi khi thủ phạm là rule iptables cũ hoặc bảng routing không khớp ở tầng node — hiếm gặp, nhưng vẫn xảy ra trên cluster chạy lâu sau khi nâng cấp một phần.

Related Error Notes