Xem bản video
Cái hộp không tồn tại
Tạo một Service, curl vào tên nó, request tới được pod. Nên ai cũng vẽ trong đầu một cái hộp
đứng giữa nhận request rồi chia cho các pod. Cái hộp đó không tồn tại. Không có tiến trình
nào tên là Service chạy ở bất cứ đâu trong cụm.
Tự kiểm chứng
$ kubectl get svc web
NAME TYPE CLUSTER-IP PORT(S)
web ClusterIP 10.96.0.31 80/TCP
$ ping 10.96.0.31 # không ai trả lời
$ ss -ltn | grep 10.96 # không có gì nghe ở đây
ClusterIP là một địa chỉ ảo. Không ai lắng nghe, không máy nào mang nó. Nó chỉ là cái đích để viết luật.
Ai làm việc thật
kube-proxy chạy trên từng máy. Nó theo dõi API server, thấy Service nào có pod nào đứng
sau, rồi ghi vào bảng lọc gói tin của nhân:
-d 10.96.0.31 --dport 80 -j KUBE-SVC-WEB
KUBE-SVC-WEB --probability 0.33 -j DNAT --to 10.42.1.7:8080
Việc đổi địa chỉ đích xảy ra ngay trên máy nguồn, trước khi gói tin rời máy. Không có chặng trung gian nào. Điều này giải thích luôn ba chuyện: vì sao ping ClusterIP không được, vì sao chẳng thấy log ở đâu, và vì sao gói tin không chậm thêm.
Ai được đứng sau một Service
endpoint controller khớp selector với nhãn pod và ghi kết quả vào EndpointSlice. Chỉ pod
đang Ready mới được ghi vào. Đây mới là chỗ readinessProbe thật sự có tác dụng.
Bốn loại, chồng lên nhau
- ClusterIP — chỉ dùng trong cụm
- NodePort — mở thêm một cổng trên mọi node
- LoadBalancer — nhờ nhà cung cấp dựng bộ cân tải bên ngoài trỏ vào NodePort đó
- ExternalName — không tạo luật nào, chỉ là một bản ghi CNAME
Và clusterIP: None → headless: DNS trả thẳng danh sách IP của pod. Đây là loại StatefulSet
dùng.
Tin cần biết ngay: IPVS đã bị gỡ
Chế độ IPVS của kube-proxy bị đánh dấu lỗi thời ở 1.35 và gỡ hẳn ở 1.36. Một node chạy
kube-proxy 1.36 mà cấu hình vẫn ghi mode: ipvs thì Service không định tuyến được gì.
Không phải chậm — là không chạy.
kubectl -n kube-system get cm kube-proxy -o yaml | grep mode
Đường ra là chế độ nftables: ổn định từ 1.33, nhanh hơn iptables khi cụm lớn. Nhưng nó
không phải mặc định, và không có kế hoạch làm mặc định — mặc định vẫn là iptables. Sửa
ConfigMap của kube-proxy cho mode: nftables rồi khởi động lại lần lượt DaemonSet đó, trước
khi nâng lên 1.36.
Một điều hay bị bỏ qua
Cân tải của kube-proxy là ngẫu nhiên theo kết nối, không phải luân phiên, và nó không biết pod nào đang bận. Với HTTP/1.1 thì tạm ổn. Với kết nối giữ lâu — gRPC, WebSocket, HTTP/2 — thì một kết nối dính một pod mãi mãi và tải lệch hẳn. Đó mới là lý do người ta đưa service mesh hoặc cân tải phía client vào.
Mang gì đi
- Service là luật trên từng máy, không phải một tiến trình
- Chỉ pod sẵn sàng mới có tên trong EndpointSlice
- Còn chạy IPVS thì chuyển sang nftables trước khi nâng lên 1.36
Nguồn: nftables mode cho kube-proxy · Kubernetes v1.36 “Haru”